onyxcord 2.0.13 → 3.2.0

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/{errors.rb → core/errors.rb} +18 -3
  27. data/lib/onyxcord/{logger.rb → core/logger.rb} +5 -0
  28. data/lib/onyxcord/core/version.rb +5 -0
  29. data/lib/onyxcord/events/await/base.rb +48 -0
  30. data/lib/onyxcord/events/await.rb +1 -46
  31. data/lib/onyxcord/events/ban/base.rb +54 -0
  32. data/lib/onyxcord/events/ban/remove.rb +11 -0
  33. data/lib/onyxcord/events/bans.rb +2 -58
  34. data/lib/onyxcord/events/channels/base.rb +91 -0
  35. data/lib/onyxcord/events/channels/delete.rb +73 -0
  36. data/lib/onyxcord/events/channels/pins.rb +44 -0
  37. data/lib/onyxcord/events/channels/recipients.rb +77 -0
  38. data/lib/onyxcord/events/channels.rb +4 -239
  39. data/lib/onyxcord/events/guilds/audit_logs.rb +79 -0
  40. data/lib/onyxcord/events/guilds/base.rb +97 -0
  41. data/lib/onyxcord/events/guilds/emoji.rb +147 -0
  42. data/lib/onyxcord/events/guilds.rb +3 -267
  43. data/lib/onyxcord/events/handlers/channels.rb +133 -0
  44. data/lib/onyxcord/events/handlers/core.rb +146 -0
  45. data/lib/onyxcord/events/handlers/guilds.rb +375 -0
  46. data/lib/onyxcord/events/handlers/interactions.rb +238 -0
  47. data/lib/onyxcord/events/handlers/lifetime.rb +71 -0
  48. data/lib/onyxcord/events/handlers/messages.rb +218 -0
  49. data/lib/onyxcord/events/handlers/presence.rb +56 -0
  50. data/lib/onyxcord/events/handlers/raw.rb +39 -0
  51. data/lib/onyxcord/events/handlers/reactions.rb +98 -0
  52. data/lib/onyxcord/events/handlers/scheduled_events.rb +243 -0
  53. data/lib/onyxcord/events/handlers/voice.rb +46 -0
  54. data/lib/onyxcord/events/integration/base.rb +45 -0
  55. data/lib/onyxcord/events/integration/create.rb +11 -0
  56. data/lib/onyxcord/events/integration/delete.rb +48 -0
  57. data/lib/onyxcord/events/integration/update.rb +11 -0
  58. data/lib/onyxcord/events/integrations.rb +4 -98
  59. data/lib/onyxcord/events/interactions/application_commands.rb +185 -0
  60. data/lib/onyxcord/events/interactions/autocomplete.rb +62 -0
  61. data/lib/onyxcord/events/interactions/base.rb +175 -0
  62. data/lib/onyxcord/events/interactions/components.rb +221 -0
  63. data/lib/onyxcord/events/interactions/permissions.rb +53 -0
  64. data/lib/onyxcord/events/interactions.rb +5 -622
  65. data/lib/onyxcord/events/invite/create.rb +79 -0
  66. data/lib/onyxcord/events/invite/delete.rb +55 -0
  67. data/lib/onyxcord/events/invites.rb +2 -125
  68. data/lib/onyxcord/events/lifetime/base.rb +31 -0
  69. data/lib/onyxcord/events/lifetime.rb +1 -29
  70. data/lib/onyxcord/events/member/add.rb +12 -0
  71. data/lib/onyxcord/events/member/base.rb +67 -0
  72. data/lib/onyxcord/events/member/delete.rb +21 -0
  73. data/lib/onyxcord/events/member/update.rb +25 -0
  74. data/lib/onyxcord/events/members.rb +4 -108
  75. data/lib/onyxcord/events/message/base.rb +295 -0
  76. data/lib/onyxcord/events/message/ids.rb +71 -0
  77. data/lib/onyxcord/events/message/mentions.rb +55 -0
  78. data/lib/onyxcord/events/message/updates.rb +27 -0
  79. data/lib/onyxcord/events/message.rb +4 -397
  80. data/lib/onyxcord/events/poll/add.rb +11 -0
  81. data/lib/onyxcord/events/poll/base.rb +106 -0
  82. data/lib/onyxcord/events/poll/remove.rb +11 -0
  83. data/lib/onyxcord/events/polls.rb +3 -116
  84. data/lib/onyxcord/events/presence/playing.rb +77 -0
  85. data/lib/onyxcord/events/presence/status.rb +60 -0
  86. data/lib/onyxcord/events/presence.rb +2 -129
  87. data/lib/onyxcord/events/raw/base.rb +74 -0
  88. data/lib/onyxcord/events/raw.rb +1 -72
  89. data/lib/onyxcord/events/reactions/base.rb +143 -0
  90. data/lib/onyxcord/events/reactions/clear.rb +99 -0
  91. data/lib/onyxcord/events/reactions.rb +2 -216
  92. data/lib/onyxcord/events/role/create.rb +46 -0
  93. data/lib/onyxcord/events/role/delete.rb +40 -0
  94. data/lib/onyxcord/events/role/update.rb +11 -0
  95. data/lib/onyxcord/events/roles.rb +3 -85
  96. data/lib/onyxcord/events/scheduled_events/events.rb +112 -0
  97. data/lib/onyxcord/events/scheduled_events/users.rb +91 -0
  98. data/lib/onyxcord/events/scheduled_events.rb +2 -169
  99. data/lib/onyxcord/events/thread/base.rb +49 -0
  100. data/lib/onyxcord/events/thread/members.rb +44 -0
  101. data/lib/onyxcord/events/thread/update.rb +18 -0
  102. data/lib/onyxcord/events/threads.rb +3 -98
  103. data/lib/onyxcord/events/typing/base.rb +73 -0
  104. data/lib/onyxcord/events/typing.rb +1 -71
  105. data/lib/onyxcord/events/voice/server_update.rb +48 -0
  106. data/lib/onyxcord/events/voice/state_update.rb +137 -0
  107. data/lib/onyxcord/events/voice_server_update.rb +2 -46
  108. data/lib/onyxcord/events/voice_state_update.rb +2 -135
  109. data/lib/onyxcord/events/webhook/update.rb +65 -0
  110. data/lib/onyxcord/events/webhooks.rb +1 -63
  111. data/lib/onyxcord/gateway/client.rb +460 -0
  112. data/lib/onyxcord/interactions/command.rb +120 -0
  113. data/lib/onyxcord/interactions/context.rb +109 -0
  114. data/lib/onyxcord/interactions/internal/application_command.rb +203 -0
  115. data/lib/onyxcord/interactions/internal/message.rb +152 -0
  116. data/lib/onyxcord/interactions/internal/metadata.rb +109 -0
  117. data/lib/onyxcord/interactions/internal/option_builder.rb +230 -0
  118. data/lib/onyxcord/interactions/internal/permission_builder.rb +90 -0
  119. data/lib/onyxcord/interactions/option.rb +88 -0
  120. data/lib/onyxcord/interactions/registry.rb +50 -0
  121. data/lib/onyxcord/interactions.rb +16 -0
  122. data/lib/onyxcord/internal/async_runtime.rb +32 -0
  123. data/lib/onyxcord/internal/event_bus.rb +591 -0
  124. data/lib/onyxcord/internal/event_executor.rb +155 -0
  125. data/lib/onyxcord/internal/gateway/opcodes.rb +22 -0
  126. data/lib/onyxcord/internal/gateway/session.rb +45 -0
  127. data/lib/onyxcord/internal/http.rb +189 -0
  128. data/lib/onyxcord/internal/json.rb +51 -0
  129. data/lib/onyxcord/internal/message_payload.rb +78 -0
  130. data/lib/onyxcord/internal/rate_limiter/async_rest.rb +151 -0
  131. data/lib/onyxcord/internal/rate_limiter/gateway.rb +44 -0
  132. data/lib/onyxcord/internal/rate_limiter/rest.rb +144 -0
  133. data/lib/onyxcord/internal/upload.rb +39 -0
  134. data/lib/onyxcord/internal/websocket.rb +79 -0
  135. data/lib/onyxcord/light/data.rb +1 -1
  136. data/lib/onyxcord/light/integrations.rb +1 -1
  137. data/lib/onyxcord/light/light_bot.rb +7 -7
  138. data/lib/onyxcord/{data → models}/activity.rb +2 -2
  139. data/lib/onyxcord/{data → models}/application.rb +3 -3
  140. data/lib/onyxcord/{data → models}/avatar_decoration.rb +1 -1
  141. data/lib/onyxcord/{data → models}/channel.rb +23 -23
  142. data/lib/onyxcord/{data → models}/collectibles.rb +1 -1
  143. data/lib/onyxcord/{data → models}/emoji.rb +1 -1
  144. data/lib/onyxcord/models/interaction.rb +450 -0
  145. data/lib/onyxcord/{data → models}/invite.rb +1 -1
  146. data/lib/onyxcord/{data → models}/member.rb +11 -11
  147. data/lib/onyxcord/{data → models}/message.rb +22 -14
  148. data/lib/onyxcord/{data → models}/poll.rb +2 -2
  149. data/lib/onyxcord/{data → models}/primary_server.rb +1 -1
  150. data/lib/onyxcord/{data → models}/profile.rb +4 -4
  151. data/lib/onyxcord/{data → models}/role.rb +6 -12
  152. data/lib/onyxcord/{data → models}/scheduled_event.rb +5 -5
  153. data/lib/onyxcord/{data → models}/server.rb +41 -41
  154. data/lib/onyxcord/{data → models}/server_preview.rb +2 -2
  155. data/lib/onyxcord/{data → models}/team.rb +1 -1
  156. data/lib/onyxcord/{data → models}/user.rb +5 -5
  157. data/lib/onyxcord/{data → models}/webhook.rb +13 -11
  158. data/lib/onyxcord/models.rb +57 -0
  159. data/lib/onyxcord/{api.rb → rest/client.rb} +83 -27
  160. data/lib/onyxcord/{api → rest/routes}/application.rb +79 -57
  161. data/lib/onyxcord/rest/routes/channel/base.rb +73 -0
  162. data/lib/onyxcord/rest/routes/channel/groups.rb +78 -0
  163. data/lib/onyxcord/rest/routes/channel/messages.rb +130 -0
  164. data/lib/onyxcord/rest/routes/channel/permissions.rb +73 -0
  165. data/lib/onyxcord/rest/routes/channel/pins.rb +45 -0
  166. data/lib/onyxcord/rest/routes/channel/polls.rb +32 -0
  167. data/lib/onyxcord/rest/routes/channel/reactions.rb +86 -0
  168. data/lib/onyxcord/rest/routes/channel/threads.rb +176 -0
  169. data/lib/onyxcord/rest/routes/channel/webhooks.rb +47 -0
  170. data/lib/onyxcord/rest/routes/channel.rb +12 -0
  171. data/lib/onyxcord/{api/interaction.rb → rest/routes/interaction/base.rb} +10 -33
  172. data/lib/onyxcord/rest/routes/interaction/response.rb +24 -0
  173. data/lib/onyxcord/rest/routes/interaction.rb +4 -0
  174. data/lib/onyxcord/{api → rest/routes}/invite.rb +7 -7
  175. data/lib/onyxcord/rest/routes/server/admin.rb +174 -0
  176. data/lib/onyxcord/rest/routes/server/assets.rb +25 -0
  177. data/lib/onyxcord/rest/routes/server/bans.rb +67 -0
  178. data/lib/onyxcord/rest/routes/server/base.rb +77 -0
  179. data/lib/onyxcord/rest/routes/server/channels.rb +58 -0
  180. data/lib/onyxcord/rest/routes/server/emoji.rb +48 -0
  181. data/lib/onyxcord/rest/routes/server/members.rb +109 -0
  182. data/lib/onyxcord/rest/routes/server/messages.rb +19 -0
  183. data/lib/onyxcord/rest/routes/server/roles.rb +156 -0
  184. data/lib/onyxcord/rest/routes/server/scheduled_events.rb +86 -0
  185. data/lib/onyxcord/rest/routes/server.rb +12 -0
  186. data/lib/onyxcord/{api → rest/routes}/user.rb +23 -23
  187. data/lib/onyxcord/{api → rest/routes}/webhook.rb +30 -32
  188. data/lib/onyxcord/{allowed_mentions.rb → utils/allowed_mentions.rb} +9 -1
  189. data/lib/onyxcord/utils/timestamp.rb +69 -0
  190. data/lib/onyxcord/voice/{voice_bot.rb → client.rb} +4 -4
  191. data/lib/onyxcord/voice/encoder.rb +1 -1
  192. data/lib/onyxcord/voice/network/udp.rb +135 -0
  193. data/lib/onyxcord/voice/network/websocket.rb +225 -0
  194. data/lib/onyxcord/voice/network.rb +5 -357
  195. data/lib/onyxcord/webhooks/builder.rb +1 -1
  196. data/lib/onyxcord/webhooks/client.rb +13 -13
  197. data/lib/onyxcord/webhooks/modal/group_builder.rb +55 -0
  198. data/lib/onyxcord/webhooks/modal/label_builder.rb +183 -0
  199. data/lib/onyxcord/webhooks/modal.rb +2 -239
  200. data/lib/onyxcord/webhooks/version.rb +3 -3
  201. data/lib/onyxcord/webhooks/view/container_builder.rb +80 -0
  202. data/lib/onyxcord/webhooks/view/file_builder.rb +24 -0
  203. data/lib/onyxcord/webhooks/view/media_gallery_builder.rb +50 -0
  204. data/lib/onyxcord/webhooks/view/row_builder.rb +120 -0
  205. data/lib/onyxcord/webhooks/view/section_builder.rb +61 -0
  206. data/lib/onyxcord/webhooks/view/select_menu_builder.rb +77 -0
  207. data/lib/onyxcord/webhooks/view/separator_builder.rb +22 -0
  208. data/lib/onyxcord/webhooks/view/text_display_builder.rb +20 -0
  209. data/lib/onyxcord/webhooks/view.rb +9 -425
  210. data/lib/onyxcord.rb +106 -40
  211. metadata +208 -91
  212. data/.github/CONTRIBUTING.md +0 -13
  213. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  214. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
  215. data/.github/pull_request_template.md +0 -37
  216. data/.github/workflows/ci.yml +0 -78
  217. data/.github/workflows/codeql.yml +0 -65
  218. data/.github/workflows/deploy.yml +0 -54
  219. data/.github/workflows/release.yml +0 -51
  220. data/.gitignore +0 -17
  221. data/.markdownlint.json +0 -4
  222. data/.overcommit.yml +0 -7
  223. data/.rspec +0 -2
  224. data/.rubocop.yml +0 -129
  225. data/.yardopts +0 -1
  226. data/CHANGELOG.md +0 -202
  227. data/Gemfile +0 -6
  228. data/LICENSE.txt +0 -21
  229. data/README.md +0 -448
  230. data/Rakefile +0 -13
  231. data/lib/onyxcord/api/channel.rb +0 -705
  232. data/lib/onyxcord/api/server.rb +0 -775
  233. data/lib/onyxcord/async/runtime.rb +0 -30
  234. data/lib/onyxcord/commands/command_bot.rb +0 -505
  235. data/lib/onyxcord/data/interaction.rb +0 -1143
  236. data/lib/onyxcord/data.rb +0 -57
  237. data/lib/onyxcord/event_executor.rb +0 -153
  238. data/lib/onyxcord/gateway.rb +0 -500
  239. data/lib/onyxcord/http.rb +0 -115
  240. data/lib/onyxcord/json.rb +0 -49
  241. data/lib/onyxcord/rate_limiter/async_rest.rb +0 -149
  242. data/lib/onyxcord/rate_limiter/gateway.rb +0 -42
  243. data/lib/onyxcord/rate_limiter/rest.rb +0 -142
  244. data/lib/onyxcord/version.rb +0 -7
  245. data/lib/onyxcord/websocket.rb +0 -77
  246. data/onyxcord.gemspec +0 -63
  247. /data/lib/onyxcord/{configuration.rb → core/configuration.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,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'lru_redux'
4
- require 'onyxcord/api'
5
- require 'onyxcord/api/server'
6
- require 'onyxcord/api/invite'
7
- require 'onyxcord/api/user'
8
- require 'onyxcord/configuration'
9
- require 'onyxcord/data'
4
+ require 'onyxcord/rest/client'
5
+ require 'onyxcord/rest/routes/server'
6
+ require 'onyxcord/rest/routes/invite'
7
+ require 'onyxcord/rest/routes/user'
8
+ require 'onyxcord/core/configuration'
9
+ require 'onyxcord/models'
10
10
 
11
11
  module OnyxCord
12
12
  # This mixin module does caching stuff for the library. It conveniently separates the logic behind
@@ -73,7 +73,7 @@ module OnyxCord
73
73
  def fetch_voice_regions
74
74
  regions_by_id = {}
75
75
 
76
- regions = JSON.parse API.voice_regions(token)
76
+ regions = JSON.parse REST.voice_regions(token)
77
77
  regions.each do |data|
78
78
  regions_by_id[data['id']] = VoiceRegion.new(data)
79
79
  end
@@ -95,7 +95,7 @@ module OnyxCord
95
95
  return @channels[id] if @channels&.[](id)
96
96
 
97
97
  begin
98
- response = API::Channel.resolve(token, id)
98
+ response = REST::Channel.resolve(token, id)
99
99
  rescue OnyxCord::Errors::UnknownChannel
100
100
  return nil
101
101
  end
@@ -116,7 +116,7 @@ module OnyxCord
116
116
 
117
117
  LOGGER.out("Resolving user #{id}")
118
118
  begin
119
- response = API::User.resolve(token, id)
119
+ response = REST::User.resolve(token, id)
120
120
  rescue OnyxCord::Errors::UnknownUser
121
121
  return nil
122
122
  end
@@ -135,7 +135,7 @@ module OnyxCord
135
135
 
136
136
  LOGGER.out("Resolving server #{id}")
137
137
  begin
138
- response = API::Server.resolve(token, id)
138
+ response = REST::Server.resolve(token, id)
139
139
  rescue OnyxCord::Errors::NoPermission
140
140
  return nil
141
141
  end
@@ -157,7 +157,7 @@ module OnyxCord
157
157
 
158
158
  LOGGER.out("Resolving member #{server_id} on server #{user_id}")
159
159
  begin
160
- response = API::Server.resolve_member(token, server_id, user_id)
160
+ response = REST::Server.resolve_member(token, server_id, user_id)
161
161
  rescue OnyxCord::Errors::UnknownUser, OnyxCord::Errors::UnknownMember
162
162
  return nil
163
163
  end
@@ -175,7 +175,7 @@ module OnyxCord
175
175
  return @pm_channels[id] if @pm_channels&.[](id)
176
176
 
177
177
  debug("Creating pm channel with user id #{id}")
178
- response = API::User.create_pm(token, id)
178
+ response = REST::User.create_pm(token, id)
179
179
  channel = Channel.new(JSON.parse(response), self)
180
180
  @pm_channels[id] = channel if cache_enabled?(:pm_channels)
181
181
  channel
@@ -190,7 +190,7 @@ module OnyxCord
190
190
  id = id.resolve_id
191
191
  return @server_previews[id] if @server_previews&.[](id)
192
192
 
193
- response = JSON.parse(API::Server.preview(token, id))
193
+ response = JSON.parse(REST::Server.preview(token, id))
194
194
  preview = ServerPreview.new(response, self)
195
195
  @server_previews[id] = preview if cache_enabled?(:server_previews)
196
196
  preview
@@ -301,7 +301,7 @@ module OnyxCord
301
301
  # @return [Invite] The invite with information about the given invite URL.
302
302
  def invite(invite)
303
303
  code = resolve_invite_code(invite)
304
- Invite.new(JSON.parse(API::Invite.resolve(token, code)), self)
304
+ Invite.new(JSON.parse(REST::Invite.resolve(token, code)), self)
305
305
  end
306
306
 
307
307
  # Finds a channel given its name and optionally the name of the server it is in.
@@ -352,3 +352,5 @@ module OnyxCord
352
352
  end
353
353
  end
354
354
  end
355
+
356
+ require 'onyxcord/cache/stores/gateway'
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnyxCord
4
+ module Cache
5
+ module Stores
6
+ module Gateway
7
+ private
8
+
9
+ # Internal handler for PRESENCE_UPDATE
10
+ def update_presence(data)
11
+ # Friends list presences have no server ID so ignore these to not cause an error
12
+ return unless data['guild_id']
13
+
14
+ user_id = data['user']['id'].to_i
15
+ server_id = data['guild_id'].to_i
16
+ server = server(server_id)
17
+ return unless server
18
+
19
+ member_is_new = false
20
+
21
+ if server.member_cached?(user_id)
22
+ member = server.member(user_id)
23
+ else
24
+ # If the member is not cached yet, it means that it just came online from not being cached at all
25
+ # due to large_threshold. Fortunately, Discord sends the entire member object in this case, and
26
+ # not just a part of it - we can just cache this member directly
27
+ member = Member.new(data, server, self)
28
+ debug("Implicitly adding presence-obtained member #{user_id} to #{server_id} cache")
29
+
30
+ member_is_new = true
31
+ end
32
+
33
+ username = data['user']['username']
34
+ if username && !member_is_new # Don't set the username for newly-cached members
35
+ debug "Implicitly updating presence-obtained information username for member #{user_id}"
36
+ member.update_username(username)
37
+ end
38
+
39
+ global_name = data['user']['global_name']
40
+ if global_name && !member_is_new # Don't set the global_name for newly-cached members
41
+ debug "Implicitly updating presence-obtained information global_name for member #{user_id}"
42
+ member.update_global_name(global_name)
43
+ end
44
+
45
+ member.update_presence(data)
46
+
47
+ member.avatar_id = data['user']['avatar'] if data['user']['avatar']
48
+
49
+ server.cache_member(member)
50
+ end
51
+
52
+ # Internal handler for VOICE_STATE_UPDATE
53
+ def update_voice_state(data)
54
+ @session_id = data['session_id']
55
+
56
+ server_id = data['guild_id'].to_i
57
+ server = @servers&.[](server_id)
58
+ return unless server
59
+
60
+ user_id = data['user_id'].to_i
61
+ old_voice_state = server.voice_states[user_id]
62
+ old_channel_id = old_voice_state&.channel_id || old_voice_state&.voice_channel&.id
63
+
64
+ server.update_voice_state(data)
65
+
66
+ existing_voice = @voices[server_id]
67
+ if user_id == @profile.id && existing_voice
68
+ new_channel_id = data['channel_id']
69
+ if new_channel_id
70
+ channel_id = new_channel_id.to_i
71
+ new_channel = @channels&.[](channel_id) || server.channels.find { |channel| channel.id == channel_id }
72
+ existing_voice.channel = new_channel
73
+ else
74
+ voice_destroy(server_id)
75
+ end
76
+ end
77
+
78
+ old_channel_id
79
+ end
80
+
81
+ # Internal handler for VOICE_SERVER_UPDATE
82
+ def update_voice_server(data)
83
+ server_id = data['guild_id'].to_i
84
+ channel = @should_connect_to_voice[server_id]
85
+
86
+ debug("Voice server update received! chan: #{channel.inspect}")
87
+ return unless channel
88
+
89
+ @should_connect_to_voice.delete(server_id)
90
+ debug('Updating voice server!')
91
+
92
+ token = data['token']
93
+ endpoint = data['endpoint']
94
+
95
+ unless endpoint
96
+ debug('VOICE_SERVER_UPDATE sent with nil endpoint! Ignoring')
97
+ return
98
+ end
99
+
100
+ debug('Got data, now creating the bot.')
101
+ @voices[server_id] = OnyxCord::Voice::Client.new(channel, self, token, @session_id, endpoint)
102
+ end
103
+
104
+ # Internal handler for CHANNEL_CREATE
105
+ def create_channel(data)
106
+ channel = data.is_a?(OnyxCord::Channel) ? data : Channel.new(data, self)
107
+ server = channel.server
108
+
109
+ # The last message ID of a forum channel is the most recent post
110
+ channel.parent.process_last_message_id(channel.id) if channel.parent&.forum? || channel.parent&.media?
111
+
112
+ # Handle normal and private channels separately
113
+ if server
114
+ server.add_channel(channel)
115
+ @channels[channel.id] = channel if @channels
116
+ elsif channel.private?
117
+ @pm_channels[channel.recipient.id] = channel if @pm_channels
118
+ elsif channel.group?
119
+ @channels[channel.id] = channel if @channels
120
+ end
121
+ end
122
+
123
+ # Internal handler for CHANNEL_UPDATE
124
+ def update_channel(data)
125
+ @channels&.[](data['id'].to_i)&.update_data(data)
126
+ end
127
+
128
+ # Internal handler for CHANNEL_DELETE
129
+ def delete_channel(data)
130
+ channel = Channel.new(data, self)
131
+ server = channel.server
132
+
133
+ # Handle normal and private channels separately
134
+ if server
135
+ @channels&.delete(channel.id)
136
+ server.delete_channel(channel.id)
137
+ elsif channel.pm?
138
+ @pm_channels&.delete(channel.recipient.id)
139
+ elsif channel.group?
140
+ @channels&.delete(channel.id)
141
+ end
142
+
143
+ @thread_members&.delete(channel.id) if channel.thread?
144
+ end
145
+
146
+ # Internal handler for GUILD_MEMBER_ADD
147
+ def add_guild_member(data)
148
+ server_id = data['guild_id'].to_i
149
+ server = self.server(server_id)
150
+
151
+ member = Member.new(data, server, self)
152
+ server.add_member(member)
153
+ end
154
+
155
+ # Internal handler for GUILD_MEMBER_UPDATE
156
+ def update_guild_member(data)
157
+ server_id = data['guild_id'].to_i
158
+ server = self.server(server_id)
159
+
160
+ # Only attempt to update members that're already cached
161
+ if (member = server.member(data['user']['id'].to_i, false))
162
+ member.update_data(data)
163
+ else
164
+ ensure_user(data['user'])
165
+ end
166
+ end
167
+
168
+ # Internal handler for GUILD_MEMBER_DELETE
169
+ def delete_guild_member(data)
170
+ server_id = data['guild_id'].to_i
171
+ server = self.server(server_id)
172
+ return unless server
173
+
174
+ user_id = data['user']['id'].to_i
175
+ server.delete_member(user_id)
176
+ rescue OnyxCord::Errors::NoPermission
177
+ OnyxCord::LOGGER.warn("delete_guild_member attempted to access a server for which the bot doesn't have permission! Not sure what happened here, ignoring")
178
+ end
179
+
180
+ # Internal handler for GUILD_CREATE
181
+ def create_guild(data)
182
+ ensure_server(data, true)
183
+ end
184
+
185
+ # Internal handler for GUILD_UPDATE
186
+ def update_guild(data)
187
+ server_id = data['id'].to_i
188
+ server = @servers&.[](server_id)
189
+
190
+ if server
191
+ server.update_data(data)
192
+ else
193
+ LOGGER.warn("GUILD_UPDATE received for uncached server #{server_id}; caching from payload")
194
+ ensure_server(data, true)
195
+ end
196
+ end
197
+
198
+ # Internal handler for GUILD_DELETE
199
+ def delete_guild(data)
200
+ id = data['id'].to_i
201
+ @servers.delete(id)
202
+ end
203
+
204
+ # Internal handler for GUILD_ROLE_CREATE and GUILD_ROLE_UPDATE
205
+ def update_guild_role(data)
206
+ server = @servers[data['guild_id'].to_i]
207
+
208
+ if (role = server&.role(data['role']['id'].to_i))
209
+ role.update_data(data['role'])
210
+ else
211
+ server&.add_role(Role.new(data['role'], self, server))
212
+ end
213
+ end
214
+
215
+ # Internal handler for GUILD_ROLE_DELETE
216
+ def delete_guild_role(data)
217
+ role_id = data['role_id'].to_i
218
+ server_id = data['guild_id'].to_i
219
+ server = @servers[server_id]
220
+ server&.delete_role(role_id)
221
+ end
222
+
223
+ # Internal handler for GUILD_EMOJIS_UPDATE
224
+ def update_guild_emoji(data)
225
+ server_id = data['guild_id'].to_i
226
+ server = @servers[server_id]
227
+ server&.update_emoji_data(data)
228
+ end
229
+
230
+ # Internal handler for GUILD_SCHEDULED_EVENT_CREATE and GUILD_SCHEDULED_EVENT_UPDATE
231
+ def update_guild_scheduled_event(data)
232
+ server = @servers[data['guild_id'].to_i]
233
+
234
+ if (event = server&.scheduled_event(data['id'].to_i, request: false))
235
+ event&.update_data(data)
236
+ else
237
+ server&.cache_scheduled_event(ScheduledEvent.new(data, server, self))
238
+ end
239
+ end
240
+
241
+ # Internal handler for MESSAGE_CREATE
242
+ def create_message(data); end
243
+
244
+ # Internal handler for TYPING_START
245
+ def start_typing(data); end
246
+
247
+ # Internal handler for MESSAGE_UPDATE
248
+ def update_message(data); end
249
+
250
+ # Internal handler for MESSAGE_DELETE
251
+ def delete_message(data); end
252
+
253
+ # Internal handler for MESSAGE_REACTION_ADD
254
+ def add_message_reaction(data); end
255
+
256
+ # Internal handler for MESSAGE_REACTION_REMOVE
257
+ def remove_message_reaction(data); end
258
+
259
+ # Internal handler for MESSAGE_REACTION_REMOVE_ALL
260
+ def remove_all_message_reactions(data); end
261
+
262
+ # Internal handler for GUILD_BAN_ADD
263
+ def add_user_ban(data); end
264
+
265
+ # Internal handler for GUILD_BAN_REMOVE
266
+ def remove_user_ban(data); end
267
+ end
268
+ end
269
+
270
+ include Stores::Gateway
271
+ end
272
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OnyxCord::Commands::Bot
4
+ module Channels
5
+ # @see Commands::Bot#update_channels
6
+ def channels=(channels)
7
+ update_channels(channels)
8
+ end
9
+
10
+ # Update the list of channels the bot accepts commands from.
11
+ # @param channels [Array<String, Integer, Channel>] The channels this command bot accepts commands on.
12
+
13
+ def update_channels(channels = [])
14
+ @attributes[:channels] = Array(channels)
15
+ end
16
+
17
+ # Add a channel to the list of channels the bot accepts commands from.
18
+ # @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be added
19
+
20
+ def add_channel(channel)
21
+ return if @attributes[:channels].find { |c| channel.resolve_id == c.resolve_id }
22
+
23
+ @attributes[:channels] << channel
24
+ end
25
+
26
+ # Remove a channel from the list of channels the bot accepts commands from.
27
+ # @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be removed
28
+
29
+ def remove_channel(channel)
30
+ @attributes[:channels].delete_if { |c| channel.resolve_id == c.resolve_id }
31
+ end
32
+
33
+ private
34
+
35
+ # Internal handler for MESSAGE_CREATE that is overwritten to allow for command handling
36
+
37
+ def channels?(channel, channels)
38
+ return true if channels.nil? || channels.empty?
39
+
40
+ channels.any? do |c|
41
+ # if c is string, make sure to remove the "#" from channel names in case it was specified
42
+ return true if c.is_a?(String) && c.delete('#') == channel.name
43
+
44
+ c.resolve_id == channel.resolve_id
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OnyxCord::Commands::Bot
4
+ module Execution
5
+ # Returns all aliases for the command with the given name
6
+ # @param name [Symbol] the name of the `Command`
7
+ # @return [Array<CommandAlias>]
8
+ def command_aliases(name)
9
+ commands.values.select do |command|
10
+ command.is_a?(OnyxCord::Commands::CommandAlias) && command.aliased_command.name == name
11
+ end
12
+ end
13
+
14
+ # Executes a particular command on the bot. Mostly useful for internal stuff, but one can never know.
15
+ # @param name [Symbol] The command to execute.
16
+ # @param event [CommandEvent] The event to pass to the command.
17
+ # @param arguments [Array<String>] The arguments to pass to the command.
18
+ # @param chained [true, false] Whether or not it should be executed as part of a command chain. If this is false,
19
+ # commands that have chain_usable set to false will not work.
20
+ # @param check_permissions [true, false] Whether permission parameters such as `required_permission` or
21
+ # `permission_level` should be checked.
22
+ # @return [String, nil] the command's result, if there is any.
23
+
24
+ def execute_command(name, event, arguments, chained = false, check_permissions = true)
25
+ debug("Executing command #{name} with arguments #{arguments}")
26
+ return unless @commands
27
+
28
+ command = @commands[name]
29
+ command = command.aliased_command if command.is_a?(OnyxCord::Commands::CommandAlias)
30
+ return unless !check_permissions || channels?(event.channel, @attributes[:channels]) ||
31
+ (command && !command.attributes[:channels].nil?)
32
+
33
+ unless command
34
+ if @attributes[:command_doesnt_exist_message]
35
+ message = @attributes[:command_doesnt_exist_message]
36
+ message = message.call(event) if message.respond_to?(:call)
37
+ event.respond message.gsub('%command%', name.to_s) if message
38
+ end
39
+ return
40
+ end
41
+ return unless !check_permissions || channels?(event.channel, command.attributes[:channels])
42
+
43
+ arguments = arg_check(arguments, command.attributes[:arg_types], event.server) if check_permissions
44
+ if (check_permissions &&
45
+ permission?(event.author, command.attributes[:permission_level], event.server) &&
46
+ required_permissions?(event.author, command.attributes[:required_permissions], event.channel) &&
47
+ required_roles?(event.author, command.attributes[:required_roles]) &&
48
+ allowed_roles?(event.author, command.attributes[:allowed_roles])) ||
49
+ !check_permissions
50
+ event.command = command
51
+ result = command.call(event, arguments, chained, check_permissions)
52
+ stringify(result)
53
+ else
54
+ event.respond command.attributes[:permission_message].gsub('%name%', name.to_s) if command.attributes[:permission_message]
55
+ nil
56
+ end
57
+ rescue OnyxCord::Errors::NoPermission
58
+ event.respond @attributes[:no_permission_message] unless @attributes[:no_permission_message].nil?
59
+ raise
60
+ end
61
+
62
+ # Transforms an array of string arguments based on types array.
63
+ # For example, `['1', '10..14']` with types `[Integer, Range]` would turn into `[1, 10..14]`.
64
+
65
+ def arg_check(args, types = nil, server = nil)
66
+ return args unless types
67
+
68
+ args.each_with_index.map do |arg, i|
69
+ next arg if types[i].nil? || types[i] == String
70
+
71
+ if types[i] == Integer
72
+ Integer(arg, 10, exception: false)
73
+ elsif types[i] == Float
74
+ Float(arg, exception: false)
75
+ elsif types[i] == Time
76
+ begin
77
+ Time.parse arg
78
+ rescue ArgumentError
79
+ nil
80
+ end
81
+ elsif [TrueClass, FalseClass].include?(types[i])
82
+ if arg.casecmp('true').zero? || arg.downcase.start_with?('y')
83
+ true
84
+ elsif arg.casecmp('false').zero? || arg.downcase.start_with?('n')
85
+ false
86
+ end
87
+ elsif types[i] == Symbol
88
+ arg.to_sym
89
+ elsif types[i] == Encoding
90
+ begin
91
+ Encoding.find arg
92
+ rescue ArgumentError
93
+ nil
94
+ end
95
+ elsif types[i] == Regexp
96
+ begin
97
+ Regexp.new arg
98
+ rescue ArgumentError
99
+ nil
100
+ end
101
+ elsif types[i] == Rational
102
+ Rational(arg, exception: false)
103
+ elsif types[i] == Range
104
+ begin
105
+ if arg.include? '...'
106
+ Range.new(*arg.split('...').map(&:to_i), true)
107
+ elsif arg.include? '..'
108
+ Range.new(*arg.split('..').map(&:to_i))
109
+ end
110
+ rescue ArgumentError
111
+ nil
112
+ end
113
+ elsif types[i] == NilClass
114
+ nil
115
+ elsif [OnyxCord::User, OnyxCord::Role, OnyxCord::Emoji].include? types[i]
116
+ result = parse_mention arg, server
117
+ result if result.instance_of? types[i]
118
+ elsif types[i] == OnyxCord::Invite
119
+ resolve_invite_code arg
120
+ elsif types[i].respond_to?(:from_argument)
121
+ begin
122
+ types[i].from_argument arg
123
+ rescue StandardError
124
+ nil
125
+ end
126
+ else
127
+ raise ArgumentError, "#{types[i]} doesn't implement from_argument"
128
+ end
129
+ end
130
+ end
131
+
132
+ # Executes a command in a simple manner, without command chains or permissions.
133
+ # @param chain [String] The command with its arguments separated by spaces.
134
+ # @param event [CommandEvent] The event to pass to the command.
135
+ # @return [String, nil] the command's result, if there is any.
136
+
137
+ def simple_execute(chain, event)
138
+ return nil if chain.empty?
139
+
140
+ args = chain.split(' ')
141
+ execute_command(args[0].to_sym, event, args[1..])
142
+ end
143
+
144
+ # Sets the permission level of a user
145
+ # @param id [Integer] the ID of the user whose level to set
146
+ # @param level [Integer] the level to set the permission to
147
+
148
+ private
149
+
150
+ def execute_chain(chain, event)
151
+ Async do
152
+ debug("Parsing command chain #{chain}")
153
+ result = @attributes[:advanced_functionality] ? OnyxCord::Commands::CommandChain.new(chain, self).execute(event) : simple_execute(chain, event)
154
+ result = event.drain_into(result)
155
+
156
+ if event.file
157
+ event.send_file(event.file, caption: result)
158
+ else
159
+ event.respond result unless result.nil? || result.empty?
160
+ end
161
+ rescue StandardError => e
162
+ log_exception(e)
163
+ end
164
+ end
165
+
166
+ # Turns the object into a string, using to_s by default
167
+
168
+ def stringify(object)
169
+ return '' if object.is_a? OnyxCord::Message
170
+
171
+ object.to_s
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OnyxCord::Commands::Bot
4
+ module MessageDispatch
5
+ private
6
+
7
+ # Internal handler for MESSAGE_CREATE that is overwritten to allow for command handling
8
+ def create_message(data)
9
+ message = OnyxCord::Message.new(data, self)
10
+ return message if message.from_bot? && !@should_parse_self
11
+ return message if message.webhook? && !@attributes[:webhook_commands]
12
+
13
+ unless message.author
14
+ OnyxCord::LOGGER.warn("Received a message (#{message.inspect}) with nil author! Ignoring, please report this if you can")
15
+ return
16
+ end
17
+
18
+ event = OnyxCord::Commands::CommandEvent.new(message, self)
19
+
20
+ chain = trigger?(message)
21
+ return message unless chain
22
+
23
+ # Don't allow spaces between the prefix and the command
24
+ if chain.start_with?(' ') && !@attributes[:spaces_allowed]
25
+ debug('Chain starts with a space')
26
+ return message
27
+ end
28
+
29
+ if chain.strip.empty?
30
+ debug('Chain is empty')
31
+ return message
32
+ end
33
+
34
+ execute_chain(chain, event)
35
+
36
+ # Return the message so it doesn't get parsed again during the rest of the dispatch handling
37
+ message
38
+ end
39
+
40
+ # Check whether a message should trigger command execution, and if it does, return the raw chain
41
+
42
+ def trigger?(message)
43
+ if @prefix.is_a? String
44
+ standard_prefix_trigger(message.content, @prefix)
45
+ elsif @prefix.is_a? Array
46
+ @prefix.map { |e| standard_prefix_trigger(message.content, e) }.reduce { |m, e| m || e }
47
+ elsif @prefix.respond_to? :call
48
+ @prefix.call(message)
49
+ end
50
+ end
51
+
52
+ def standard_prefix_trigger(message, prefix)
53
+ return nil unless message.start_with? prefix
54
+
55
+ message[prefix.length..]
56
+ end
57
+ end
58
+ end