rita 0.1.0 → 5.0.0.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +19 -0
- data/.rubocop.yml +51 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/CONTRIBUTING.md +18 -0
- data/Gemfile +5 -0
- data/{LICENSE.txt → LICENSE} +1 -3
- data/README.md +17 -24
- data/Rakefile +5 -5
- data/bin/lita +7 -0
- data/lib/lita/adapter.rb +147 -0
- data/lib/lita/adapters/shell.rb +126 -0
- data/lib/lita/adapters/test.rb +62 -0
- data/lib/lita/authorization.rb +112 -0
- data/lib/lita/callback.rb +39 -0
- data/lib/lita/cli.rb +218 -0
- data/lib/lita/configurable.rb +47 -0
- data/lib/lita/configuration_builder.rb +247 -0
- data/lib/lita/configuration_validator.rb +95 -0
- data/lib/lita/default_configuration.rb +122 -0
- data/lib/lita/errors.rb +25 -0
- data/lib/lita/handler/chat_router.rb +141 -0
- data/lib/lita/handler/common.rb +208 -0
- data/lib/lita/handler/event_router.rb +84 -0
- data/lib/lita/handler/http_router.rb +31 -0
- data/lib/lita/handler.rb +15 -0
- data/lib/lita/handlers/authorization.rb +129 -0
- data/lib/lita/handlers/help.rb +171 -0
- data/lib/lita/handlers/info.rb +66 -0
- data/lib/lita/handlers/room.rb +36 -0
- data/lib/lita/handlers/users.rb +37 -0
- data/lib/lita/http_callback.rb +46 -0
- data/lib/lita/http_route.rb +83 -0
- data/lib/lita/logger.rb +43 -0
- data/lib/lita/message.rb +124 -0
- data/lib/lita/middleware_registry.rb +39 -0
- data/lib/lita/namespace.rb +29 -0
- data/lib/lita/plugin_builder.rb +43 -0
- data/lib/lita/rack_app.rb +100 -0
- data/lib/lita/registry.rb +164 -0
- data/lib/lita/response.rb +65 -0
- data/lib/lita/robot.rb +273 -0
- data/lib/lita/room.rb +119 -0
- data/lib/lita/route_validator.rb +82 -0
- data/lib/lita/rspec/handler.rb +127 -0
- data/lib/lita/rspec/matchers/chat_route_matcher.rb +53 -0
- data/lib/lita/rspec/matchers/event_route_matcher.rb +29 -0
- data/lib/lita/rspec/matchers/http_route_matcher.rb +34 -0
- data/lib/lita/rspec.rb +48 -0
- data/lib/lita/source.rb +81 -0
- data/lib/lita/store.rb +23 -0
- data/lib/lita/target.rb +3 -0
- data/lib/lita/template.rb +71 -0
- data/lib/lita/template_resolver.rb +52 -0
- data/lib/lita/timer.rb +49 -0
- data/lib/lita/user.rb +157 -0
- data/lib/lita/util.rb +31 -0
- data/lib/lita/version.rb +6 -0
- data/lib/lita.rb +166 -0
- data/lib/rita.rb +2 -7
- data/rita.gemspec +50 -0
- data/spec/lita/adapter_spec.rb +54 -0
- data/spec/lita/adapters/shell_spec.rb +99 -0
- data/spec/lita/authorization_spec.rb +122 -0
- data/spec/lita/configuration_builder_spec.rb +247 -0
- data/spec/lita/configuration_validator_spec.rb +114 -0
- data/spec/lita/default_configuration_spec.rb +242 -0
- data/spec/lita/handler/chat_router_spec.rb +236 -0
- data/spec/lita/handler/common_spec.rb +289 -0
- data/spec/lita/handler/event_router_spec.rb +121 -0
- data/spec/lita/handler/http_router_spec.rb +155 -0
- data/spec/lita/handler_spec.rb +62 -0
- data/spec/lita/handlers/authorization_spec.rb +111 -0
- data/spec/lita/handlers/help_spec.rb +124 -0
- data/spec/lita/handlers/info_spec.rb +67 -0
- data/spec/lita/handlers/room_spec.rb +24 -0
- data/spec/lita/handlers/users_spec.rb +35 -0
- data/spec/lita/logger_spec.rb +28 -0
- data/spec/lita/message_spec.rb +178 -0
- data/spec/lita/plugin_builder_spec.rb +41 -0
- data/spec/lita/response_spec.rb +62 -0
- data/spec/lita/robot_spec.rb +285 -0
- data/spec/lita/room_spec.rb +136 -0
- data/spec/lita/rspec/handler_spec.rb +33 -0
- data/spec/lita/rspec_spec.rb +162 -0
- data/spec/lita/source_spec.rb +68 -0
- data/spec/lita/store_spec.rb +23 -0
- data/spec/lita/template_resolver_spec.rb +42 -0
- data/spec/lita/template_spec.rb +52 -0
- data/spec/lita/timer_spec.rb +32 -0
- data/spec/lita/user_spec.rb +167 -0
- data/spec/lita/util_spec.rb +18 -0
- data/spec/lita_spec.rb +227 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/templates/basic.erb +1 -0
- data/spec/templates/basic.irc.erb +1 -0
- data/spec/templates/helpers.erb +1 -0
- data/spec/templates/interpolated.erb +1 -0
- data/templates/locales/en.yml +137 -0
- data/templates/plugin/Gemfile +5 -0
- data/templates/plugin/README.tt +29 -0
- data/templates/plugin/Rakefile +8 -0
- data/templates/plugin/gemspec.tt +27 -0
- data/templates/plugin/gitignore +18 -0
- data/templates/plugin/lib/lita/plugin_type/plugin.tt +19 -0
- data/templates/plugin/lib/plugin.tt +16 -0
- data/templates/plugin/locales/en.yml.tt +4 -0
- data/templates/plugin/spec/lita/plugin_type/plugin_spec.tt +6 -0
- data/templates/plugin/spec/spec_helper.tt +8 -0
- data/templates/plugin/templates/gitkeep +0 -0
- data/templates/robot/Gemfile +5 -0
- data/templates/robot/lita_config.rb +28 -0
- metadata +386 -20
- data/.standard.yml +0 -3
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -132
- data/lib/rita/version.rb +0 -5
- data/sig/rita.rbs +0 -4
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "configuration_builder"
|
4
|
+
require_relative "middleware_registry"
|
5
|
+
|
6
|
+
module Lita
|
7
|
+
# Builds the configuration object that is stored in each {Registry}.
|
8
|
+
# @since 4.0.0
|
9
|
+
# @api private
|
10
|
+
class DefaultConfiguration
|
11
|
+
# Valid levels for Lita's logger.
|
12
|
+
LOG_LEVELS = %w[debug info warn error fatal].freeze
|
13
|
+
|
14
|
+
# The default log formatter.
|
15
|
+
#
|
16
|
+
# This is specified as a constant instead of inline for +config.robot.log_formatter+ so it can
|
17
|
+
# be used by {Lita.logger} for early logging without accessing it via the config attribute and
|
18
|
+
# generating the default config too early, before plugins have been registered.
|
19
|
+
DEFAULT_LOG_FORMATTER = lambda { |severity, datetime, _progname, msg|
|
20
|
+
"[#{datetime.utc}] #{severity}: #{msg}\n"
|
21
|
+
}
|
22
|
+
|
23
|
+
# A {Registry} to extract configuration for plugins from.
|
24
|
+
# @return [Registry] The registry.
|
25
|
+
attr_reader :registry
|
26
|
+
|
27
|
+
# The top-level {ConfigurationBuilder} attribute.
|
28
|
+
# @return [Configuration] The root attribute.
|
29
|
+
attr_reader :root
|
30
|
+
|
31
|
+
# @param registry [Registry] The registry to build a default configuration object from.
|
32
|
+
def initialize(registry)
|
33
|
+
@registry = registry
|
34
|
+
@root = ConfigurationBuilder.new
|
35
|
+
|
36
|
+
adapters_config
|
37
|
+
handlers_config
|
38
|
+
http_config
|
39
|
+
redis_config
|
40
|
+
robot_config
|
41
|
+
end
|
42
|
+
|
43
|
+
# Processes the {ConfigurationBuilder} object to return a {Configuration}.
|
44
|
+
# @return [Configuration] The built configuration object.
|
45
|
+
def build
|
46
|
+
root.build
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Builds config.adapters
|
52
|
+
def adapters_config
|
53
|
+
adapters = registry.adapters
|
54
|
+
|
55
|
+
root.config :adapters do
|
56
|
+
adapters.each do |key, adapter|
|
57
|
+
combine(key, adapter.configuration_builder)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Builds config.handlers
|
63
|
+
def handlers_config
|
64
|
+
handlers = registry.handlers
|
65
|
+
|
66
|
+
root.config :handlers do
|
67
|
+
handlers.each do |handler|
|
68
|
+
if handler.configuration_builder.children?
|
69
|
+
combine(handler.namespace, handler.configuration_builder)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Builds config.http
|
76
|
+
def http_config
|
77
|
+
root.config :http do
|
78
|
+
config :host, type: String, default: "0.0.0.0"
|
79
|
+
config :port, type: [Integer, String], default: 8080
|
80
|
+
config :min_threads, type: [Integer, String], default: 0
|
81
|
+
config :max_threads, type: [Integer, String], default: 16
|
82
|
+
config :middleware, type: MiddlewareRegistry, default: MiddlewareRegistry.new
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Builds config.redis
|
87
|
+
def redis_config
|
88
|
+
root.config :redis, type: Hash, default: {}
|
89
|
+
end
|
90
|
+
|
91
|
+
# Builds config.robot
|
92
|
+
def robot_config
|
93
|
+
root.config :robot do
|
94
|
+
config :name, type: String, default: "Lita"
|
95
|
+
config :mention_name, type: String
|
96
|
+
config :alias, type: String
|
97
|
+
config :adapter, types: [String, Symbol], default: :shell
|
98
|
+
config :locale, types: [String, Symbol], default: nil
|
99
|
+
config :default_locale, types: [String, Symbol], default: nil
|
100
|
+
config :redis_namespace, type: String, default: Lita.test_mode? ? "lita.test" : "lita"
|
101
|
+
config :log_level, types: [String, Symbol], default: :info do
|
102
|
+
validate do |value|
|
103
|
+
unless LOG_LEVELS.include?(value.to_s.downcase.strip)
|
104
|
+
"must be one of: #{LOG_LEVELS.join(", ")}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
config :log_formatter, default: DEFAULT_LOG_FORMATTER do
|
109
|
+
validate do |value|
|
110
|
+
"must respond to #call" unless value.respond_to?(:call)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
config :admins
|
114
|
+
config :error_handler, default: ->(_error, _metadata) {} do
|
115
|
+
validate do |value|
|
116
|
+
"must respond to #call" unless value.respond_to?(:call)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/lita/errors.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lita
|
4
|
+
# The root exception class that all Lita-specific exceptions inherit from.
|
5
|
+
# @since 4.0.0
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
|
+
# An exception raised when a custom validation is set on a configuration attribute that is
|
9
|
+
# violated by the default value of the attribute.
|
10
|
+
# @since 4.0.0
|
11
|
+
class ValidationError < Error; end
|
12
|
+
|
13
|
+
# An exception raised when Lita can't connect to Redis in test mode.
|
14
|
+
# @since 4.0.3
|
15
|
+
class RedisError < Error; end
|
16
|
+
|
17
|
+
# An exception raised when attempting to resolve a template that doesn't exist.
|
18
|
+
# @since 4.2.0
|
19
|
+
class MissingTemplateError < Error; end
|
20
|
+
|
21
|
+
# An exception raised when a handler attempts to render a template without having set its
|
22
|
+
# template root.
|
23
|
+
# @since 4.2.0
|
24
|
+
class MissingTemplateRootError < Error; end
|
25
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "i18n"
|
4
|
+
|
5
|
+
require_relative "../callback"
|
6
|
+
require_relative "../response"
|
7
|
+
require_relative "../route_validator"
|
8
|
+
require_relative "common"
|
9
|
+
|
10
|
+
module Lita
|
11
|
+
class Handler
|
12
|
+
# A handler mixin that provides the methods necessary for responding to chat messages.
|
13
|
+
# @since 4.0.0
|
14
|
+
module ChatRouter
|
15
|
+
# Includes common handler methods in any class that includes {ChatRouter}.
|
16
|
+
def self.extended(klass)
|
17
|
+
klass.include(Common)
|
18
|
+
end
|
19
|
+
|
20
|
+
# A Struct representing a chat route defined by a handler.
|
21
|
+
Route = Struct.new(
|
22
|
+
:pattern,
|
23
|
+
:callback,
|
24
|
+
:command,
|
25
|
+
:required_groups,
|
26
|
+
:help,
|
27
|
+
:extensions
|
28
|
+
)
|
29
|
+
|
30
|
+
class Route
|
31
|
+
alias command? command
|
32
|
+
end
|
33
|
+
|
34
|
+
# @overload route(pattern, method_name, **options)
|
35
|
+
# Creates a chat route.
|
36
|
+
# @param pattern [Regexp] A regular expression to match incoming messages against.
|
37
|
+
# @param method_name [Symbol, String] The name of the instance method to trigger.
|
38
|
+
# @param command [Boolean] Whether or not the message must be directed at the robot.
|
39
|
+
# @param restrict_to [Array<Symbol, String>, nil] An optional list of authorization
|
40
|
+
# groups the user must be in to trigger the route.
|
41
|
+
# @param help [Hash] An optional map of example invocations to descriptions.
|
42
|
+
# @param options [Hash] Aribtrary additional data that can be used by Lita extensions.
|
43
|
+
# @return [void]
|
44
|
+
# @overload route(pattern, **options)
|
45
|
+
# Creates a chat route.
|
46
|
+
# @param pattern [Regexp] A regular expression to match incoming messages against.
|
47
|
+
# @param command [Boolean] Whether or not the message must be directed at the robot.
|
48
|
+
# @param restrict_to [Array<Symbol, String>, nil] An optional list of authorization
|
49
|
+
# groups the user must be in to trigger the route.
|
50
|
+
# @param help [Hash] An optional map of example invocations to descriptions.
|
51
|
+
# @param options [Hash] Aribtrary additional data that can be used by Lita extensions.
|
52
|
+
# @yield The body of the route's callback.
|
53
|
+
# @return [void]
|
54
|
+
# @since 4.0.0
|
55
|
+
def route(pattern, method_name = nil, **options, &block)
|
56
|
+
options = default_route_options.merge(options)
|
57
|
+
options[:restrict_to] = options[:restrict_to].nil? ? nil : Array(options[:restrict_to])
|
58
|
+
routes << Route.new(
|
59
|
+
pattern,
|
60
|
+
Callback.new(method_name || block),
|
61
|
+
options.delete(:command),
|
62
|
+
options.delete(:restrict_to),
|
63
|
+
options.delete(:help),
|
64
|
+
options
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# A list of chat routes defined by the handler.
|
69
|
+
# @return [Array<Handler::Route>]
|
70
|
+
def routes
|
71
|
+
@routes ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
# The main entry point for the handler at runtime. Checks if the message
|
75
|
+
# matches any of the routes and invokes the route's method if it does.
|
76
|
+
# Called by {Robot#receive}.
|
77
|
+
# @param robot [Robot] The currently running robot.
|
78
|
+
# @param message [Message] The incoming message.
|
79
|
+
# @return [Boolean] Whether or not the message matched any routes.
|
80
|
+
def dispatch(robot, message)
|
81
|
+
routes.map do |route|
|
82
|
+
next unless route_applies?(route, message, robot)
|
83
|
+
|
84
|
+
log_dispatch(robot, route)
|
85
|
+
|
86
|
+
robot.run_concurrently { dispatch_to_route(route, robot, message) }
|
87
|
+
|
88
|
+
true
|
89
|
+
end.any?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Dispatch directly to a {Route}, ignoring route conditions.
|
93
|
+
# @param route [Route] The route to invoke.
|
94
|
+
# @param robot [Robot] The currently running robot.
|
95
|
+
# @param message [Message] The incoming message.
|
96
|
+
# @return [void]
|
97
|
+
# @since 3.3.0
|
98
|
+
def dispatch_to_route(route, robot, message)
|
99
|
+
response = Response.new(message, route.pattern)
|
100
|
+
robot.hooks[:trigger_route].each { |hook| hook.call(response: response, route: route) }
|
101
|
+
handler = new(robot)
|
102
|
+
route.callback.call(handler, response)
|
103
|
+
robot.hooks[:post_route].each { |hook| hook.call(response: response, route: route) }
|
104
|
+
robot.trigger(
|
105
|
+
:message_dispatched,
|
106
|
+
handler: self,
|
107
|
+
route: route,
|
108
|
+
message: message,
|
109
|
+
robot: robot
|
110
|
+
)
|
111
|
+
rescue StandardError => e
|
112
|
+
log_error(robot, e, message: message)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
# The default options for every chat route.
|
118
|
+
def default_route_options
|
119
|
+
{
|
120
|
+
command: false,
|
121
|
+
restrict_to: nil,
|
122
|
+
help: {}
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
# Determines whether or not an incoming messages should trigger a route.
|
127
|
+
def route_applies?(route, message, robot)
|
128
|
+
RouteValidator.new(self, route, message, robot).call
|
129
|
+
end
|
130
|
+
|
131
|
+
# Logs the dispatch of message.
|
132
|
+
def log_dispatch(robot, route)
|
133
|
+
robot.logger.debug I18n.t(
|
134
|
+
"lita.handler.dispatch",
|
135
|
+
handler: name,
|
136
|
+
method: route.callback.method_name || "(block)"
|
137
|
+
)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "i18n"
|
5
|
+
require "redis-namespace"
|
6
|
+
|
7
|
+
require_relative "../configurable"
|
8
|
+
require_relative "../configuration_builder"
|
9
|
+
require_relative "../errors"
|
10
|
+
require_relative "../namespace"
|
11
|
+
require_relative "../template"
|
12
|
+
require_relative "../template_resolver"
|
13
|
+
require_relative "../timer"
|
14
|
+
require_relative "../version"
|
15
|
+
|
16
|
+
module Lita
|
17
|
+
class Handler
|
18
|
+
# Methods included in any class that includes at least one type of router.
|
19
|
+
# @since 4.0.0
|
20
|
+
module Common
|
21
|
+
# Adds common functionality to the class and initializes the handler's configuration builder.
|
22
|
+
def self.included(klass)
|
23
|
+
klass.extend(ClassMethods)
|
24
|
+
klass.extend(Configurable)
|
25
|
+
klass.extend(Namespace)
|
26
|
+
klass.configuration_builder = ConfigurationBuilder.new
|
27
|
+
end
|
28
|
+
|
29
|
+
# Common class-level methods for all handlers.
|
30
|
+
module ClassMethods
|
31
|
+
# Gets (and optionally sets) the directory where the handler's templates are stored.
|
32
|
+
# @param path [String] If provided, sets the template root to this value.
|
33
|
+
# @return [String] The template root path.
|
34
|
+
# @raise [MissingTemplateRootError] If accessed without setting a value first.
|
35
|
+
def template_root(path = nil)
|
36
|
+
@template_root = path if path
|
37
|
+
|
38
|
+
if defined?(@template_root)
|
39
|
+
@template_root
|
40
|
+
else
|
41
|
+
raise MissingTemplateRootError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the translation for a key, automatically namespaced to the handler.
|
46
|
+
# @param key [String] The key of the translation.
|
47
|
+
# @param hash [Hash] An optional hash of values to be interpolated in the string.
|
48
|
+
# @return [String] The translated string.
|
49
|
+
# @since 3.0.0
|
50
|
+
def translate(key, hash = {})
|
51
|
+
I18n.translate("lita.handlers.#{namespace}.#{key}", **hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
alias t translate
|
55
|
+
|
56
|
+
# Logs an error raised by a plugin.
|
57
|
+
def log_error(robot, error, metadata = {})
|
58
|
+
error_handler = robot.config.robot.error_handler
|
59
|
+
|
60
|
+
if error_handler.arity == 2
|
61
|
+
error_handler.call(error, metadata.merge(robot: robot))
|
62
|
+
else
|
63
|
+
error_handler.call(error)
|
64
|
+
end
|
65
|
+
|
66
|
+
robot.logger.error I18n.t(
|
67
|
+
"lita.handler.exception",
|
68
|
+
handler: name,
|
69
|
+
message: error.message,
|
70
|
+
backtrace: error.backtrace.join("\n")
|
71
|
+
)
|
72
|
+
|
73
|
+
raise error if Lita.test_mode?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# A Redis::Namespace scoped to the handler.
|
78
|
+
# @return [Redis::Namespace]
|
79
|
+
attr_reader :redis
|
80
|
+
|
81
|
+
# The running {Robot} instance.
|
82
|
+
# @return [Robot]
|
83
|
+
attr_reader :robot
|
84
|
+
|
85
|
+
# @param robot [Robot] The currently running robot.
|
86
|
+
def initialize(robot)
|
87
|
+
@robot = robot
|
88
|
+
@redis = Redis::Namespace.new(redis_namespace, redis: robot.redis)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Invokes the given block after the given number of seconds.
|
92
|
+
# @param interval [Integer] The number of seconds to wait before invoking the block.
|
93
|
+
# @yieldparam timer [Timer] The current {Timer} instance.
|
94
|
+
# @return [void]
|
95
|
+
# @since 3.0.0
|
96
|
+
def after(interval, &block)
|
97
|
+
robot.run_concurrently { Timer.new(interval: interval, &block).start }
|
98
|
+
end
|
99
|
+
|
100
|
+
# The handler's configuration object.
|
101
|
+
# @return [Configuration, Config] The handler's configuration object.
|
102
|
+
# @since 3.2.0
|
103
|
+
def config
|
104
|
+
if robot.config.handlers.respond_to?(self.class.namespace)
|
105
|
+
robot.config.handlers.public_send(self.class.namespace)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Invokes the given block repeatedly, waiting the given number of seconds between each
|
110
|
+
# invocation.
|
111
|
+
# @param interval [Integer] The number of seconds to wait before each invocation of the block.
|
112
|
+
# @yieldparam timer [Timer] The current {Timer} instance.
|
113
|
+
# @return [void]
|
114
|
+
# @note The block should call {Timer#stop} at a terminating condition to avoid infinite
|
115
|
+
# recursion.
|
116
|
+
# @since 3.0.0
|
117
|
+
def every(interval, &block)
|
118
|
+
robot.run_concurrently { Timer.new(interval: interval, recurring: true, &block).start }
|
119
|
+
end
|
120
|
+
|
121
|
+
# Creates a new +Faraday::Connection+ for making HTTP requests.
|
122
|
+
# @param options [Hash] A set of options passed on to Faraday.
|
123
|
+
# @yield [builder] A Faraday builder object for adding middleware.
|
124
|
+
# @return [Faraday::Connection] The new connection object.
|
125
|
+
def http(options = {}, &block)
|
126
|
+
options = default_faraday_options.merge(options)
|
127
|
+
|
128
|
+
if block
|
129
|
+
Faraday::Connection.new(nil, options, &block)
|
130
|
+
else
|
131
|
+
Faraday::Connection.new(nil, options)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# The +Logger+ for the currently running robot.
|
136
|
+
# @return [Logger] The Lita logger.
|
137
|
+
# @since 3.2.0
|
138
|
+
def log
|
139
|
+
robot.logger
|
140
|
+
end
|
141
|
+
|
142
|
+
# Render an ERB template to a string, with any provided variables made available to the
|
143
|
+
# template.
|
144
|
+
# @param template_name [String] The name of the ERB template file.
|
145
|
+
# @param variables [Hash] An optional hash of variables to make available within the
|
146
|
+
# template. Hash keys become instance variable names with the hash values as the instance
|
147
|
+
# variables' values.
|
148
|
+
# @return [String] The rendered template.
|
149
|
+
# @since 4.2.0
|
150
|
+
def render_template(template_name, variables = {})
|
151
|
+
Template.from_file(file_for_template(template_name)).render(variables)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Renders an ERB template to a string, like {#render_template}, but also takes an array of
|
155
|
+
# modules with helper methods to be made available to the template.
|
156
|
+
# @param template_name [String] The name of the ERB template file.
|
157
|
+
# @param helpers [Array<Module>] An array of modules whose methods should be added to the
|
158
|
+
# template evaluation context.
|
159
|
+
# @param variables [Hash] An optional hash of variables to make available within the
|
160
|
+
# template. Hash keys become instance variable names with the hash values as the instance
|
161
|
+
# variables' values.
|
162
|
+
# @return [String] The rendered template.
|
163
|
+
# @since 4.5.0
|
164
|
+
def render_template_with_helpers(template_name, helpers, variables = {})
|
165
|
+
template = Template.from_file(file_for_template(template_name))
|
166
|
+
helpers.each { |helper| template.add_helper(helper) }
|
167
|
+
template.render(variables)
|
168
|
+
end
|
169
|
+
|
170
|
+
# A hash-like object that can be used to persist data between instances of the current
|
171
|
+
# handler. Useful for storing objects that are expensive to create and that can be safely
|
172
|
+
# reused between instances.
|
173
|
+
# @return [Store] The handler's shared store.
|
174
|
+
# @since 5.0.0
|
175
|
+
def store
|
176
|
+
robot.store[self.class.namespace]
|
177
|
+
end
|
178
|
+
|
179
|
+
# @see .translate
|
180
|
+
def translate(*args)
|
181
|
+
self.class.translate(*args)
|
182
|
+
end
|
183
|
+
|
184
|
+
alias t translate
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
# Default options for new Faraday connections. Sets the user agent to the
|
189
|
+
# current version of Lita.
|
190
|
+
def default_faraday_options
|
191
|
+
{ headers: { "User-Agent" => "Lita v#{VERSION}" } }
|
192
|
+
end
|
193
|
+
|
194
|
+
def file_for_template(template_name)
|
195
|
+
TemplateResolver.new(
|
196
|
+
self.class.template_root,
|
197
|
+
template_name,
|
198
|
+
robot.config.robot.adapter
|
199
|
+
).resolve
|
200
|
+
end
|
201
|
+
|
202
|
+
# The handler's namespace for Redis.
|
203
|
+
def redis_namespace
|
204
|
+
"handlers:#{self.class.namespace}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../callback"
|
4
|
+
require_relative "common"
|
5
|
+
|
6
|
+
module Lita
|
7
|
+
class Handler
|
8
|
+
# A handler mixin that provides the methods necessary for handling events.
|
9
|
+
# @since 4.0.0
|
10
|
+
module EventRouter
|
11
|
+
# Includes common handler methods in any class that includes {EventRouter}.
|
12
|
+
def self.extended(klass)
|
13
|
+
klass.include(Common)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @overload on(event_name, method_name)
|
17
|
+
# Registers an event subscription. When an event is triggered with
|
18
|
+
# {#trigger}, a new instance of the handler will be created and the
|
19
|
+
# instance method name supplied to {#on} will be invoked with a payload
|
20
|
+
# (a hash of arbitrary keys and values).
|
21
|
+
# @param event_name [String, Symbol] The name of the event to subscribe to.
|
22
|
+
# @param method_name [String, Symbol] The name of the instance method on
|
23
|
+
# the handler that should be invoked when the event is triggered.
|
24
|
+
# @return [void]
|
25
|
+
# @overload on(event_name, callable)
|
26
|
+
# Registers an event subscription. When an event is triggered with
|
27
|
+
# {#trigger}, a new instance of the handler will be created and the
|
28
|
+
# callable object supplied to {#on} will be evaluated within the context of the new
|
29
|
+
# handler instance, and passed a payload (a hash of arbitrary keys and values).
|
30
|
+
# @param event_name [String, Symbol] The name of the event to subscribe to.
|
31
|
+
# @param callable [#call] A callable object to serve as the event callback.
|
32
|
+
# @return [void]
|
33
|
+
# @since 4.0.0
|
34
|
+
# @overload on(event_name)
|
35
|
+
# Registers an event subscription. When an event is triggered with
|
36
|
+
# {#trigger}, a new instance of the handler will be created and the
|
37
|
+
# block supplied to {#on} will be evaluated within the context of the new
|
38
|
+
# handler instance, and passed a payload (a hash of arbitrary keys and values).
|
39
|
+
# @param event_name [String, Symbol] The name of the event to subscribe to.
|
40
|
+
# @yield The body of the event callback.
|
41
|
+
# @return [void]
|
42
|
+
# @since 4.0.0
|
43
|
+
def on(event_name, method_name_or_callable = nil, &block)
|
44
|
+
event_subscriptions[normalize_event(event_name)] << Callback.new(
|
45
|
+
method_name_or_callable || block
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns an array of all callbacks registered for the named event.
|
50
|
+
# @param event_name [String, Symbol] The name of the event to return callbacks for.
|
51
|
+
# @return [Array] The array of callbacks.
|
52
|
+
# @since 4.0.0
|
53
|
+
def event_subscriptions_for(event_name)
|
54
|
+
event_subscriptions[normalize_event(event_name)]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Triggers an event, invoking methods previously registered with {#on} and
|
58
|
+
# passing them a payload hash with any arbitrary data.
|
59
|
+
# @param robot [Robot] The currently running robot instance.
|
60
|
+
# @param event_name [String, Symbol], The name of the event to trigger.
|
61
|
+
# @param payload [Hash] An optional hash of arbitrary data.
|
62
|
+
# @return [Boolean] Whether or not the event triggered any callbacks.
|
63
|
+
def trigger(robot, event_name, payload = {})
|
64
|
+
event_subscriptions_for(event_name).map do |callback|
|
65
|
+
callback.call(new(robot), payload)
|
66
|
+
rescue StandardError => e
|
67
|
+
log_error(robot, e, payload: payload)
|
68
|
+
end.any?
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# A hash of arrays used to store event subscriptions registered with {#on}.
|
74
|
+
def event_subscriptions
|
75
|
+
@event_subscriptions ||= Hash.new { |h, k| h[k] = [] }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Normalize the event name, ignoring casing and spaces.
|
79
|
+
def normalize_event(event_name)
|
80
|
+
event_name.to_s.downcase.strip.to_sym
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../http_route"
|
4
|
+
require_relative "common"
|
5
|
+
|
6
|
+
module Lita
|
7
|
+
class Handler
|
8
|
+
# A handler mixin that provides the methods necessary for handling incoming HTTP requests.
|
9
|
+
# @since 4.0.0
|
10
|
+
module HTTPRouter
|
11
|
+
# Includes common handler methods in any class that includes {HTTPRouter}.
|
12
|
+
def self.extended(klass)
|
13
|
+
klass.include(Common)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Creates a new {HTTPRoute} which is used to define an HTTP route
|
17
|
+
# for the built-in web server.
|
18
|
+
# @see HTTPRoute
|
19
|
+
# @return [HTTPRoute] The new {HTTPRoute}.
|
20
|
+
def http
|
21
|
+
HTTPRoute.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
# An array of all HTTP routes defined for the handler.
|
25
|
+
# @return [Array<HTTPRoute>] The array of routes.
|
26
|
+
def http_routes
|
27
|
+
@http_routes ||= []
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/lita/handler.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "handler/chat_router"
|
4
|
+
require_relative "handler/http_router"
|
5
|
+
require_relative "handler/event_router"
|
6
|
+
|
7
|
+
module Lita
|
8
|
+
# Base class for objects that add new behavior to Lita. {Handler} is simply a class with all
|
9
|
+
# types of routers mixed in.
|
10
|
+
class Handler
|
11
|
+
extend ChatRouter
|
12
|
+
extend HTTPRouter
|
13
|
+
extend EventRouter
|
14
|
+
end
|
15
|
+
end
|