lita 2.7.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +26 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +6 -470
- data/Rakefile +3 -3
- data/lib/lita.rb +27 -19
- data/lib/lita/adapter.rb +46 -5
- data/lib/lita/adapters/shell.rb +18 -13
- data/lib/lita/authorization.rb +1 -1
- data/lib/lita/cli.rb +37 -23
- data/lib/lita/common.rb +35 -0
- data/lib/lita/config.rb +33 -13
- data/lib/lita/daemon.rb +15 -12
- data/lib/lita/handler.rb +49 -9
- data/lib/lita/handlers/authorization.rb +47 -47
- data/lib/lita/handlers/help.rb +16 -17
- data/lib/lita/handlers/info.rb +38 -0
- data/lib/lita/handlers/room.rb +32 -0
- data/lib/lita/http_route.rb +30 -19
- data/lib/lita/message.rb +3 -6
- data/lib/lita/rack_app.rb +11 -89
- data/lib/lita/response.rb +5 -15
- data/lib/lita/robot.rb +26 -10
- data/lib/lita/rspec.rb +6 -8
- data/lib/lita/rspec/handler.rb +49 -121
- data/lib/lita/rspec/matchers/event_subscription_matcher.rb +67 -0
- data/lib/lita/rspec/matchers/http_route_matcher.rb +72 -0
- data/lib/lita/rspec/matchers/route_matcher.rb +69 -0
- data/lib/lita/source.rb +5 -18
- data/lib/lita/timer.rb +45 -0
- data/lib/lita/user.rb +51 -4
- data/lib/lita/util.rb +5 -5
- data/lib/lita/version.rb +1 -1
- data/lita.gemspec +6 -3
- data/spec/lita/adapter_spec.rb +10 -2
- data/spec/lita/adapters/shell_spec.rb +3 -3
- data/spec/lita/authorization_spec.rb +11 -11
- data/spec/lita/config_spec.rb +8 -0
- data/spec/lita/daemon_spec.rb +65 -0
- data/spec/lita/handler_spec.rb +50 -11
- data/spec/lita/handlers/authorization_spec.rb +1 -1
- data/spec/lita/handlers/info_spec.rb +31 -0
- data/spec/lita/handlers/room_spec.rb +20 -0
- data/spec/lita/logger_spec.rb +1 -1
- data/spec/lita/message_spec.rb +4 -4
- data/spec/lita/rack_app_spec.rb +92 -0
- data/spec/lita/response_spec.rb +17 -8
- data/spec/lita/robot_spec.rb +23 -14
- data/spec/lita/rspec_spec.rb +1 -1
- data/spec/lita/source_spec.rb +0 -16
- data/spec/lita/timer_spec.rb +30 -0
- data/spec/lita/user_spec.rb +66 -6
- data/spec/lita_spec.rb +37 -0
- data/spec/spec_helper.rb +11 -0
- data/templates/locales/en.yml +90 -0
- data/templates/plugin/Rakefile +1 -1
- data/templates/plugin/lib/lita/plugin_type/plugin.tt +4 -0
- data/templates/plugin/locales/en.yml.tt +4 -0
- metadata +77 -18
- data/lib/lita/handlers/web.rb +0 -25
- data/spec/lita/handlers/web_spec.rb +0 -19
data/Rakefile
CHANGED
data/lib/lita.rb
CHANGED
@@ -3,12 +3,15 @@ require "logger"
|
|
3
3
|
require "rbconfig"
|
4
4
|
require "set"
|
5
5
|
require "shellwords"
|
6
|
+
require "thread"
|
6
7
|
|
8
|
+
require "http_router"
|
9
|
+
require "ice_nine"
|
7
10
|
require "faraday"
|
8
11
|
require "multi_json"
|
12
|
+
require "puma"
|
9
13
|
require "rack"
|
10
14
|
require "redis-namespace"
|
11
|
-
require "thin"
|
12
15
|
|
13
16
|
# The main namespace for Lita. Provides a global registry of adapters and
|
14
17
|
# handlers, as well as global configuration, logger, and Redis store.
|
@@ -85,26 +88,31 @@ module Lita
|
|
85
88
|
# @return [void]
|
86
89
|
def run(config_path = nil)
|
87
90
|
Config.load_user_config(config_path)
|
91
|
+
Lita.config.finalize
|
92
|
+
self.locale = Lita.config.robot.locale
|
88
93
|
Robot.new.run
|
89
94
|
end
|
90
95
|
end
|
91
96
|
end
|
92
97
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
98
|
+
require_relative "lita/version"
|
99
|
+
require_relative "lita/common"
|
100
|
+
require_relative "lita/config"
|
101
|
+
require_relative "lita/util"
|
102
|
+
require_relative "lita/logger"
|
103
|
+
require_relative "lita/user"
|
104
|
+
require_relative "lita/source"
|
105
|
+
require_relative "lita/authorization"
|
106
|
+
require_relative "lita/message"
|
107
|
+
require_relative "lita/response"
|
108
|
+
require_relative "lita/http_route"
|
109
|
+
require_relative "lita/rack_app"
|
110
|
+
require_relative "lita/timer"
|
111
|
+
require_relative "lita/robot"
|
112
|
+
require_relative "lita/adapter"
|
113
|
+
require_relative "lita/adapters/shell"
|
114
|
+
require_relative "lita/handler"
|
115
|
+
require_relative "lita/handlers/authorization"
|
116
|
+
require_relative "lita/handlers/help"
|
117
|
+
require_relative "lita/handlers/info"
|
118
|
+
require_relative "lita/handlers/room"
|
data/lib/lita/adapter.rb
CHANGED
@@ -10,6 +10,18 @@ module Lita
|
|
10
10
|
# @return [Array]
|
11
11
|
attr_reader :required_configs
|
12
12
|
|
13
|
+
# The namespace for the adapter, used for registry and for I18n. If the handler is an
|
14
|
+
# anonymous class, it must explicitly define +self.name+.
|
15
|
+
# @return [String] The adapter's namespace.
|
16
|
+
# @raise [RuntimeError] If +self.name+ is not defined.
|
17
|
+
def namespace
|
18
|
+
if name
|
19
|
+
Util.underscore(name.split("::").last)
|
20
|
+
else
|
21
|
+
raise I18n.t("lita.adapter.name_required")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
13
25
|
# Defines configuration keys that are requried for the adapter to boot.
|
14
26
|
# @param keys [String, Symbol] The required keys.
|
15
27
|
# @return [void]
|
@@ -19,6 +31,16 @@ module Lita
|
|
19
31
|
end
|
20
32
|
|
21
33
|
alias_method :require_configs, :require_config
|
34
|
+
|
35
|
+
# Returns the translation for a key, automatically namespaced to the adapter.
|
36
|
+
# @param key [String] The key of the translation.
|
37
|
+
# @param hash [Hash] An optional hash of values to be interpolated in the string.
|
38
|
+
# @return [String] The translated string.
|
39
|
+
def translate(key, hash = {})
|
40
|
+
I18n.translate("lita.adapters.#{namespace}.#{key}", hash)
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :t, :translate
|
22
44
|
end
|
23
45
|
|
24
46
|
# @param robot [Lita::Robot] The currently running robot.
|
@@ -27,6 +49,20 @@ module Lita
|
|
27
49
|
ensure_required_configs
|
28
50
|
end
|
29
51
|
|
52
|
+
# @!method join
|
53
|
+
# Joins the room with the specified ID.
|
54
|
+
# @param room_id [String] The ID of the room.
|
55
|
+
# @return [void]
|
56
|
+
# @abstract This should be implemented by the adapter.
|
57
|
+
# @since 3.0.0
|
58
|
+
|
59
|
+
# @!method part
|
60
|
+
# Parts from the room with the specified ID.
|
61
|
+
# @param room_id [String] The ID of the room.
|
62
|
+
# @return [void]
|
63
|
+
# @abstract This should be implemented by the adapter.
|
64
|
+
# @since 3.0.0
|
65
|
+
|
30
66
|
# @!method run
|
31
67
|
# The main loop. Should connect to the chat service, listen for incoming
|
32
68
|
# messages, create {Lita::Message} objects from them, and dispatch them to
|
@@ -52,12 +88,19 @@ module Lita
|
|
52
88
|
# Performs any clean up necessary when disconnecting from the chat service.
|
53
89
|
# @return [void]
|
54
90
|
# @abstract This should be implemented by the adapter.
|
55
|
-
[:run, :send_messages, :set_topic, :shut_down].each do |method|
|
91
|
+
[:join, :part, :run, :send_messages, :set_topic, :shut_down].each do |method|
|
56
92
|
define_method(method) do |*args|
|
57
|
-
Lita.logger.warn("
|
93
|
+
Lita.logger.warn(I18n.t("lita.adapter.method_not_implemented", method: method))
|
58
94
|
end
|
59
95
|
end
|
60
96
|
|
97
|
+
# @see .translate
|
98
|
+
def translate(*args)
|
99
|
+
self.class.translate(*args)
|
100
|
+
end
|
101
|
+
|
102
|
+
alias_method :t, :translate
|
103
|
+
|
61
104
|
private
|
62
105
|
|
63
106
|
# Logs a fatal message and aborts if a required config key is not set.
|
@@ -72,9 +115,7 @@ module Lita
|
|
72
115
|
end
|
73
116
|
|
74
117
|
unless missing_keys.empty?
|
75
|
-
Lita.logger.fatal(
|
76
|
-
"The following keys are required on config.adapter: #{missing_keys.join(", ")}"
|
77
|
-
)
|
118
|
+
Lita.logger.fatal(I18n.t("lita.adapter.missing_configs", configs: missing_keys.join(", ")))
|
78
119
|
abort
|
79
120
|
end
|
80
121
|
end
|
data/lib/lita/adapters/shell.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Lita
|
2
|
+
# A namespace to hold all subclasses of {Adapter}.
|
2
3
|
module Adapters
|
3
4
|
# An adapter that runs Lita in a UNIX shell.
|
4
5
|
class Shell < Adapter
|
@@ -7,21 +8,11 @@ module Lita
|
|
7
8
|
# @return [void]
|
8
9
|
def run
|
9
10
|
user = User.create(1, name: "Shell User")
|
10
|
-
source = Source.new(user: user)
|
11
|
-
puts
|
11
|
+
@source = Source.new(user: user)
|
12
|
+
puts t("startup_message")
|
12
13
|
robot.trigger(:connected)
|
13
14
|
|
14
|
-
|
15
|
-
print "#{robot.name} > "
|
16
|
-
input = $stdin.gets
|
17
|
-
if input.nil?
|
18
|
-
puts
|
19
|
-
break
|
20
|
-
end
|
21
|
-
input = input.chomp.strip
|
22
|
-
break if input == "exit" || input == "quit"
|
23
|
-
robot.receive(build_message(input, source))
|
24
|
-
end
|
15
|
+
run_loop
|
25
16
|
end
|
26
17
|
|
27
18
|
# Outputs outgoing messages to the shell.
|
@@ -51,6 +42,20 @@ module Lita
|
|
51
42
|
message.command! if Lita.config.adapter.private_chat
|
52
43
|
message
|
53
44
|
end
|
45
|
+
|
46
|
+
def run_loop
|
47
|
+
loop do
|
48
|
+
print "#{robot.name} > "
|
49
|
+
input = $stdin.gets
|
50
|
+
if input.nil?
|
51
|
+
puts
|
52
|
+
break
|
53
|
+
end
|
54
|
+
input = input.chomp.strip
|
55
|
+
break if input == "exit" || input == "quit"
|
56
|
+
robot.receive(build_message(input, @source))
|
57
|
+
end
|
58
|
+
end
|
54
59
|
end
|
55
60
|
|
56
61
|
Lita.register_adapter(:shell, Shell)
|
data/lib/lita/authorization.rb
CHANGED
@@ -52,7 +52,7 @@ module Lita
|
|
52
52
|
# Returns a hash of authorization group names and the users in them.
|
53
53
|
# @return [Hash] A map of +Symbol+ group names to +Lita::User+ objects.
|
54
54
|
def groups_with_users
|
55
|
-
groups.
|
55
|
+
groups.reduce({}) do |list, group|
|
56
56
|
list[group] = redis.smembers(group).map do |user_id|
|
57
57
|
User.find_by_id(user_id)
|
58
58
|
end
|
data/lib/lita/cli.rb
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
require "thor"
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative "common"
|
4
|
+
require_relative "daemon"
|
5
|
+
require_relative "version"
|
5
6
|
|
6
7
|
module Lita
|
7
8
|
# The command line interface for Lita.
|
8
9
|
class CLI < Thor
|
9
10
|
include Thor::Actions
|
10
11
|
|
12
|
+
# The root file path for the templates directory.
|
13
|
+
# @note This is a magic method required by Thor for file operations.
|
14
|
+
# @return [String] The path.
|
11
15
|
def self.source_root
|
12
|
-
|
16
|
+
Lita.template_root
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the full destination file path for the given file, using the supplied +default_path+
|
20
|
+
# as the base if run as root, otherwise falling back to the user's home directory.
|
21
|
+
# @param file_name [String] The name of the file.
|
22
|
+
# @param default_path [String] The base of the file path to use when run as root.
|
23
|
+
# @return [String] The full file path.
|
24
|
+
def self.file_path_for(file_name, default_path)
|
25
|
+
base_path = Process.euid == 0 ? default_path : ENV["HOME"]
|
26
|
+
File.join(base_path, file_name)
|
13
27
|
end
|
14
28
|
|
15
29
|
default_task :start
|
@@ -28,29 +42,25 @@ module Lita
|
|
28
42
|
option :log_file,
|
29
43
|
aliases: "-l",
|
30
44
|
banner: "PATH",
|
31
|
-
default:
|
32
|
-
"/var/log/lita.log" : File.expand_path("lita.log", ENV["HOME"]),
|
45
|
+
default: file_path_for("lita.log", "/var/log"),
|
33
46
|
desc: "Path where the log file should be written when daemonized"
|
34
47
|
option :pid_file,
|
35
48
|
aliases: "-p",
|
36
49
|
banner: "PATH",
|
37
|
-
default:
|
38
|
-
"/var/run/lita.pid" : File.expand_path("lita.pid", ENV["HOME"]),
|
50
|
+
default: file_path_for("lita.pid", "/var/run"),
|
39
51
|
desc: "Path where the PID file should be written when daemonized"
|
40
52
|
option :kill,
|
41
53
|
aliases: "-k",
|
42
54
|
default: false,
|
43
55
|
desc: "Kill existing Lita processes when starting the daemon",
|
44
56
|
type: :boolean
|
57
|
+
# Starts Lita.
|
58
|
+
# @return [void]
|
45
59
|
def start
|
46
60
|
begin
|
47
61
|
Bundler.require
|
48
62
|
rescue Bundler::GemfileNotFound
|
49
|
-
no_gemfile_warning
|
50
|
-
The default command "start" must be run inside a Lita project. Try running \
|
51
|
-
`lita new` to generate a new Lita project or `lita help` to see all commands.
|
52
|
-
WARN
|
53
|
-
say no_gemfile_warning, :red
|
63
|
+
say I18n.t("lita.cli.no_gemfile_warning"), :red
|
54
64
|
abort
|
55
65
|
end
|
56
66
|
|
@@ -66,21 +76,32 @@ WARN
|
|
66
76
|
end
|
67
77
|
|
68
78
|
desc "new NAME", "Generates a new Lita project (default name: lita)"
|
79
|
+
# Generates a new Lita project.
|
80
|
+
# @param name [String] The directory name for the new project.
|
81
|
+
# @return [void]
|
69
82
|
def new(name = "lita")
|
70
83
|
directory "robot", name
|
71
84
|
end
|
72
85
|
|
73
86
|
desc "adapter NAME", "Generates a new Lita adapter"
|
87
|
+
# Generates a new Lita adapter.
|
88
|
+
# @param name [String] The name for the new adapter.
|
89
|
+
# @return [void]
|
74
90
|
def adapter(name)
|
75
91
|
generate_templates(generate_config(name, "adapter"))
|
76
92
|
end
|
77
93
|
|
78
94
|
desc "handler NAME", "Generates a new Lita handler"
|
95
|
+
# Generates a new Lita handler.
|
96
|
+
# @param name [String] The name for the new handler.
|
97
|
+
# @return [void]
|
79
98
|
def handler(name)
|
80
99
|
generate_templates(generate_config(name, "handler"))
|
81
100
|
end
|
82
101
|
|
83
102
|
desc "version", "Outputs the current version of Lita"
|
103
|
+
# Outputs the current version of Lita.
|
104
|
+
# @return [void]
|
84
105
|
def version
|
85
106
|
puts VERSION
|
86
107
|
end
|
@@ -139,11 +160,8 @@ WARN
|
|
139
160
|
"#{target}/spec/lita/#{namespace}/#{name}_spec.rb",
|
140
161
|
config
|
141
162
|
)
|
142
|
-
template(
|
143
|
-
|
144
|
-
"#{target}/spec/spec_helper.rb",
|
145
|
-
config
|
146
|
-
)
|
163
|
+
template("plugin/spec/spec_helper.tt", "#{target}/spec/spec_helper.rb", config)
|
164
|
+
template("plugin/locales/en.yml.tt", "#{target}/locales/en.yml", config)
|
147
165
|
copy_file("plugin/Gemfile", "#{target}/Gemfile")
|
148
166
|
template("plugin/gemspec.tt", "#{target}/#{gem_name}.gemspec", config)
|
149
167
|
copy_file("plugin/gitignore", "#{target}/.gitignore")
|
@@ -161,13 +179,9 @@ WARN
|
|
161
179
|
end
|
162
180
|
|
163
181
|
def optional_content
|
164
|
-
coveralls_question = <<-Q.chomp
|
165
|
-
Do you want to generate code coverage information with SimpleCov \
|
166
|
-
and Coveralls.io?
|
167
|
-
Q
|
168
182
|
{
|
169
|
-
travis: yes?("
|
170
|
-
coveralls: yes?(coveralls_question)
|
183
|
+
travis: yes?(I18n.t("lita.cli.travis_question")),
|
184
|
+
coveralls: yes?(I18n.t("lita.cli.coveralls_question"))
|
171
185
|
}
|
172
186
|
end
|
173
187
|
end
|
data/lib/lita/common.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "i18n"
|
2
|
+
require "i18n/backend/fallbacks"
|
3
|
+
|
4
|
+
module Lita
|
5
|
+
class << self
|
6
|
+
# Adds one or more paths to the I18n load path and reloads I18n.
|
7
|
+
# @param paths [String, Array<String>] The path(s) to add.
|
8
|
+
# @return [void]
|
9
|
+
# @since 3.0.0
|
10
|
+
def load_locales(paths)
|
11
|
+
I18n.load_path.concat(Array(paths))
|
12
|
+
I18n.reload!
|
13
|
+
end
|
14
|
+
|
15
|
+
# Sets I18n.locale, normalizing the provided locale name.
|
16
|
+
# @param new_locale [Symbol, String] The code of the locale to use.
|
17
|
+
# @return [void]
|
18
|
+
# @since 3.0.0
|
19
|
+
def locale=(new_locale)
|
20
|
+
I18n.locale = new_locale.to_s.tr("_", "-")
|
21
|
+
end
|
22
|
+
|
23
|
+
# The absolute path to Lita's templates directory.
|
24
|
+
# @return [String] The path.
|
25
|
+
# @since 3.0.0
|
26
|
+
def template_root
|
27
|
+
File.expand_path("../../../templates", __FILE__)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
33
|
+
Lita.load_locales(Dir[File.join(Lita.template_root, "locales", "*.yml")])
|
34
|
+
I18n.enforce_available_locales = false
|
35
|
+
Lita.locale = ENV["LANG"] unless ENV["LANG"].nil?
|
data/lib/lita/config.rb
CHANGED
@@ -6,15 +6,9 @@ module Lita
|
|
6
6
|
# @return [Lita::Config] The default configuration.
|
7
7
|
def default_config
|
8
8
|
new.tap do |c|
|
9
|
-
c
|
10
|
-
c.robot.name = "Lita"
|
11
|
-
c.robot.adapter = :shell
|
12
|
-
c.robot.log_level = :info
|
13
|
-
c.robot.admins = nil
|
9
|
+
load_robot_configs(c)
|
14
10
|
c.redis = new
|
15
|
-
c
|
16
|
-
c.http.port = 8080
|
17
|
-
c.http.debug = false
|
11
|
+
load_http_configs(c)
|
18
12
|
c.adapter = new
|
19
13
|
c.handlers = new
|
20
14
|
load_handler_configs(c)
|
@@ -30,11 +24,11 @@ module Lita
|
|
30
24
|
begin
|
31
25
|
load(config_path)
|
32
26
|
rescue Exception => e
|
33
|
-
Lita.logger.fatal
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
Lita.logger.fatal I18n.t(
|
28
|
+
"lita.config.exception",
|
29
|
+
message: e.message,
|
30
|
+
backtrace: e.backtrace.join("\n")
|
31
|
+
)
|
38
32
|
abort
|
39
33
|
end if File.exist?(config_path)
|
40
34
|
end
|
@@ -50,6 +44,25 @@ MSG
|
|
50
44
|
handler.default_config(handler_config)
|
51
45
|
end
|
52
46
|
end
|
47
|
+
|
48
|
+
# Adds and populates a Config object for the built-in web server.
|
49
|
+
def load_http_configs(config)
|
50
|
+
config.http = new
|
51
|
+
config.http.host = "0.0.0.0"
|
52
|
+
config.http.port = 8080
|
53
|
+
config.http.min_threads = 0
|
54
|
+
config.http.max_threads = 16
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds and populates a Config object for the Robot.
|
58
|
+
def load_robot_configs(config)
|
59
|
+
config.robot = new
|
60
|
+
config.robot.name = "Lita"
|
61
|
+
config.robot.adapter = :shell
|
62
|
+
config.robot.locale = I18n.locale
|
63
|
+
config.robot.log_level = :info
|
64
|
+
config.robot.admins = nil
|
65
|
+
end
|
53
66
|
end
|
54
67
|
|
55
68
|
# Sets a config key.
|
@@ -67,6 +80,13 @@ MSG
|
|
67
80
|
super(key.to_sym)
|
68
81
|
end
|
69
82
|
|
83
|
+
# Deeply freezes the object to prevent any further mutation.
|
84
|
+
# @return [void]
|
85
|
+
# @since 3.0.0
|
86
|
+
def finalize
|
87
|
+
IceNine.deep_freeze!(self)
|
88
|
+
end
|
89
|
+
|
70
90
|
# Allows keys to be read and written with struct-like syntax.
|
71
91
|
def method_missing(name, *args)
|
72
92
|
name_string = name.to_s
|