module Lita class Handler # A handler mixin that provides the methods necessary for responding to chat messages. # @since 4.0.0 module ChatRouter # Includes common handler methods in any class that includes {ChatRouter}. def self.extended(klass) klass.send(:include, Common) end # A Struct representing a chat route defined by a handler. Route = Struct.new( :pattern, :callback, :command, :required_groups, :help, :extensions ) class Route alias_method :command?, :command end # @overload route(pattern, method_name, **options) # Creates a chat route. # @param pattern [Regexp] A regular expression to match incoming messages against. # @param method_name [Symbol, String] The name of the instance method to trigger. # @param command [Boolean] Whether or not the message must be directed at the robot. # @param restrict_to [Array, nil] An optional list of authorization # groups the user must be in to trigger the route. # @param help [Hash] An optional map of example invocations to descriptions. # @param options [Hash] Aribtrary additional data that can be used by Lita extensions. # @return [void] # @overload route(pattern, **options) # Creates a chat route. # @param pattern [Regexp] A regular expression to match incoming messages against. # @param command [Boolean] Whether or not the message must be directed at the robot. # @param restrict_to [Array, nil] An optional list of authorization # groups the user must be in to trigger the route. # @param help [Hash] An optional map of example invocations to descriptions. # @param options [Hash] Aribtrary additional data that can be used by Lita extensions. # @yield The body of the route's callback. # @return [void] # @since 4.0.0 def route(pattern, method_name = nil, **options, &block) options = default_route_options.merge(options) options[:restrict_to] = options[:restrict_to].nil? ? nil : Array(options[:restrict_to]) routes << Route.new( pattern, Callback.new(method_name || block), options.delete(:command), options.delete(:restrict_to), options.delete(:help), options ) end # A list of chat routes defined by the handler. # @return [Array] def routes @routes ||= [] end # The main entry point for the handler at runtime. Checks if the message # matches any of the routes and invokes the route's method if it does. # Called by {Lita::Robot#receive}. # @param robot [Lita::Robot] The currently running robot. # @param message [Lita::Message] The incoming message. # @return [Boolean] Whether or not the message matched any routes. def dispatch(robot, message) routes.map do |route| next unless route_applies?(route, message, robot) log_dispatch(route) robot.trigger( :message_dispatched, handler: self, route: route, message: message, robot: robot ) dispatch_to_route(route, robot, message) true end.any? end # Dispatch directly to a {Route}, ignoring route conditions. # @param route [Route] The route to invoke. # @param robot [Lita::Robot] The currently running robot. # @param message [Lita::Message] The incoming message. # @return [void] # @since 3.3.0 def dispatch_to_route(route, robot, message) response = Response.new(message, route.pattern) robot.hooks[:trigger_route].each { |hook| hook.call(response: response, route: route) } handler = new(robot) route.callback.call(handler, response) rescue => error log_error(robot, error) end private # The default options for every chat route. def default_route_options { command: false, restrict_to: nil, help: {} } end # Determines whether or not an incoming messages should trigger a route. def route_applies?(route, message, robot) RouteValidator.new(self, route, message, robot).call end # Logs the dispatch of message. def log_dispatch(route) Lita.logger.debug I18n.t( "lita.handler.dispatch", handler: name, method: route.callback.method_name || "(block)" ) end end end end