rita 0.1.0 → 5.0.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -0
  3. data/.rubocop.yml +51 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +16 -0
  6. data/CONTRIBUTING.md +18 -0
  7. data/Gemfile +5 -0
  8. data/{LICENSE.txt → LICENSE} +1 -3
  9. data/README.md +17 -24
  10. data/Rakefile +5 -5
  11. data/bin/lita +7 -0
  12. data/lib/lita/adapter.rb +147 -0
  13. data/lib/lita/adapters/shell.rb +126 -0
  14. data/lib/lita/adapters/test.rb +62 -0
  15. data/lib/lita/authorization.rb +112 -0
  16. data/lib/lita/callback.rb +39 -0
  17. data/lib/lita/cli.rb +218 -0
  18. data/lib/lita/configurable.rb +47 -0
  19. data/lib/lita/configuration_builder.rb +247 -0
  20. data/lib/lita/configuration_validator.rb +95 -0
  21. data/lib/lita/default_configuration.rb +122 -0
  22. data/lib/lita/errors.rb +25 -0
  23. data/lib/lita/handler/chat_router.rb +141 -0
  24. data/lib/lita/handler/common.rb +208 -0
  25. data/lib/lita/handler/event_router.rb +84 -0
  26. data/lib/lita/handler/http_router.rb +31 -0
  27. data/lib/lita/handler.rb +15 -0
  28. data/lib/lita/handlers/authorization.rb +129 -0
  29. data/lib/lita/handlers/help.rb +171 -0
  30. data/lib/lita/handlers/info.rb +66 -0
  31. data/lib/lita/handlers/room.rb +36 -0
  32. data/lib/lita/handlers/users.rb +37 -0
  33. data/lib/lita/http_callback.rb +46 -0
  34. data/lib/lita/http_route.rb +83 -0
  35. data/lib/lita/logger.rb +43 -0
  36. data/lib/lita/message.rb +124 -0
  37. data/lib/lita/middleware_registry.rb +39 -0
  38. data/lib/lita/namespace.rb +29 -0
  39. data/lib/lita/plugin_builder.rb +43 -0
  40. data/lib/lita/rack_app.rb +100 -0
  41. data/lib/lita/registry.rb +164 -0
  42. data/lib/lita/response.rb +65 -0
  43. data/lib/lita/robot.rb +273 -0
  44. data/lib/lita/room.rb +119 -0
  45. data/lib/lita/route_validator.rb +82 -0
  46. data/lib/lita/rspec/handler.rb +127 -0
  47. data/lib/lita/rspec/matchers/chat_route_matcher.rb +53 -0
  48. data/lib/lita/rspec/matchers/event_route_matcher.rb +29 -0
  49. data/lib/lita/rspec/matchers/http_route_matcher.rb +34 -0
  50. data/lib/lita/rspec.rb +48 -0
  51. data/lib/lita/source.rb +81 -0
  52. data/lib/lita/store.rb +23 -0
  53. data/lib/lita/target.rb +3 -0
  54. data/lib/lita/template.rb +71 -0
  55. data/lib/lita/template_resolver.rb +52 -0
  56. data/lib/lita/timer.rb +49 -0
  57. data/lib/lita/user.rb +157 -0
  58. data/lib/lita/util.rb +31 -0
  59. data/lib/lita/version.rb +6 -0
  60. data/lib/lita.rb +166 -0
  61. data/lib/rita.rb +2 -7
  62. data/rita.gemspec +50 -0
  63. data/spec/lita/adapter_spec.rb +54 -0
  64. data/spec/lita/adapters/shell_spec.rb +99 -0
  65. data/spec/lita/authorization_spec.rb +122 -0
  66. data/spec/lita/configuration_builder_spec.rb +247 -0
  67. data/spec/lita/configuration_validator_spec.rb +114 -0
  68. data/spec/lita/default_configuration_spec.rb +242 -0
  69. data/spec/lita/handler/chat_router_spec.rb +236 -0
  70. data/spec/lita/handler/common_spec.rb +289 -0
  71. data/spec/lita/handler/event_router_spec.rb +121 -0
  72. data/spec/lita/handler/http_router_spec.rb +155 -0
  73. data/spec/lita/handler_spec.rb +62 -0
  74. data/spec/lita/handlers/authorization_spec.rb +111 -0
  75. data/spec/lita/handlers/help_spec.rb +124 -0
  76. data/spec/lita/handlers/info_spec.rb +67 -0
  77. data/spec/lita/handlers/room_spec.rb +24 -0
  78. data/spec/lita/handlers/users_spec.rb +35 -0
  79. data/spec/lita/logger_spec.rb +28 -0
  80. data/spec/lita/message_spec.rb +178 -0
  81. data/spec/lita/plugin_builder_spec.rb +41 -0
  82. data/spec/lita/response_spec.rb +62 -0
  83. data/spec/lita/robot_spec.rb +285 -0
  84. data/spec/lita/room_spec.rb +136 -0
  85. data/spec/lita/rspec/handler_spec.rb +33 -0
  86. data/spec/lita/rspec_spec.rb +162 -0
  87. data/spec/lita/source_spec.rb +68 -0
  88. data/spec/lita/store_spec.rb +23 -0
  89. data/spec/lita/template_resolver_spec.rb +42 -0
  90. data/spec/lita/template_spec.rb +52 -0
  91. data/spec/lita/timer_spec.rb +32 -0
  92. data/spec/lita/user_spec.rb +167 -0
  93. data/spec/lita/util_spec.rb +18 -0
  94. data/spec/lita_spec.rb +227 -0
  95. data/spec/spec_helper.rb +35 -0
  96. data/spec/templates/basic.erb +1 -0
  97. data/spec/templates/basic.irc.erb +1 -0
  98. data/spec/templates/helpers.erb +1 -0
  99. data/spec/templates/interpolated.erb +1 -0
  100. data/templates/locales/en.yml +137 -0
  101. data/templates/plugin/Gemfile +5 -0
  102. data/templates/plugin/README.tt +29 -0
  103. data/templates/plugin/Rakefile +8 -0
  104. data/templates/plugin/gemspec.tt +27 -0
  105. data/templates/plugin/gitignore +18 -0
  106. data/templates/plugin/lib/lita/plugin_type/plugin.tt +19 -0
  107. data/templates/plugin/lib/plugin.tt +16 -0
  108. data/templates/plugin/locales/en.yml.tt +4 -0
  109. data/templates/plugin/spec/lita/plugin_type/plugin_spec.tt +6 -0
  110. data/templates/plugin/spec/spec_helper.tt +8 -0
  111. data/templates/plugin/templates/gitkeep +0 -0
  112. data/templates/robot/Gemfile +5 -0
  113. data/templates/robot/lita_config.rb +28 -0
  114. metadata +386 -20
  115. data/.standard.yml +0 -3
  116. data/CHANGELOG.md +0 -5
  117. data/CODE_OF_CONDUCT.md +0 -132
  118. data/lib/rita/version.rb +0 -5
  119. data/sig/rita.rbs +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f002b817d2d1f2e8d14e305469539677a58f7eb4cd0b228ac2871ed27da9f5b
4
- data.tar.gz: 62488454ffba6eac6db255c30fce2493cb9a4bbd891d2239ea09fc54459132e5
3
+ metadata.gz: 794929430473fc5c22aaf15d6224e391198e7ff1a86b4ed1470da3333f088a95
4
+ data.tar.gz: 3c2224e807b35317d5f5471b24d6cdfac1fa2dc1f8019ee26f608da3541c2625
5
5
  SHA512:
6
- metadata.gz: c5f078432a52addb9a2e62c340dab87add154545bac4be1aef8c12034cecbed5b41fea72a0fdbbb56993a1a7a27bbf82232886b0c70f39f66b8a24dc97a974fe
7
- data.tar.gz: ca037a350c53489610153657e88ff538ab2023cebe5fcfea87e0a840d9bc8302b08453d367bd1aa2a537f6a277f3d1d143dd0ee729ec2a5710553f87eb86950a
6
+ metadata.gz: a9ab511179725535882ba53c851dc12145af192ae0256ee3d295d74f259cc6610f2e6a70ace9394a4021e784b1bac6f89b53d8cf626cf47ce16a5dd9e32d2cb4
7
+ data.tar.gz: 33b57d7cb945f2db328b63955173e3ad687cf1dd80a919269312979ee26aba7438f09a93308cd970dd8b98fe3e56f02c67e55c35a14837eec49eb46cc9b5ea0f
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
19
+ lita_config.rb
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.3.6
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
@@ -1,6 +1,4 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2024 Ben Oakes
1
+ Copyright (c) 2013-2021 Jimmy Cuadra
4
2
 
5
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,39 +1,32 @@
1
- # Rita
1
+ # Lita
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rita`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- ## Installation
10
+ Automate your business and have fun with your very own robot companion.
8
11
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
12
+ ## Documentation
10
13
 
11
- Install the gem and add to the application's Gemfile by executing:
14
+ Please visit [lita.io](https://www.lita.io/) for comprehensive documentation.
12
15
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
16
+ ## Plugins
14
17
 
15
- If bundler is not being used to manage dependencies, install the gem by executing:
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
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
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
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rita. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/rita/blob/master/CODE_OF_CONDUCT.md).
24
+ See the [contribution guide](https://github.com/litaio/lita/blob/main/CONTRIBUTING.md).
32
25
 
33
- ## License
26
+ ## History
34
27
 
35
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
28
+ For a history of releases, see the [Releases](https://github.com/litaio/lita/releases) page.
36
29
 
37
- ## Code of Conduct
30
+ ## License
38
31
 
39
- Everyone interacting in the Rita project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/rita/blob/master/CODE_OF_CONDUCT.md).
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 "minitest/test_task"
4
+ require "rspec/core/rake_task"
5
+ require "rubocop/rake_task"
5
6
 
6
- Minitest::TestTask.create
7
+ RSpec::Core::RakeTask.new
8
+ RuboCop::RakeTask.new
7
9
 
8
- require "standard/rake"
9
-
10
- task default: %i[test standard]
10
+ task default: %i[spec rubocop]
data/bin/lita ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler"
5
+ require "lita/cli"
6
+
7
+ Lita::CLI.start
@@ -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