lita 2.7.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|