onyxcord 2.1.0 → 3.2.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.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/lib/onyxcord/application_commands/command.rb +4 -97
  3. data/lib/onyxcord/application_commands/context.rb +4 -94
  4. data/lib/onyxcord/application_commands/option.rb +4 -75
  5. data/lib/onyxcord/application_commands/registry.rb +4 -45
  6. data/lib/onyxcord/application_commands.rb +8 -5
  7. data/lib/onyxcord/bot.rb +24 -1759
  8. data/lib/onyxcord/{cache.rb → cache/manager.rb} +16 -14
  9. data/lib/onyxcord/cache/stores/gateway.rb +272 -0
  10. data/lib/onyxcord/commands/bot/channels.rb +48 -0
  11. data/lib/onyxcord/commands/bot/execution.rb +174 -0
  12. data/lib/onyxcord/commands/bot/message_dispatch.rb +58 -0
  13. data/lib/onyxcord/commands/bot/permissions.rb +72 -0
  14. data/lib/onyxcord/commands/bot.rb +209 -0
  15. data/lib/onyxcord/commands/container.rb +21 -2
  16. data/lib/onyxcord/commands/parser.rb +30 -1
  17. data/lib/onyxcord/container.rb +16 -960
  18. data/lib/onyxcord/core/bot/application_commands.rb +210 -0
  19. data/lib/onyxcord/core/bot/awaits.rb +112 -0
  20. data/lib/onyxcord/core/bot/invites.rb +42 -0
  21. data/lib/onyxcord/core/bot/messaging.rb +161 -0
  22. data/lib/onyxcord/core/bot/oauth.rb +27 -0
  23. data/lib/onyxcord/core/bot/presence.rb +103 -0
  24. data/lib/onyxcord/core/bot/runtime.rb +183 -0
  25. data/lib/onyxcord/core/bot/voice.rb +69 -0
  26. data/lib/onyxcord/{logger.rb → core/logger.rb} +5 -0
  27. data/lib/onyxcord/core/version.rb +5 -0
  28. data/lib/onyxcord/events/await/base.rb +48 -0
  29. data/lib/onyxcord/events/await.rb +1 -46
  30. data/lib/onyxcord/events/ban/base.rb +54 -0
  31. data/lib/onyxcord/events/ban/remove.rb +11 -0
  32. data/lib/onyxcord/events/bans.rb +2 -58
  33. data/lib/onyxcord/events/channels/base.rb +91 -0
  34. data/lib/onyxcord/events/channels/delete.rb +73 -0
  35. data/lib/onyxcord/events/channels/pins.rb +44 -0
  36. data/lib/onyxcord/events/channels/recipients.rb +77 -0
  37. data/lib/onyxcord/events/channels.rb +4 -239
  38. data/lib/onyxcord/events/guilds/audit_logs.rb +79 -0
  39. data/lib/onyxcord/events/guilds/base.rb +97 -0
  40. data/lib/onyxcord/events/guilds/emoji.rb +147 -0
  41. data/lib/onyxcord/events/guilds.rb +3 -267
  42. data/lib/onyxcord/events/handlers/channels.rb +133 -0
  43. data/lib/onyxcord/events/handlers/core.rb +146 -0
  44. data/lib/onyxcord/events/handlers/guilds.rb +375 -0
  45. data/lib/onyxcord/events/handlers/interactions.rb +238 -0
  46. data/lib/onyxcord/events/handlers/lifetime.rb +71 -0
  47. data/lib/onyxcord/events/handlers/messages.rb +218 -0
  48. data/lib/onyxcord/events/handlers/presence.rb +56 -0
  49. data/lib/onyxcord/events/handlers/raw.rb +39 -0
  50. data/lib/onyxcord/events/handlers/reactions.rb +98 -0
  51. data/lib/onyxcord/events/handlers/scheduled_events.rb +243 -0
  52. data/lib/onyxcord/events/handlers/voice.rb +46 -0
  53. data/lib/onyxcord/events/integration/base.rb +45 -0
  54. data/lib/onyxcord/events/integration/create.rb +11 -0
  55. data/lib/onyxcord/events/integration/delete.rb +48 -0
  56. data/lib/onyxcord/events/integration/update.rb +11 -0
  57. data/lib/onyxcord/events/integrations.rb +4 -98
  58. data/lib/onyxcord/events/interactions/application_commands.rb +185 -0
  59. data/lib/onyxcord/events/interactions/autocomplete.rb +62 -0
  60. data/lib/onyxcord/events/interactions/base.rb +175 -0
  61. data/lib/onyxcord/events/interactions/components.rb +221 -0
  62. data/lib/onyxcord/events/interactions/permissions.rb +53 -0
  63. data/lib/onyxcord/events/interactions.rb +5 -622
  64. data/lib/onyxcord/events/invite/create.rb +79 -0
  65. data/lib/onyxcord/events/invite/delete.rb +55 -0
  66. data/lib/onyxcord/events/invites.rb +2 -125
  67. data/lib/onyxcord/events/lifetime/base.rb +31 -0
  68. data/lib/onyxcord/events/lifetime.rb +1 -29
  69. data/lib/onyxcord/events/member/add.rb +12 -0
  70. data/lib/onyxcord/events/member/base.rb +67 -0
  71. data/lib/onyxcord/events/member/delete.rb +21 -0
  72. data/lib/onyxcord/events/member/update.rb +25 -0
  73. data/lib/onyxcord/events/members.rb +4 -108
  74. data/lib/onyxcord/events/message/base.rb +295 -0
  75. data/lib/onyxcord/events/message/ids.rb +71 -0
  76. data/lib/onyxcord/events/message/mentions.rb +55 -0
  77. data/lib/onyxcord/events/message/updates.rb +27 -0
  78. data/lib/onyxcord/events/message.rb +4 -397
  79. data/lib/onyxcord/events/poll/add.rb +11 -0
  80. data/lib/onyxcord/events/poll/base.rb +106 -0
  81. data/lib/onyxcord/events/poll/remove.rb +11 -0
  82. data/lib/onyxcord/events/polls.rb +3 -116
  83. data/lib/onyxcord/events/presence/playing.rb +77 -0
  84. data/lib/onyxcord/events/presence/status.rb +60 -0
  85. data/lib/onyxcord/events/presence.rb +2 -129
  86. data/lib/onyxcord/events/raw/base.rb +74 -0
  87. data/lib/onyxcord/events/raw.rb +1 -72
  88. data/lib/onyxcord/events/reactions/base.rb +143 -0
  89. data/lib/onyxcord/events/reactions/clear.rb +99 -0
  90. data/lib/onyxcord/events/reactions.rb +2 -216
  91. data/lib/onyxcord/events/role/create.rb +46 -0
  92. data/lib/onyxcord/events/role/delete.rb +40 -0
  93. data/lib/onyxcord/events/role/update.rb +11 -0
  94. data/lib/onyxcord/events/roles.rb +3 -85
  95. data/lib/onyxcord/events/scheduled_events/events.rb +112 -0
  96. data/lib/onyxcord/events/scheduled_events/users.rb +91 -0
  97. data/lib/onyxcord/events/scheduled_events.rb +2 -169
  98. data/lib/onyxcord/events/thread/base.rb +49 -0
  99. data/lib/onyxcord/events/thread/members.rb +44 -0
  100. data/lib/onyxcord/events/thread/update.rb +18 -0
  101. data/lib/onyxcord/events/threads.rb +3 -98
  102. data/lib/onyxcord/events/typing/base.rb +73 -0
  103. data/lib/onyxcord/events/typing.rb +1 -71
  104. data/lib/onyxcord/events/voice/server_update.rb +48 -0
  105. data/lib/onyxcord/events/voice/state_update.rb +137 -0
  106. data/lib/onyxcord/events/voice_server_update.rb +2 -46
  107. data/lib/onyxcord/events/voice_state_update.rb +2 -135
  108. data/lib/onyxcord/events/webhook/update.rb +65 -0
  109. data/lib/onyxcord/events/webhooks.rb +1 -63
  110. data/lib/onyxcord/gateway/client.rb +460 -0
  111. data/lib/onyxcord/interactions/command.rb +120 -0
  112. data/lib/onyxcord/interactions/context.rb +109 -0
  113. data/lib/onyxcord/interactions/internal/application_command.rb +203 -0
  114. data/lib/onyxcord/interactions/internal/message.rb +152 -0
  115. data/lib/onyxcord/interactions/internal/metadata.rb +109 -0
  116. data/lib/onyxcord/interactions/internal/option_builder.rb +230 -0
  117. data/lib/onyxcord/interactions/internal/permission_builder.rb +90 -0
  118. data/lib/onyxcord/interactions/option.rb +88 -0
  119. data/lib/onyxcord/interactions/registry.rb +50 -0
  120. data/lib/onyxcord/interactions.rb +16 -0
  121. data/lib/onyxcord/internal/async_runtime.rb +32 -0
  122. data/lib/onyxcord/internal/event_bus.rb +591 -0
  123. data/lib/onyxcord/internal/event_executor.rb +155 -0
  124. data/lib/onyxcord/internal/gateway/opcodes.rb +22 -0
  125. data/lib/onyxcord/internal/gateway/session.rb +45 -0
  126. data/lib/onyxcord/internal/http.rb +189 -0
  127. data/lib/onyxcord/internal/json.rb +51 -0
  128. data/lib/onyxcord/{message_payload.rb → internal/message_payload.rb} +3 -3
  129. data/lib/onyxcord/internal/rate_limiter/async_rest.rb +151 -0
  130. data/lib/onyxcord/internal/rate_limiter/gateway.rb +44 -0
  131. data/lib/onyxcord/internal/rate_limiter/rest.rb +144 -0
  132. data/lib/onyxcord/internal/websocket.rb +79 -0
  133. data/lib/onyxcord/light/data.rb +1 -1
  134. data/lib/onyxcord/light/integrations.rb +1 -1
  135. data/lib/onyxcord/light/light_bot.rb +7 -7
  136. data/lib/onyxcord/{data → models}/activity.rb +2 -2
  137. data/lib/onyxcord/{data → models}/application.rb +3 -3
  138. data/lib/onyxcord/{data → models}/avatar_decoration.rb +1 -1
  139. data/lib/onyxcord/{data → models}/channel.rb +23 -23
  140. data/lib/onyxcord/{data → models}/collectibles.rb +1 -1
  141. data/lib/onyxcord/{data → models}/emoji.rb +1 -1
  142. data/lib/onyxcord/models/interaction.rb +450 -0
  143. data/lib/onyxcord/{data → models}/invite.rb +1 -1
  144. data/lib/onyxcord/{data → models}/member.rb +11 -11
  145. data/lib/onyxcord/{data → models}/message.rb +18 -14
  146. data/lib/onyxcord/{data → models}/poll.rb +2 -2
  147. data/lib/onyxcord/{data → models}/primary_server.rb +1 -1
  148. data/lib/onyxcord/{data → models}/profile.rb +4 -4
  149. data/lib/onyxcord/{data → models}/role.rb +6 -12
  150. data/lib/onyxcord/{data → models}/scheduled_event.rb +5 -5
  151. data/lib/onyxcord/{data → models}/server.rb +41 -41
  152. data/lib/onyxcord/{data → models}/server_preview.rb +2 -2
  153. data/lib/onyxcord/{data → models}/team.rb +1 -1
  154. data/lib/onyxcord/{data → models}/user.rb +5 -5
  155. data/lib/onyxcord/{data → models}/webhook.rb +10 -10
  156. data/lib/onyxcord/models.rb +57 -0
  157. data/lib/onyxcord/{api.rb → rest/client.rb} +23 -22
  158. data/lib/onyxcord/{api → rest/routes}/application.rb +79 -57
  159. data/lib/onyxcord/rest/routes/channel/base.rb +73 -0
  160. data/lib/onyxcord/rest/routes/channel/groups.rb +78 -0
  161. data/lib/onyxcord/rest/routes/channel/messages.rb +130 -0
  162. data/lib/onyxcord/rest/routes/channel/permissions.rb +73 -0
  163. data/lib/onyxcord/rest/routes/channel/pins.rb +45 -0
  164. data/lib/onyxcord/rest/routes/channel/polls.rb +32 -0
  165. data/lib/onyxcord/rest/routes/channel/reactions.rb +86 -0
  166. data/lib/onyxcord/rest/routes/channel/threads.rb +176 -0
  167. data/lib/onyxcord/rest/routes/channel/webhooks.rb +47 -0
  168. data/lib/onyxcord/rest/routes/channel.rb +12 -0
  169. data/lib/onyxcord/{api/interaction.rb → rest/routes/interaction/base.rb} +7 -25
  170. data/lib/onyxcord/rest/routes/interaction/response.rb +24 -0
  171. data/lib/onyxcord/rest/routes/interaction.rb +4 -0
  172. data/lib/onyxcord/{api → rest/routes}/invite.rb +7 -7
  173. data/lib/onyxcord/rest/routes/server/admin.rb +174 -0
  174. data/lib/onyxcord/rest/routes/server/assets.rb +25 -0
  175. data/lib/onyxcord/rest/routes/server/bans.rb +67 -0
  176. data/lib/onyxcord/rest/routes/server/base.rb +77 -0
  177. data/lib/onyxcord/rest/routes/server/channels.rb +58 -0
  178. data/lib/onyxcord/rest/routes/server/emoji.rb +48 -0
  179. data/lib/onyxcord/rest/routes/server/members.rb +109 -0
  180. data/lib/onyxcord/rest/routes/server/messages.rb +19 -0
  181. data/lib/onyxcord/rest/routes/server/roles.rb +156 -0
  182. data/lib/onyxcord/rest/routes/server/scheduled_events.rb +86 -0
  183. data/lib/onyxcord/rest/routes/server.rb +12 -0
  184. data/lib/onyxcord/{api → rest/routes}/user.rb +23 -23
  185. data/lib/onyxcord/{api → rest/routes}/webhook.rb +22 -22
  186. data/lib/onyxcord/{allowed_mentions.rb → utils/allowed_mentions.rb} +1 -1
  187. data/lib/onyxcord/utils/timestamp.rb +69 -0
  188. data/lib/onyxcord/voice/{voice_bot.rb → client.rb} +4 -4
  189. data/lib/onyxcord/voice/encoder.rb +1 -1
  190. data/lib/onyxcord/voice/network/udp.rb +135 -0
  191. data/lib/onyxcord/voice/network/websocket.rb +225 -0
  192. data/lib/onyxcord/voice/network.rb +5 -357
  193. data/lib/onyxcord/webhooks/builder.rb +1 -1
  194. data/lib/onyxcord/webhooks/client.rb +13 -13
  195. data/lib/onyxcord/webhooks/modal/group_builder.rb +55 -0
  196. data/lib/onyxcord/webhooks/modal/label_builder.rb +183 -0
  197. data/lib/onyxcord/webhooks/modal.rb +2 -239
  198. data/lib/onyxcord/webhooks/version.rb +3 -3
  199. data/lib/onyxcord/webhooks/view/container_builder.rb +80 -0
  200. data/lib/onyxcord/webhooks/view/file_builder.rb +24 -0
  201. data/lib/onyxcord/webhooks/view/media_gallery_builder.rb +50 -0
  202. data/lib/onyxcord/webhooks/view/row_builder.rb +120 -0
  203. data/lib/onyxcord/webhooks/view/section_builder.rb +61 -0
  204. data/lib/onyxcord/webhooks/view/select_menu_builder.rb +77 -0
  205. data/lib/onyxcord/webhooks/view/separator_builder.rb +22 -0
  206. data/lib/onyxcord/webhooks/view/text_display_builder.rb +20 -0
  207. data/lib/onyxcord/webhooks/view.rb +9 -425
  208. data/lib/onyxcord.rb +106 -42
  209. metadata +207 -92
  210. data/.github/CONTRIBUTING.md +0 -13
  211. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  212. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
  213. data/.github/pull_request_template.md +0 -37
  214. data/.github/workflows/ci.yml +0 -78
  215. data/.github/workflows/codeql.yml +0 -65
  216. data/.github/workflows/deploy.yml +0 -54
  217. data/.github/workflows/release.yml +0 -51
  218. data/.gitignore +0 -17
  219. data/.markdownlint.json +0 -4
  220. data/.overcommit.yml +0 -7
  221. data/.rspec +0 -2
  222. data/.rubocop.yml +0 -129
  223. data/.yardopts +0 -1
  224. data/CHANGELOG.md +0 -257
  225. data/Gemfile +0 -6
  226. data/LICENSE.txt +0 -21
  227. data/README.md +0 -448
  228. data/Rakefile +0 -13
  229. data/lib/onyxcord/api/channel.rb +0 -703
  230. data/lib/onyxcord/api/server.rb +0 -775
  231. data/lib/onyxcord/async/runtime.rb +0 -30
  232. data/lib/onyxcord/commands/command_bot.rb +0 -505
  233. data/lib/onyxcord/data/interaction.rb +0 -1151
  234. data/lib/onyxcord/data.rb +0 -57
  235. data/lib/onyxcord/event_executor.rb +0 -153
  236. data/lib/onyxcord/gateway.rb +0 -505
  237. data/lib/onyxcord/http.rb +0 -186
  238. data/lib/onyxcord/json.rb +0 -49
  239. data/lib/onyxcord/rate_limiter/async_rest.rb +0 -150
  240. data/lib/onyxcord/rate_limiter/gateway.rb +0 -42
  241. data/lib/onyxcord/rate_limiter/rest.rb +0 -142
  242. data/lib/onyxcord/version.rb +0 -7
  243. data/lib/onyxcord/websocket.rb +0 -77
  244. data/onyxcord.gemspec +0 -63
  245. /data/lib/onyxcord/{configuration.rb → core/configuration.rb} +0 -0
  246. /data/lib/onyxcord/{errors.rb → core/errors.rb} +0 -0
  247. /data/lib/onyxcord/{upload.rb → internal/upload.rb} +0 -0
  248. /data/lib/onyxcord/{data → models}/attachment.rb +0 -0
  249. /data/lib/onyxcord/{data → models}/audit_logs.rb +0 -0
  250. /data/lib/onyxcord/{data → models}/call.rb +0 -0
  251. /data/lib/onyxcord/{data → models}/channel_tag.rb +0 -0
  252. /data/lib/onyxcord/{data → models}/component.rb +0 -0
  253. /data/lib/onyxcord/{data → models}/embed.rb +0 -0
  254. /data/lib/onyxcord/{data → models}/install_params.rb +0 -0
  255. /data/lib/onyxcord/{data → models}/integration.rb +0 -0
  256. /data/lib/onyxcord/{data → models}/message_activity.rb +0 -0
  257. /data/lib/onyxcord/{data → models}/overwrite.rb +0 -0
  258. /data/lib/onyxcord/{data → models}/reaction.rb +0 -0
  259. /data/lib/onyxcord/{data → models}/recipient.rb +0 -0
  260. /data/lib/onyxcord/{data → models}/role_connection_data.rb +0 -0
  261. /data/lib/onyxcord/{data → models}/role_subscription.rb +0 -0
  262. /data/lib/onyxcord/{data → models}/snapshot.rb +0 -0
  263. /data/lib/onyxcord/{data → models}/timestamp.rb +0 -0
  264. /data/lib/onyxcord/{data → models}/voice_region.rb +0 -0
  265. /data/lib/onyxcord/{data → models}/voice_state.rb +0 -0
  266. /data/lib/onyxcord/{colour_rgb.rb → utils/colour_rgb.rb} +0 -0
  267. /data/lib/onyxcord/{id_object.rb → utils/id_object.rb} +0 -0
  268. /data/lib/onyxcord/{message_components.rb → utils/message_components.rb} +0 -0
  269. /data/lib/onyxcord/{paginator.rb → utils/paginator.rb} +0 -0
  270. /data/lib/onyxcord/{permissions.rb → utils/permissions.rb} +0 -0
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'async'
4
-
5
- module OnyxCord
6
- module AsyncRuntime
7
- module_function
8
-
9
- def run(&block)
10
- current = Async::Task.current?
11
- return yield current if current
12
-
13
- Sync(&block)
14
- end
15
-
16
- def async(&block)
17
- current = Async::Task.current?
18
- return current.async(&block) if current
19
-
20
- Async(&block)
21
- end
22
-
23
- def sleep(duration)
24
- task = Async::Task.current?
25
- return task.sleep(duration) if task.respond_to?(:sleep)
26
-
27
- Kernel.sleep(duration)
28
- end
29
- end
30
- end
@@ -1,505 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'onyxcord/bot'
4
- require 'onyxcord/data'
5
- require 'onyxcord/commands/parser'
6
- require 'onyxcord/commands/events'
7
- require 'onyxcord/commands/container'
8
- require 'onyxcord/commands/rate_limiter'
9
- require 'time'
10
-
11
- # Specialized bot to run commands
12
-
13
- module OnyxCord::Commands
14
- # Bot that supports commands and command chains
15
- class CommandBot < OnyxCord::Bot
16
- # @return [Hash] this bot's attributes.
17
- attr_reader :attributes
18
-
19
- # @return [String, Array<String>, #call] the prefix commands are triggered with.
20
- # @see #initialize
21
- attr_reader :prefix
22
-
23
- include CommandContainer
24
-
25
- # Creates a new CommandBot and logs in to Discord.
26
- # @param attributes [Hash] The attributes to initialize the CommandBot with.
27
- # @see OnyxCord::Bot#initialize OnyxCord::Bot#initialize for other attributes that should be used to create the underlying regular bot.
28
- # @option attributes [String, Array<String>, #call] :prefix The prefix that should trigger this bot's commands. It
29
- # can be:
30
- #
31
- # * Any string (including the empty string). This has the effect that if a message starts with the prefix, the
32
- # prefix will be stripped and the rest of the chain will be parsed as a command chain. Note that it will be
33
- # literal - if the prefix is "hi" then the corresponding trigger string for a command called "test" would be
34
- # "hitest". Don't forget to put spaces in if you need them!
35
- # * An array of prefixes. Those will behave similarly to setting one string as a prefix, but instead of only one
36
- # string, any of the strings in the array can be used.
37
- # * Something Proc-like (responds to :call) that takes a {Message} object as an argument and returns either
38
- # the command chain in raw form or `nil` if the given message shouldn't be parsed. This can be used to make more
39
- # complicated dynamic prefixes (e. g. based on server), or even something else entirely (suffixes, or most
40
- # adventurous, infixes).
41
- # @option attributes [true, false] :advanced_functionality Whether to enable advanced functionality (very powerful
42
- # way to nest commands into chains, see https://github.com/kruldevb/OnyxCord/wiki/Commands#command-chain-syntax
43
- # for info. Default is false.
44
- # @option attributes [Symbol, Array<Symbol>, false] :help_command The name of the command that displays info for
45
- # other commands. Use an array if you want to have aliases. Default is "help". If none should be created, use
46
- # `false` as the value.
47
- # @option attributes [String, #call] :command_doesnt_exist_message The message that should be displayed if a user attempts
48
- # to use a command that does not exist. If none is specified, no message will be displayed. In the message, you
49
- # can use the string '%command%' that will be replaced with the name of the command. Anything responding to call
50
- # such as a proc will be called with the event, and is expected to return a String or nil.
51
- # @option attributes [String] :no_permission_message The message to be displayed when `NoPermission` error is raised.
52
- # @option attributes [true, false] :spaces_allowed Whether spaces are allowed to occur between the prefix and the
53
- # command. Default is false.
54
- # @option attributes [true, false] :webhook_commands Whether messages sent by webhooks are allowed to trigger
55
- # commands. Default is true.
56
- # @option attributes [Array<String, Integer, Channel>] :channels The channels this command bot accepts commands on.
57
- # Superseded if a command has a 'channels' attribute.
58
- # @option attributes [String] :previous Character that should designate the result of the previous command in
59
- # a command chain (see :advanced_functionality). Default is '~'. Set to an empty string to disable.
60
- # @option attributes [String] :chain_delimiter Character that should designate that a new command begins in the
61
- # command chain (see :advanced_functionality). Default is '>'. Set to an empty string to disable.
62
- # @option attributes [String] :chain_args_delim Character that should separate the command chain arguments from the
63
- # chain itself (see :advanced_functionality). Default is ':'. Set to an empty string to disable.
64
- # @option attributes [String] :sub_chain_start Character that should start a sub-chain (see
65
- # :advanced_functionality). Default is '['. Set to an empty string to disable.
66
- # @option attributes [String] :sub_chain_end Character that should end a sub-chain (see
67
- # :advanced_functionality). Default is ']'. Set to an empty string to disable.
68
- # @option attributes [String] :quote_start Character that should start a quoted string (see
69
- # :advanced_functionality). Default is '"'. Set to an empty string to disable.
70
- # @option attributes [String] :quote_end Character that should end a quoted string (see
71
- # :advanced_functionality). Default is '"' or the same as :quote_start. Set to an empty string to disable.
72
- # @option attributes [true, false] :ignore_bots Whether the bot should ignore bot accounts or not. Default is false.
73
- def initialize(**attributes)
74
- # TODO: This needs to be revisited. undefined attributes are treated
75
- # as explicitly passed nils.
76
- super(
77
- log_mode: attributes[:log_mode],
78
- token: attributes[:token],
79
- client_id: attributes[:client_id],
80
- type: attributes[:type],
81
- name: attributes[:name],
82
- fancy_log: attributes[:fancy_log],
83
- suppress_ready: attributes[:suppress_ready],
84
- parse_self: attributes[:parse_self],
85
- shard_id: attributes[:shard_id],
86
- num_shards: attributes[:num_shards],
87
- redact_token: attributes.key?(:redact_token) ? attributes[:redact_token] : true,
88
- ignore_bots: attributes[:ignore_bots],
89
- compress_mode: attributes[:compress_mode],
90
- intents: attributes[:intents] || :all
91
- )
92
-
93
- @prefix = attributes[:prefix]
94
- @attributes = {
95
- # Whether advanced functionality such as command chains are enabled
96
- advanced_functionality: attributes[:advanced_functionality].nil? ? false : attributes[:advanced_functionality],
97
-
98
- # The name of the help command (that displays information to other commands). False if none should exist
99
- help_command: attributes[:help_command].is_a?(FalseClass) ? nil : (attributes[:help_command] || :help),
100
-
101
- # The message to display for when a command doesn't exist, %command% to get the command name in question and nil for no message
102
- # No default value here because it may not be desired behaviour
103
- command_doesnt_exist_message: attributes[:command_doesnt_exist_message],
104
-
105
- # The message to be displayed when `NoPermission` error is raised.
106
- no_permission_message: attributes[:no_permission_message],
107
-
108
- # Spaces allowed between prefix and command
109
- spaces_allowed: attributes[:spaces_allowed].nil? ? false : attributes[:spaces_allowed],
110
-
111
- # Webhooks allowed to trigger commands
112
- webhook_commands: attributes[:webhook_commands].nil? || attributes[:webhook_commands],
113
-
114
- channels: attributes[:channels] || [],
115
-
116
- # All of the following need to be one character
117
- # String to designate previous result in command chain
118
- previous: attributes[:previous] || '~',
119
-
120
- # Command chain delimiter
121
- chain_delimiter: attributes[:chain_delimiter] || '>',
122
-
123
- # Chain argument delimiter
124
- chain_args_delim: attributes[:chain_args_delim] || ':',
125
-
126
- # Sub-chain starting character
127
- sub_chain_start: attributes[:sub_chain_start] || '[',
128
-
129
- # Sub-chain ending character
130
- sub_chain_end: attributes[:sub_chain_end] || ']',
131
-
132
- # Quoted mode starting character
133
- quote_start: attributes[:quote_start] || '"',
134
-
135
- # Quoted mode ending character
136
- quote_end: attributes[:quote_end] || attributes[:quote_start] || '"',
137
-
138
- # Default block for handling internal exceptions, or a string to respond with
139
- rescue: attributes[:rescue]
140
- }
141
-
142
- @permissions = {
143
- roles: {},
144
- users: {}
145
- }
146
-
147
- return unless @attributes[:help_command]
148
-
149
- command(@attributes[:help_command], max_args: 1, description: 'Shows a list of all the commands available or displays help for a specific command.', usage: 'help [command name]') do |event, command_name|
150
- if command_name
151
- command = @commands[command_name.to_sym]
152
- if command.is_a?(CommandAlias)
153
- command = command.aliased_command
154
- command_name = command.name
155
- end
156
- # rubocop:disable Lint/ReturnInVoidContext
157
- return "The command `#{command_name}` does not exist!" unless command
158
- # rubocop:enable Lint/ReturnInVoidContext
159
-
160
- desc = command.attributes[:description] || '*No description available*'
161
- usage = command.attributes[:usage]
162
- parameters = command.attributes[:parameters]
163
- result = "**`#{command_name}`**: #{desc}"
164
- aliases = command_aliases(command_name.to_sym)
165
- unless aliases.empty?
166
- result += "\nAliases: "
167
- result += aliases.map { |a| "`#{a.name}`" }.join(', ')
168
- end
169
- result += "\nUsage: `#{usage}`" if usage
170
- if parameters
171
- result += "\nAccepted Parameters:\n```"
172
- parameters.each { |p| result += "\n#{p}" }
173
- result += '```'
174
- end
175
- result
176
- else
177
- available_commands = @commands.values.reject do |c|
178
- c.is_a?(CommandAlias) || !c.attributes[:help_available] || !required_roles?(event.user, c.attributes[:required_roles]) || !allowed_roles?(event.user, c.attributes[:allowed_roles]) || !required_permissions?(event.user, c.attributes[:required_permissions], event.channel)
179
- end
180
- case available_commands.length
181
- when 0..5
182
- available_commands.reduce "**List of commands:**\n" do |memo, c|
183
- memo + "**`#{c.name}`**: #{c.attributes[:description] || '*No description available*'}\n"
184
- end
185
- when 5..50
186
- (available_commands.reduce "**List of commands:**\n" do |memo, c|
187
- memo + "`#{c.name}`, "
188
- end)[0..-3]
189
- else
190
- event.user.pm(available_commands.reduce("**List of commands:**\n") { |m, e| m + "`#{e.name}`, " }[0..-3])
191
- event.channel.pm? ? '' : 'Sending list in PM!'
192
- end
193
- end
194
- end
195
- end
196
-
197
- # Returns all aliases for the command with the given name
198
- # @param name [Symbol] the name of the `Command`
199
- # @return [Array<CommandAlias>]
200
- def command_aliases(name)
201
- commands.values.select do |command|
202
- command.is_a?(CommandAlias) && command.aliased_command.name == name
203
- end
204
- end
205
-
206
- # Executes a particular command on the bot. Mostly useful for internal stuff, but one can never know.
207
- # @param name [Symbol] The command to execute.
208
- # @param event [CommandEvent] The event to pass to the command.
209
- # @param arguments [Array<String>] The arguments to pass to the command.
210
- # @param chained [true, false] Whether or not it should be executed as part of a command chain. If this is false,
211
- # commands that have chain_usable set to false will not work.
212
- # @param check_permissions [true, false] Whether permission parameters such as `required_permission` or
213
- # `permission_level` should be checked.
214
- # @return [String, nil] the command's result, if there is any.
215
- def execute_command(name, event, arguments, chained = false, check_permissions = true)
216
- debug("Executing command #{name} with arguments #{arguments}")
217
- return unless @commands
218
-
219
- command = @commands[name]
220
- command = command.aliased_command if command.is_a?(CommandAlias)
221
- return unless !check_permissions || channels?(event.channel, @attributes[:channels]) ||
222
- (command && !command.attributes[:channels].nil?)
223
-
224
- unless command
225
- if @attributes[:command_doesnt_exist_message]
226
- message = @attributes[:command_doesnt_exist_message]
227
- message = message.call(event) if message.respond_to?(:call)
228
- event.respond message.gsub('%command%', name.to_s) if message
229
- end
230
- return
231
- end
232
- return unless !check_permissions || channels?(event.channel, command.attributes[:channels])
233
-
234
- arguments = arg_check(arguments, command.attributes[:arg_types], event.server) if check_permissions
235
- if (check_permissions &&
236
- permission?(event.author, command.attributes[:permission_level], event.server) &&
237
- required_permissions?(event.author, command.attributes[:required_permissions], event.channel) &&
238
- required_roles?(event.author, command.attributes[:required_roles]) &&
239
- allowed_roles?(event.author, command.attributes[:allowed_roles])) ||
240
- !check_permissions
241
- event.command = command
242
- result = command.call(event, arguments, chained, check_permissions)
243
- stringify(result)
244
- else
245
- event.respond command.attributes[:permission_message].gsub('%name%', name.to_s) if command.attributes[:permission_message]
246
- nil
247
- end
248
- rescue OnyxCord::Errors::NoPermission
249
- event.respond @attributes[:no_permission_message] unless @attributes[:no_permission_message].nil?
250
- raise
251
- end
252
-
253
- # Transforms an array of string arguments based on types array.
254
- # For example, `['1', '10..14']` with types `[Integer, Range]` would turn into `[1, 10..14]`.
255
- def arg_check(args, types = nil, server = nil)
256
- return args unless types
257
-
258
- args.each_with_index.map do |arg, i|
259
- next arg if types[i].nil? || types[i] == String
260
-
261
- if types[i] == Integer
262
- Integer(arg, 10, exception: false)
263
- elsif types[i] == Float
264
- Float(arg, exception: false)
265
- elsif types[i] == Time
266
- begin
267
- Time.parse arg
268
- rescue ArgumentError
269
- nil
270
- end
271
- elsif [TrueClass, FalseClass].include?(types[i])
272
- if arg.casecmp('true').zero? || arg.downcase.start_with?('y')
273
- true
274
- elsif arg.casecmp('false').zero? || arg.downcase.start_with?('n')
275
- false
276
- end
277
- elsif types[i] == Symbol
278
- arg.to_sym
279
- elsif types[i] == Encoding
280
- begin
281
- Encoding.find arg
282
- rescue ArgumentError
283
- nil
284
- end
285
- elsif types[i] == Regexp
286
- begin
287
- Regexp.new arg
288
- rescue ArgumentError
289
- nil
290
- end
291
- elsif types[i] == Rational
292
- Rational(arg, exception: false)
293
- elsif types[i] == Range
294
- begin
295
- if arg.include? '...'
296
- Range.new(*arg.split('...').map(&:to_i), true)
297
- elsif arg.include? '..'
298
- Range.new(*arg.split('..').map(&:to_i))
299
- end
300
- rescue ArgumentError
301
- nil
302
- end
303
- elsif types[i] == NilClass
304
- nil
305
- elsif [OnyxCord::User, OnyxCord::Role, OnyxCord::Emoji].include? types[i]
306
- result = parse_mention arg, server
307
- result if result.instance_of? types[i]
308
- elsif types[i] == OnyxCord::Invite
309
- resolve_invite_code arg
310
- elsif types[i].respond_to?(:from_argument)
311
- begin
312
- types[i].from_argument arg
313
- rescue StandardError
314
- nil
315
- end
316
- else
317
- raise ArgumentError, "#{types[i]} doesn't implement from_argument"
318
- end
319
- end
320
- end
321
-
322
- # Executes a command in a simple manner, without command chains or permissions.
323
- # @param chain [String] The command with its arguments separated by spaces.
324
- # @param event [CommandEvent] The event to pass to the command.
325
- # @return [String, nil] the command's result, if there is any.
326
- def simple_execute(chain, event)
327
- return nil if chain.empty?
328
-
329
- args = chain.split(' ')
330
- execute_command(args[0].to_sym, event, args[1..])
331
- end
332
-
333
- # Sets the permission level of a user
334
- # @param id [Integer] the ID of the user whose level to set
335
- # @param level [Integer] the level to set the permission to
336
- def set_user_permission(id, level)
337
- @permissions[:users][id] = level
338
- end
339
-
340
- # Sets the permission level of a role - this applies to all users in the role
341
- # @param id [Integer] the ID of the role whose level to set
342
- # @param level [Integer] the level to set the permission to
343
- def set_role_permission(id, level)
344
- @permissions[:roles][id] = level
345
- end
346
-
347
- # Check if a user has permission to do something
348
- # @param user [User] The user to check
349
- # @param level [Integer] The minimum permission level the user should have (inclusive)
350
- # @param server [Server] The server on which to check
351
- # @return [true, false] whether or not the user has the given permission
352
- def permission?(user, level, server)
353
- determined_level = if user.webhook? || server.nil?
354
- 0
355
- else
356
- user.roles.reduce(0) do |memo, role|
357
- [@permissions[:roles][role.id] || 0, memo].max
358
- end
359
- end
360
-
361
- [@permissions[:users][user.id] || 0, determined_level].max >= level
362
- end
363
-
364
- # @see CommandBot#update_channels
365
- def channels=(channels)
366
- update_channels(channels)
367
- end
368
-
369
- # Update the list of channels the bot accepts commands from.
370
- # @param channels [Array<String, Integer, Channel>] The channels this command bot accepts commands on.
371
- def update_channels(channels = [])
372
- @attributes[:channels] = Array(channels)
373
- end
374
-
375
- # Add a channel to the list of channels the bot accepts commands from.
376
- # @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be added
377
- def add_channel(channel)
378
- return if @attributes[:channels].find { |c| channel.resolve_id == c.resolve_id }
379
-
380
- @attributes[:channels] << channel
381
- end
382
-
383
- # Remove a channel from the list of channels the bot accepts commands from.
384
- # @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be removed
385
- def remove_channel(channel)
386
- @attributes[:channels].delete_if { |c| channel.resolve_id == c.resolve_id }
387
- end
388
-
389
- private
390
-
391
- # Internal handler for MESSAGE_CREATE that is overwritten to allow for command handling
392
- def create_message(data)
393
- message = OnyxCord::Message.new(data, self)
394
- return message if message.from_bot? && !@should_parse_self
395
- return message if message.webhook? && !@attributes[:webhook_commands]
396
-
397
- unless message.author
398
- OnyxCord::LOGGER.warn("Received a message (#{message.inspect}) with nil author! Ignoring, please report this if you can")
399
- return
400
- end
401
-
402
- event = CommandEvent.new(message, self)
403
-
404
- chain = trigger?(message)
405
- return message unless chain
406
-
407
- # Don't allow spaces between the prefix and the command
408
- if chain.start_with?(' ') && !@attributes[:spaces_allowed]
409
- debug('Chain starts with a space')
410
- return message
411
- end
412
-
413
- if chain.strip.empty?
414
- debug('Chain is empty')
415
- return message
416
- end
417
-
418
- execute_chain(chain, event)
419
-
420
- # Return the message so it doesn't get parsed again during the rest of the dispatch handling
421
- message
422
- end
423
-
424
- # Check whether a message should trigger command execution, and if it does, return the raw chain
425
- def trigger?(message)
426
- if @prefix.is_a? String
427
- standard_prefix_trigger(message.content, @prefix)
428
- elsif @prefix.is_a? Array
429
- @prefix.map { |e| standard_prefix_trigger(message.content, e) }.reduce { |m, e| m || e }
430
- elsif @prefix.respond_to? :call
431
- @prefix.call(message)
432
- end
433
- end
434
-
435
- def standard_prefix_trigger(message, prefix)
436
- return nil unless message.start_with? prefix
437
-
438
- message[prefix.length..]
439
- end
440
-
441
- def required_permissions?(member, required, channel = nil)
442
- required.reduce(true) do |a, action|
443
- a && !member.webhook? && !member.is_a?(OnyxCord::Recipient) && member.permission?(action, channel)
444
- end
445
- end
446
-
447
- def required_roles?(member, required)
448
- return true if member.webhook? || member.is_a?(OnyxCord::Recipient) || required.nil? || required.empty?
449
-
450
- required.is_a?(Array) ? check_multiple_roles(member, required) : member.role?(role)
451
- end
452
-
453
- def allowed_roles?(member, required)
454
- return true if member.webhook? || member.is_a?(OnyxCord::Recipient) || required.nil? || required.empty?
455
-
456
- required.is_a?(Array) ? check_multiple_roles(member, required, false) : member.role?(role)
457
- end
458
-
459
- def check_multiple_roles(member, required, all_roles = true)
460
- if all_roles
461
- required.all? do |role|
462
- member.role?(role)
463
- end
464
- else
465
- required.any? do |role|
466
- member.role?(role)
467
- end
468
- end
469
- end
470
-
471
- def channels?(channel, channels)
472
- return true if channels.nil? || channels.empty?
473
-
474
- channels.any? do |c|
475
- # if c is string, make sure to remove the "#" from channel names in case it was specified
476
- return true if c.is_a?(String) && c.delete('#') == channel.name
477
-
478
- c.resolve_id == channel.resolve_id
479
- end
480
- end
481
-
482
- def execute_chain(chain, event)
483
- Async do
484
- debug("Parsing command chain #{chain}")
485
- result = @attributes[:advanced_functionality] ? CommandChain.new(chain, self).execute(event) : simple_execute(chain, event)
486
- result = event.drain_into(result)
487
-
488
- if event.file
489
- event.send_file(event.file, caption: result)
490
- else
491
- event.respond result unless result.nil? || result.empty?
492
- end
493
- rescue StandardError => e
494
- log_exception(e)
495
- end
496
- end
497
-
498
- # Turns the object into a string, using to_s by default
499
- def stringify(object)
500
- return '' if object.is_a? OnyxCord::Message
501
-
502
- object.to_s
503
- end
504
- end
505
- end