lib/lita/robot.rb in lita-3.3.1 vs lib/lita/robot.rb in lita-4.0.0.rc1

- old
+ new

@@ -2,14 +2,21 @@ # The main object representing a running instance of Lita. Provides a high # level API for the underlying adapter. Dispatches incoming messages to # registered handlers. Can send outgoing chat messages and set the topic # of chat rooms. class Robot + extend Forwardable + # A +Rack+ application used for the built-in web server. # @return [Rack::Builder] The +Rack+ app. attr_reader :app + # The {Authorization} object for the currently running robot. + # @return [Lita::Authorization] The authorization object. + # @since 4.0.0 + attr_reader :auth + # The name the robot will look for in incoming messages to determine if it's # being addressed. # @return [String] The mention name. attr_accessor :mention_name @@ -20,61 +27,76 @@ # The name of the robot as it will appear in the chat. # @return [String] The robot's name. attr_reader :name - def initialize - @name = Lita.config.robot.name - @mention_name = Lita.config.robot.mention_name || @name - @alias = Lita.config.robot.alias - @app = RackApp.new(self) - load_adapter + # The {Registry} for the currently running robot. + # @return [Lita::Registry] The registry. + # @since 4.0.0 + attr_reader :registry + + def_delegators :registry, :config, :adapters, :handlers, :hooks + + # @param registry [Lita::Registry] The registry for the robot's configuration and plugins. + def initialize(registry = Lita) + @registry = registry + @name = config.robot.name + @mention_name = config.robot.mention_name || @name + @alias = config.robot.alias + @app = RackApp.build(self) + @auth = Authorization.new(config) trigger(:loaded) end # The primary entry point from the adapter for an incoming message. # Dispatches the message to all registered handlers. # @param message [Lita::Message] The incoming message. # @return [void] def receive(message) - Lita.handlers.each { |handler| handler.dispatch(self, message) } + matched = handlers.map do |handler| + next unless handler.respond_to?(:dispatch) + + handler.dispatch(self, message) + end.any? + + trigger(:unhandled_message, message: message) unless matched end # Starts the robot, booting the web server and delegating to the adapter to # connect to the chat service. # @return [void] def run run_app - @adapter.run + adapter.run rescue Interrupt shut_down end # Makes the robot join a room with the specified ID. # @param room_id [String] The ID of the room. # @return [void] # @since 3.0.0 def join(room_id) - @adapter.join(room_id) + adapter.join(room_id) end # Makes the robot part from the room with the specified ID. # @param room_id [String] The ID of the room. # @return [void] # @since 3.0.0 def part(room_id) - @adapter.part(room_id) + adapter.part(room_id) end # Sends one or more messages to a user or room. # @param target [Lita::Source] The user or room to send to. If the Source # has a room, it will choose the room. Otherwise, it will send to the # user. # @param strings [String, Array<String>] One or more strings to send. # @return [void] def send_messages(target, *strings) - @adapter.send_messages(target, strings.flatten) + adapter.send_messages(target, strings.flatten) end alias_method :send_message, :send_messages # Sends one or more messages to a user or room. If sending to a room, # prefixes each message with the user's mention name. @@ -87,11 +109,11 @@ def send_messages_with_mention(target, *strings) return send_messages(target, *strings) if target.private_message? mention_name = target.user.mention_name prefixed_strings = strings.map do |s| - "#{@adapter.mention_format(mention_name).strip} #{s}" + "#{adapter.mention_format(mention_name).strip} #{s}" end send_messages(target, *prefixed_strings) end alias_method :send_message_with_mention, :send_messages_with_mention @@ -99,58 +121,74 @@ # Sets the topic for a chat room. # @param target [Lita::Source] A source object specifying the room. # @param topic [String] The new topic message to set. # @return [void] def set_topic(target, topic) - @adapter.set_topic(target, topic) + adapter.set_topic(target, topic) end # Gracefully shuts the robot down, stopping the web server and delegating # to the adapter to perform any shut down tasks necessary for the chat # service. # @return [void] def shut_down trigger(:shut_down_started) @server.stop(true) if @server @server_thread.join if @server_thread - @adapter.shut_down + adapter.shut_down trigger(:shut_down_complete) end # Triggers an event, instructing all registered handlers to invoke any # methods subscribed to the event, and passing them a payload hash of # arbitrary data. # @param event_name [String, Symbol] The name of the event to trigger. # @param payload [Hash] An optional hash of arbitrary data. # @return [void] def trigger(event_name, payload = {}) - Lita.handlers.each do |handler| + handlers.each do |handler| + next unless handler.respond_to?(:trigger) + handler.trigger(self, event_name, payload) end end private + # Loads and caches the adapter on first access. + def adapter + @adapter ||= load_adapter + end + # Loads the selected adapter. def load_adapter - adapter_name = Lita.config.robot.adapter - adapter_class = Lita.adapters[adapter_name.to_sym] + adapter_name = config.robot.adapter + adapter_class = adapters[adapter_name.to_sym] unless adapter_class Lita.logger.fatal I18n.t("lita.robot.unknown_adapter", adapter: adapter_name) abort end - @adapter = adapter_class.new(self) + adapter_class.new(self) end # Starts the web server. def run_app - http_config = Lita.config.http + http_config = config.http @server_thread = Thread.new do @server = Puma::Server.new(app) - @server.add_tcp_listener(http_config.host, http_config.port.to_i) + begin + @server.add_tcp_listener(http_config.host, http_config.port.to_i) + rescue Errno::EADDRINUSE, Errno::EACCES => e + Lita.logger.fatal I18n.t( + "lita.http.exception", + message: e.message, + backtrace: e.backtrace.join("\n") + ) + abort + end @server.min_threads = http_config.min_threads @server.max_threads = http_config.max_threads @server.run end