rita 0.1.0 → 5.0.0.alpha.1
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 +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/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 -21
- 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/lib/rita.rb +0 -8
- data/sig/rita.rbs +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72821e3e7d68813ee33cfdc4f67624106b759ef23a5ed6b9d7f3f8c442b0c8f1
|
|
4
|
+
data.tar.gz: ae7c90ec162cce0c9813db6d1c172cc9efaaa4a5a6082c9e48d1275790468645
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 64a2e9506a5370018ee52bf1b3b9aad43ddc36091a452bae55df49af0cd65fff96e9280607ee800f064a6df2eedb28eeea3b8ae53bd5a0450b89bd92248c585c
|
|
7
|
+
data.tar.gz: 2f3d1a793ef84817a738f081a0d5f03022366ec8fb13bd6b02cfd5349f5944c5f1ad683d8c22c9677dc6768a97bf6bfc454b1ed66ddb1c000e69adaff02be3c7
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: "enable"
|
|
3
|
+
|
|
4
|
+
Layout/ArgumentAlignment:
|
|
5
|
+
EnforcedStyle: "with_fixed_indentation"
|
|
6
|
+
Layout/HashAlignment:
|
|
7
|
+
EnforcedHashRocketStyle: "table"
|
|
8
|
+
Layout/EndAlignment:
|
|
9
|
+
EnforcedStyleAlignWith: "variable"
|
|
10
|
+
Layout/LineLength:
|
|
11
|
+
Max: 100
|
|
12
|
+
|
|
13
|
+
Lint/EmptyClass:
|
|
14
|
+
Enabled: false
|
|
15
|
+
|
|
16
|
+
Metrics/AbcSize:
|
|
17
|
+
Enabled: false
|
|
18
|
+
Metrics/BlockLength:
|
|
19
|
+
Enabled: false
|
|
20
|
+
Metrics/ClassLength:
|
|
21
|
+
Enabled: false
|
|
22
|
+
Metrics/MethodLength:
|
|
23
|
+
Enabled: false
|
|
24
|
+
Metrics/ParameterLists:
|
|
25
|
+
Max: 6
|
|
26
|
+
|
|
27
|
+
Naming/MethodParameterName:
|
|
28
|
+
Enabled: false
|
|
29
|
+
Naming/VariableNumber:
|
|
30
|
+
EnforcedStyle: "snake_case"
|
|
31
|
+
|
|
32
|
+
Style/Documentation:
|
|
33
|
+
Enabled: false
|
|
34
|
+
Style/DoubleNegation:
|
|
35
|
+
Enabled: false
|
|
36
|
+
Style/EachWithObject:
|
|
37
|
+
Enabled: false
|
|
38
|
+
Style/GuardClause:
|
|
39
|
+
Enabled: false
|
|
40
|
+
Style/SpecialGlobalVars:
|
|
41
|
+
Enabled: false
|
|
42
|
+
Style/StringLiterals:
|
|
43
|
+
EnforcedStyle: "double_quotes"
|
|
44
|
+
Style/StringLiteralsInInterpolation:
|
|
45
|
+
EnforcedStyle: "double_quotes"
|
|
46
|
+
Style/TrailingCommaInArguments:
|
|
47
|
+
Enabled: false
|
|
48
|
+
Style/TrailingCommaInArrayLiteral:
|
|
49
|
+
Enabled: false
|
|
50
|
+
Style/TrailingCommaInHashLiteral:
|
|
51
|
+
Enabled: false
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.0.1
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
language: "ruby"
|
|
2
|
+
sudo: false
|
|
3
|
+
cache: "bundler"
|
|
4
|
+
matrix:
|
|
5
|
+
include:
|
|
6
|
+
- rvm: "3.0"
|
|
7
|
+
fast_finish: true
|
|
8
|
+
script: "bundle exec rake"
|
|
9
|
+
before_install:
|
|
10
|
+
- "gem update --system"
|
|
11
|
+
- "gem update bundler"
|
|
12
|
+
services:
|
|
13
|
+
- "redis-server"
|
|
14
|
+
if: "type != push OR (tag IS blank AND branch = main)"
|
|
15
|
+
notifications:
|
|
16
|
+
email: false
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Contributing to Lita
|
|
2
|
+
|
|
3
|
+
## Issues
|
|
4
|
+
|
|
5
|
+
Found a bug in Lita? Open an issue on [GitHub Issues](https://github.com/litaio/lita/issues). For general questions, feedback, and discussion, please visit [Google Groups](https://groups.google.com/group/litaio) or the `#lita.io` channel on the [Freenode IRC network](https://webchat.freenode.net/).
|
|
6
|
+
|
|
7
|
+
## Pull requests
|
|
8
|
+
|
|
9
|
+
Interested in contributing to Lita? That's great, and thank you for your interest!
|
|
10
|
+
|
|
11
|
+
In order to keep Lita's codebase from growing too large, you're encouraged to implement new functionality via a [plugin](https://www.lita.io/plugin-authoring). If you're not able to achieve what you want with a plugin, then a pull request may be in order. Out of respect for your time, open an issue to discuss any non-trivial changes you intend to make before starting to write code. If you are planning a pull request to improve documentation, fix a bug, or improve performance, then feel free to proceed without opening an issue for discussion.
|
|
12
|
+
|
|
13
|
+
To get your contributions accepted, make sure:
|
|
14
|
+
|
|
15
|
+
* All the tests pass. Run `rspec`.
|
|
16
|
+
* No code quality warnings are generated by [RuboCop](https://github.com/bbatsov/rubocop). Run `rubocop`.
|
|
17
|
+
* Any new code paths you've added are covered by tests.
|
|
18
|
+
* Any new classes or methods you've added have API documentation compatible with [YARD](https://yardoc.org/). If you've significantly changed the behavior of an existing class or method, you should also update any existing API documentation.
|
data/Gemfile
ADDED
data/{LICENSE.txt → LICENSE}
RENAMED
data/README.md
CHANGED
|
@@ -1,39 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Lita
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://rubygems.org/gems/lita)
|
|
4
|
+
[](https://travis-ci.com/litaio/lita)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**Lita** is a chat bot written in [Ruby](https://www.ruby-lang.org/) with persistent storage provided by [Redis](https://redis.io/).
|
|
7
|
+
It uses a plugin system to connect to different chat services and to provide new behavior.
|
|
8
|
+
The plugin system uses the familiar tools of the Ruby ecosystem: [RubyGems](https://rubygems.org/) and [Bundler](https://bundler.io).
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
Automate your business and have fun with your very own robot companion.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## Documentation
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
Please visit [lita.io](https://www.lita.io/) for comprehensive documentation.
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
## Plugins
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
A list of all publicly available Lita plugins is available on the [lita.io plugins page](https://www.lita.io/plugins).
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## Usage
|
|
20
|
-
|
|
21
|
-
TODO: Write usage instructions here
|
|
22
|
-
|
|
23
|
-
## Development
|
|
24
|
-
|
|
25
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
26
|
-
|
|
27
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
20
|
+
The plugins page automatically updates daily with information from RubyGems. See [publishing](https://docs.lita.io/plugin-authoring/#publishing) for more information.
|
|
28
21
|
|
|
29
22
|
## Contributing
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
See the [contribution guide](https://github.com/litaio/lita/blob/main/CONTRIBUTING.md).
|
|
32
25
|
|
|
33
|
-
##
|
|
26
|
+
## History
|
|
34
27
|
|
|
35
|
-
|
|
28
|
+
For a history of releases, see the [Releases](https://github.com/litaio/lita/releases) page.
|
|
36
29
|
|
|
37
|
-
##
|
|
30
|
+
## License
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
[MIT](https://opensource.org/licenses/MIT)
|
data/Rakefile
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
|
-
require "
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
require "rubocop/rake_task"
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
RSpec::Core::RakeTask.new
|
|
8
|
+
RuboCop::RakeTask.new
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
task default: %i[test standard]
|
|
10
|
+
task default: %i[spec rubocop]
|
data/bin/lita
ADDED
data/lib/lita/adapter.rb
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "i18n"
|
|
4
|
+
|
|
5
|
+
require_relative "configurable"
|
|
6
|
+
require_relative "namespace"
|
|
7
|
+
|
|
8
|
+
module Lita
|
|
9
|
+
# Adapters are the glue between Lita's API and a chat service.
|
|
10
|
+
class Adapter
|
|
11
|
+
# The names of methods that should be implemented by an adapter.
|
|
12
|
+
# @since 4.4.0
|
|
13
|
+
REQUIRED_METHODS = %i[
|
|
14
|
+
chat_service
|
|
15
|
+
join
|
|
16
|
+
part
|
|
17
|
+
roster
|
|
18
|
+
run
|
|
19
|
+
send_messages
|
|
20
|
+
set_topic
|
|
21
|
+
shut_down
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
extend Namespace
|
|
25
|
+
extend Configurable
|
|
26
|
+
|
|
27
|
+
# The instance of {Robot}.
|
|
28
|
+
# @return [Robot]
|
|
29
|
+
attr_reader :robot
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# Returns the translation for a key, automatically namespaced to the adapter.
|
|
33
|
+
# @param key [String] The key of the translation.
|
|
34
|
+
# @param hash [Hash] An optional hash of values to be interpolated in the string.
|
|
35
|
+
# @return [String] The translated string.
|
|
36
|
+
def translate(key, hash = {})
|
|
37
|
+
I18n.translate("lita.adapters.#{namespace}.#{key}", **hash)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
alias t translate
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param robot [Robot] The currently running robot.
|
|
44
|
+
def initialize(robot)
|
|
45
|
+
@robot = robot
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The adapter's configuration object.
|
|
49
|
+
# @return [Configuration] The adapter's configuration object.
|
|
50
|
+
# @since 4.0.0
|
|
51
|
+
def config
|
|
52
|
+
robot.config.adapters.public_send(self.class.namespace)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @!method chat_service
|
|
56
|
+
# May return an object exposing chat-service-specific APIs.
|
|
57
|
+
# @return [Object, nil] The chat service API object, if any.
|
|
58
|
+
# @abstract This should be implemented by the adapter.
|
|
59
|
+
# @since 4.6.0
|
|
60
|
+
|
|
61
|
+
# @!method join(room_id)
|
|
62
|
+
# Joins the room with the specified ID.
|
|
63
|
+
# @param room_id [String] The ID of the room.
|
|
64
|
+
# @return [void]
|
|
65
|
+
# @abstract This should be implemented by the adapter.
|
|
66
|
+
# @since 3.0.0
|
|
67
|
+
|
|
68
|
+
# @!method part(room_id)
|
|
69
|
+
# Parts from the room with the specified ID.
|
|
70
|
+
# @param room_id [String] The ID of the room.
|
|
71
|
+
# @return [void]
|
|
72
|
+
# @abstract This should be implemented by the adapter.
|
|
73
|
+
# @since 3.0.0
|
|
74
|
+
|
|
75
|
+
# @!method roster(room)
|
|
76
|
+
# Get a list of users that are online in the given room.
|
|
77
|
+
# @param room [Room] The room to return a roster for.
|
|
78
|
+
# @return [Array<User>] An array of users.
|
|
79
|
+
# @abstract This should be implemented by the adapter.
|
|
80
|
+
# @since 4.4.0
|
|
81
|
+
|
|
82
|
+
# @!method run
|
|
83
|
+
# The main loop. Should connect to the chat service, listen for incoming
|
|
84
|
+
# messages, create {Message} objects from them, and dispatch them to
|
|
85
|
+
# the robot by calling {Robot#receive}.
|
|
86
|
+
# @return [void]
|
|
87
|
+
# @abstract This should be implemented by the adapter.
|
|
88
|
+
|
|
89
|
+
# @!method send_messages(target, strings)
|
|
90
|
+
# Sends one or more messages to a user or room.
|
|
91
|
+
# @param target [Source] The user or room to send messages to.
|
|
92
|
+
# @param strings [Array<String>] An array of messages to send.
|
|
93
|
+
# @return [void]
|
|
94
|
+
# @abstract This should be implemented by the adapter.
|
|
95
|
+
|
|
96
|
+
# @!method set_topic(target, topic)
|
|
97
|
+
# Sets the topic for a room.
|
|
98
|
+
# @param target [Source] The room to change the topic for.
|
|
99
|
+
# @param topic [String] The new topic.
|
|
100
|
+
# @return [void]
|
|
101
|
+
# @abstract This should be implemented by the adapter.
|
|
102
|
+
|
|
103
|
+
# @!method shut_down
|
|
104
|
+
# Performs any clean up necessary when disconnecting from the chat service.
|
|
105
|
+
# @return [void]
|
|
106
|
+
# @abstract This should be implemented by the adapter.
|
|
107
|
+
REQUIRED_METHODS.each do |method|
|
|
108
|
+
define_method(method) do |*_args|
|
|
109
|
+
robot.logger.warn(I18n.t("lita.adapter.method_not_implemented", method: method))
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# The robot's logger.
|
|
114
|
+
# @return [::Logger] The robot's logger.
|
|
115
|
+
# @since 4.0.2
|
|
116
|
+
def log
|
|
117
|
+
robot.logger
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Formats a name for "mentioning" a user in a group chat. Override this
|
|
121
|
+
# method in child classes to customize the mention format for the chat
|
|
122
|
+
# service.
|
|
123
|
+
# @param name [String] The name to format as a mention name.
|
|
124
|
+
# @return [String] The formatted mention name.
|
|
125
|
+
# @since 3.1.0
|
|
126
|
+
def mention_format(name)
|
|
127
|
+
"#{name}:"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Runs a block of code concurrently. By default the block is run in a new thread. Override
|
|
131
|
+
# this method in child classes to customize the mechanism for concurrent code execution.
|
|
132
|
+
#
|
|
133
|
+
# @yield A block of code to run concurrently.
|
|
134
|
+
# @return [void]
|
|
135
|
+
# @since 5.0.0
|
|
136
|
+
def run_concurrently(&block)
|
|
137
|
+
Thread.new(&block)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @see .translate
|
|
141
|
+
def translate(*args)
|
|
142
|
+
self.class.translate(*args)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
alias t translate
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
5
|
+
require "readline"
|
|
6
|
+
|
|
7
|
+
require_relative "../adapter"
|
|
8
|
+
require_relative "../message"
|
|
9
|
+
require_relative "../source"
|
|
10
|
+
require_relative "../user"
|
|
11
|
+
|
|
12
|
+
module Lita
|
|
13
|
+
# A namespace to hold all subclasses of {Adapter}.
|
|
14
|
+
module Adapters
|
|
15
|
+
# An adapter that runs Lita in a UNIX shell.
|
|
16
|
+
class Shell < Adapter
|
|
17
|
+
config :private_chat, default: false
|
|
18
|
+
|
|
19
|
+
def initialize(robot)
|
|
20
|
+
super
|
|
21
|
+
|
|
22
|
+
self.user = User.create(1, name: "Shell User")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
|
26
|
+
|
|
27
|
+
# Returns the users in the room, which is only ever the "Shell User."
|
|
28
|
+
# @param room [Room] The room to return a roster for. Not used in this adapter.
|
|
29
|
+
# @return [Array<User>] The users in the room.
|
|
30
|
+
# @since 4.4.0
|
|
31
|
+
def roster(room)
|
|
32
|
+
[user]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
|
36
|
+
|
|
37
|
+
# Displays a prompt and requests input in a loop, passing the incoming messages to the robot.
|
|
38
|
+
# @return [void]
|
|
39
|
+
def run
|
|
40
|
+
room = robot.config.adapters.shell.private_chat ? nil : "shell"
|
|
41
|
+
@source = Source.new(user: user, room: room)
|
|
42
|
+
puts t("startup_message")
|
|
43
|
+
robot.trigger(:connected)
|
|
44
|
+
|
|
45
|
+
run_loop
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Overrides {run_concurrently} to block instead. Since there is no separate UI element for the
|
|
49
|
+
# user to enter text, we need to wait for all output for the robot before printing the next
|
|
50
|
+
# input prompt.
|
|
51
|
+
#
|
|
52
|
+
# @yield A block of code to run.
|
|
53
|
+
# @return [void]
|
|
54
|
+
# @since 5.0.0
|
|
55
|
+
def run_concurrently(&block)
|
|
56
|
+
block.call
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Outputs outgoing messages to the shell.
|
|
60
|
+
# @param _target [Source] Unused, since there is only one user in the
|
|
61
|
+
# shell environment.
|
|
62
|
+
# @param strings [Array<String>] An array of strings to output.
|
|
63
|
+
# @return [void]
|
|
64
|
+
def send_messages(_target, strings)
|
|
65
|
+
strings = Array(strings)
|
|
66
|
+
strings.reject!(&:empty?)
|
|
67
|
+
unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ || !$stdout.tty?
|
|
68
|
+
strings.map! { |string| "\e[32m#{string}\e[0m" }
|
|
69
|
+
end
|
|
70
|
+
puts strings
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Adds a blank line for a nice looking exit.
|
|
74
|
+
# @return [void]
|
|
75
|
+
def shut_down
|
|
76
|
+
puts
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
attr_accessor :user
|
|
82
|
+
|
|
83
|
+
def build_message(input, source)
|
|
84
|
+
message = Message.new(robot, input, source)
|
|
85
|
+
message.command! if robot.config.adapters.shell.private_chat
|
|
86
|
+
message
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize_history(input)
|
|
90
|
+
if input == "" || (Readline::HISTORY.size >= 2 && input == Readline::HISTORY[-2])
|
|
91
|
+
Readline::HISTORY.pop
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def normalize_input(input)
|
|
96
|
+
input.chomp.strip
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def read_input
|
|
100
|
+
input = Readline.readline("#{robot.name} > ", true)
|
|
101
|
+
# Input read via rb-readline will always be encoded as US-ASCII.
|
|
102
|
+
# @see https://github.com/ConnorAtherton/rb-readline/blob/9fba246073f78831b7c7129c76cc07d8476a8892/lib/readline.rb#L1
|
|
103
|
+
input&.dup&.force_encoding(Encoding.default_external)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def run_loop
|
|
107
|
+
exit_keywords = %w[exit quit].freeze
|
|
108
|
+
|
|
109
|
+
loop do
|
|
110
|
+
input = read_input
|
|
111
|
+
if input.nil?
|
|
112
|
+
puts
|
|
113
|
+
break
|
|
114
|
+
end
|
|
115
|
+
input = normalize_input(input)
|
|
116
|
+
normalize_history(input)
|
|
117
|
+
break if exit_keywords.include?(input)
|
|
118
|
+
|
|
119
|
+
robot.receive(build_message(input, @source))
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
Lita.register_adapter(:shell, Shell)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../adapter"
|
|
4
|
+
|
|
5
|
+
module Lita
|
|
6
|
+
# A namespace to hold all subclasses of {Adapter}.
|
|
7
|
+
module Adapters
|
|
8
|
+
# An adapter for testing Lita and Lita plugins.
|
|
9
|
+
# @since 4.6.0
|
|
10
|
+
class Test < Adapter
|
|
11
|
+
# When true, calls to {#run_concurrently} will block the current thread. This is the default
|
|
12
|
+
# because it's desirable for the majority of tests. It should be set to +false+ for tests
|
|
13
|
+
# specifically testing asynchrony.
|
|
14
|
+
config :blocking, types: [TrueClass, FalseClass], default: true
|
|
15
|
+
|
|
16
|
+
# Adapter-specific methods exposed through {Robot}.
|
|
17
|
+
class ChatService
|
|
18
|
+
def initialize(sent_messages)
|
|
19
|
+
@sent_messages = sent_messages
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# An array of recorded outgoing messages.
|
|
23
|
+
def sent_messages
|
|
24
|
+
@sent_messages.dup
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(robot)
|
|
29
|
+
super
|
|
30
|
+
|
|
31
|
+
self.sent_messages = []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Adapter-specific methods available via {Robot#chat_service}.
|
|
35
|
+
def chat_service
|
|
36
|
+
ChatService.new(sent_messages)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Records outgoing messages.
|
|
40
|
+
def send_messages(_target, strings)
|
|
41
|
+
sent_messages.concat(strings)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# If the +blocking+ config attribute is +true+ (which is the default), the block will be run
|
|
45
|
+
# on the current thread, so tests can be written without concern for asynchrony.
|
|
46
|
+
def run_concurrently(&block)
|
|
47
|
+
if config.blocking
|
|
48
|
+
block.call
|
|
49
|
+
else
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# An array of recorded outgoing messages.
|
|
57
|
+
attr_accessor :sent_messages
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Lita.register_adapter(:test, Test)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "redis-namespace"
|
|
4
|
+
|
|
5
|
+
require_relative "user"
|
|
6
|
+
|
|
7
|
+
module Lita
|
|
8
|
+
# Methods for querying and manipulating authorization groups.
|
|
9
|
+
class Authorization
|
|
10
|
+
# @param robot [Robot] The currently running robot.
|
|
11
|
+
def initialize(robot)
|
|
12
|
+
self.robot = robot
|
|
13
|
+
self.redis = Redis::Namespace.new("auth", redis: robot.redis)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Adds a user to an authorization group.
|
|
17
|
+
# @param requesting_user [User] The user who sent the command.
|
|
18
|
+
# @param user [User] The user to add to the group.
|
|
19
|
+
# @param group [Symbol, String] The name of the group.
|
|
20
|
+
# @return [Symbol] :unauthorized if the requesting user is not authorized.
|
|
21
|
+
# @return [Boolean] true if the user was added. false if the user was
|
|
22
|
+
# already in the group.
|
|
23
|
+
def add_user_to_group(requesting_user, user, group)
|
|
24
|
+
return :unauthorized unless user_is_admin?(requesting_user)
|
|
25
|
+
|
|
26
|
+
add_user_to_group!(user, group)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Adds a user to an authorization group without validating the permissions
|
|
30
|
+
# of the requesting user.
|
|
31
|
+
# @param user [User] The user to add to the group.
|
|
32
|
+
# @param group [Symbol, String] The name of the group.
|
|
33
|
+
# @return [Boolean] true if the user was added. false if the user was
|
|
34
|
+
# already in the group.
|
|
35
|
+
# @since 4.0.0
|
|
36
|
+
def add_user_to_group!(user, group)
|
|
37
|
+
redis.sadd(normalize_group(group), user.id)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Removes a user from an authorization group.
|
|
41
|
+
# @param requesting_user [User] The user who sent the command.
|
|
42
|
+
# @param user [User] The user to remove from the group.
|
|
43
|
+
# @param group [Symbol, String] The name of the group.
|
|
44
|
+
# @return [Symbol] :unauthorized if the requesting user is not authorized.
|
|
45
|
+
# @return [Boolean] true if the user was removed. false if the user was
|
|
46
|
+
# not in the group.
|
|
47
|
+
def remove_user_from_group(requesting_user, user, group)
|
|
48
|
+
return :unauthorized unless user_is_admin?(requesting_user)
|
|
49
|
+
|
|
50
|
+
remove_user_from_group!(user, group)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Removes a suer from an authorization group without validating the
|
|
54
|
+
# permissions of the requesting user.
|
|
55
|
+
# @param user [User] The user to remove from the group.
|
|
56
|
+
# @param group [Symbol, String] The name of the group.
|
|
57
|
+
# @return [Boolean] true if the user was removed. false if the user was
|
|
58
|
+
# not in the group.
|
|
59
|
+
# @since 4.0.0
|
|
60
|
+
def remove_user_from_group!(user, group)
|
|
61
|
+
redis.srem(normalize_group(group), user.id)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Checks if a user is in an authorization group.
|
|
65
|
+
# @param user [User] The user.
|
|
66
|
+
# @param group [Symbol, String] The name of the group.
|
|
67
|
+
# @return [Boolean] Whether or not the user is in the group.
|
|
68
|
+
def user_in_group?(user, group)
|
|
69
|
+
group = normalize_group(group)
|
|
70
|
+
return user_is_admin?(user) if group == "admins"
|
|
71
|
+
|
|
72
|
+
redis.sismember(group, user.id)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Checks if a user is an administrator.
|
|
76
|
+
# @param user [User] The user.
|
|
77
|
+
# @return [Boolean] Whether or not the user is an administrator.
|
|
78
|
+
def user_is_admin?(user)
|
|
79
|
+
Array(robot.config.robot.admins).include?(user.id)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Returns a list of all authorization groups.
|
|
83
|
+
# @return [Array<Symbol>] The names of all authorization groups.
|
|
84
|
+
def groups
|
|
85
|
+
redis.keys("*").map(&:to_sym)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Returns a hash of authorization group names and the users in them.
|
|
89
|
+
# @return [Hash] A map of +Symbol+ group names to {User} objects.
|
|
90
|
+
def groups_with_users
|
|
91
|
+
groups.reduce({}) do |list, group|
|
|
92
|
+
list[group] = redis.smembers(group).map do |user_id|
|
|
93
|
+
User.find_by_id(user_id)
|
|
94
|
+
end
|
|
95
|
+
list
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
# Ensures that group names are stored consistently in Redis.
|
|
102
|
+
def normalize_group(group)
|
|
103
|
+
group.to_s.downcase.strip
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [Redis::Namespace] A Redis::Namespace for authorization data.
|
|
107
|
+
attr_accessor :redis
|
|
108
|
+
|
|
109
|
+
# @return [Robot] The currently running robot.
|
|
110
|
+
attr_accessor :robot
|
|
111
|
+
end
|
|
112
|
+
end
|