rita 0.1.0 → 5.0.0.alpha.1
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/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
|
+
[![Gem Version](https://badge.fury.io/rb/lita.svg)](https://rubygems.org/gems/lita)
|
4
|
+
[![Build Status](https://travis-ci.com/litaio/lita.svg?branch=main)](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
|