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
data/lib/onyxcord/data.rb DELETED
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'onyxcord/allowed_mentions'
4
- require 'onyxcord/permissions'
5
- require 'onyxcord/id_object'
6
- require 'onyxcord/colour_rgb'
7
- require 'onyxcord/errors'
8
- require 'onyxcord/api'
9
- require 'onyxcord/api/channel'
10
- require 'onyxcord/api/server'
11
- require 'onyxcord/api/invite'
12
- require 'onyxcord/api/user'
13
- require 'onyxcord/api/webhook'
14
- require 'onyxcord/webhooks/embeds'
15
- require 'onyxcord/webhooks/view'
16
- require 'onyxcord/paginator'
17
- require 'time'
18
- require 'base64'
19
-
20
- require 'onyxcord/data/activity'
21
- require 'onyxcord/data/application'
22
- require 'onyxcord/data/user'
23
- require 'onyxcord/data/voice_state'
24
- require 'onyxcord/data/voice_region'
25
- require 'onyxcord/data/member'
26
- require 'onyxcord/data/recipient'
27
- require 'onyxcord/data/profile'
28
- require 'onyxcord/data/role'
29
- require 'onyxcord/data/invite'
30
- require 'onyxcord/data/overwrite'
31
- require 'onyxcord/data/channel'
32
- require 'onyxcord/data/embed'
33
- require 'onyxcord/data/attachment'
34
- require 'onyxcord/data/message'
35
- require 'onyxcord/data/reaction'
36
- require 'onyxcord/data/emoji'
37
- require 'onyxcord/data/integration'
38
- require 'onyxcord/data/server'
39
- require 'onyxcord/data/webhook'
40
- require 'onyxcord/data/audit_logs'
41
- require 'onyxcord/data/interaction'
42
- require 'onyxcord/data/component'
43
- require 'onyxcord/data/avatar_decoration'
44
- require 'onyxcord/data/collectibles'
45
- require 'onyxcord/data/primary_server'
46
- require 'onyxcord/data/server_preview'
47
- require 'onyxcord/data/call'
48
- require 'onyxcord/data/snapshot'
49
- require 'onyxcord/data/role_subscription'
50
- require 'onyxcord/data/message_activity'
51
- require 'onyxcord/data/team'
52
- require 'onyxcord/data/install_params'
53
- require 'onyxcord/data/role_connection_data'
54
- require 'onyxcord/data/channel_tag'
55
- require 'onyxcord/data/timestamp'
56
- require 'onyxcord/data/scheduled_event'
57
- require 'onyxcord/data/poll'
@@ -1,153 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'onyxcord/async/runtime'
4
-
5
- module OnyxCord
6
- module EventExecutor
7
- STOP = Object.new.freeze
8
-
9
- class Inline
10
- def post
11
- yield
12
- end
13
-
14
- def shutdown; end
15
-
16
- def threads
17
- []
18
- end
19
- end
20
-
21
- class Pool
22
- attr_reader :queue
23
-
24
- def initialize(size:, queue_size: nil)
25
- raise ArgumentError, 'Pool size must be greater than zero' unless size.positive?
26
-
27
- @size = size
28
- @queue = queue_size ? SizedQueue.new(queue_size) : Queue.new
29
- @closed = false
30
- @workers = []
31
-
32
- start_workers
33
- end
34
-
35
- def post(&block)
36
- raise ArgumentError, 'EventExecutor::Pool#post requires a block' unless block
37
- raise 'Event executor has been shut down' if @closed
38
-
39
- @queue << block
40
- end
41
-
42
- def queue_size
43
- @queue.size
44
- end
45
-
46
- def threads
47
- @workers
48
- end
49
-
50
- def shutdown
51
- return if @closed
52
-
53
- @closed = true
54
- @size.times { @queue << STOP }
55
- @workers.each do |w|
56
- w.join unless w == Thread.current
57
- rescue StandardError
58
- nil
59
- end
60
- end
61
-
62
- private
63
-
64
- def start_workers
65
- @workers = Array.new(@size) do |index|
66
- Thread.new do
67
- Thread.current[:onyxcord_name] = "event-worker-#{index + 1}"
68
- worker_loop
69
- end
70
- end
71
- end
72
-
73
- def worker_loop
74
- loop do
75
- job = @queue.pop
76
- break if job.equal?(STOP)
77
-
78
- job.call
79
- rescue StandardError => e
80
- OnyxCord::LOGGER.log_exception(e) if defined?(OnyxCord::LOGGER)
81
- end
82
- end
83
- end
84
-
85
- class AsyncPool
86
- attr_reader :queue
87
-
88
- def initialize(size:, queue_size: nil)
89
- raise ArgumentError, 'Pool size must be greater than zero' unless size.positive?
90
-
91
- @size = size
92
- @queue = ::Async::Queue.new
93
- @closed = false
94
- @workers = []
95
- start_workers
96
- end
97
-
98
- def post(&block)
99
- raise ArgumentError, 'EventExecutor::AsyncPool#post requires a block' unless block
100
- raise 'Event executor has been shut down' if @closed
101
-
102
- @queue.enqueue(block)
103
- end
104
-
105
- def queue_size
106
- @queue.size
107
- end
108
-
109
- def shutdown
110
- return if @closed
111
-
112
- @closed = true
113
- @size.times { @queue.enqueue(STOP) }
114
- end
115
-
116
- def threads
117
- []
118
- end
119
-
120
- private
121
-
122
- def start_workers
123
- @workers = Array.new(@size) do
124
- OnyxCord::AsyncRuntime.async do
125
- loop do
126
- job = @queue.dequeue
127
- break if job.equal?(STOP)
128
-
129
- job.call
130
- rescue StandardError => e
131
- OnyxCord::LOGGER.log_exception(e) if defined?(OnyxCord::LOGGER)
132
- end
133
- end
134
- end
135
- end
136
- end
137
-
138
- module_function
139
-
140
- def build(type, workers:, queue_size: nil)
141
- case type
142
- when :inline
143
- Inline.new
144
- when :pool
145
- Pool.new(size: workers, queue_size: queue_size)
146
- when :async_pool
147
- AsyncPool.new(size: workers, queue_size: queue_size)
148
- else
149
- raise ArgumentError, "Unknown event executor: #{type.inspect}"
150
- end
151
- end
152
- end
153
- end
@@ -1,500 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'async'
4
- require 'async/http/endpoint'
5
- require 'async/http/protocol/http11'
6
- require 'async/websocket/client'
7
- require 'onyxcord/async/runtime'
8
- require 'onyxcord/rate_limiter/gateway'
9
-
10
- module OnyxCord
11
- # Gateway packet opcodes
12
- module Opcodes
13
- DISPATCH = 0
14
- HEARTBEAT = 1
15
- IDENTIFY = 2
16
- PRESENCE = 3
17
- VOICE_STATE = 4
18
- VOICE_PING = 5
19
- RESUME = 6
20
- RECONNECT = 7
21
- REQUEST_MEMBERS = 8
22
- INVALIDATE_SESSION = 9
23
- HELLO = 10
24
- HEARTBEAT_ACK = 11
25
- end
26
-
27
- # Stores data of an active gateway session.
28
- class Session
29
- attr_reader :session_id, :resume_gateway_url
30
- attr_accessor :sequence
31
-
32
- def initialize(session_id, resume_gateway_url)
33
- @session_id = session_id
34
- @sequence = 0
35
- @suspended = false
36
- @invalid = false
37
- @resume_gateway_url = resume_gateway_url
38
- end
39
-
40
- def suspend
41
- @suspended = true
42
- end
43
-
44
- def suspended?
45
- @suspended
46
- end
47
-
48
- def resume
49
- @suspended = false
50
- end
51
-
52
- def invalidate
53
- @invalid = true
54
- @resume_gateway_url = nil
55
- end
56
-
57
- def invalid?
58
- @invalid
59
- end
60
-
61
- def should_resume?
62
- suspended? && !invalid?
63
- end
64
- end
65
-
66
- # Client for the Discord gateway protocol — fully async.
67
- class Gateway
68
- LARGE_THRESHOLD = 100
69
- GATEWAY_VERSION = 9
70
- FATAL_CLOSE_CODES = [4003, 4004, 4011, 4014].freeze
71
-
72
- attr_accessor :check_heartbeat_acks
73
- attr_reader :intents
74
-
75
- def initialize(bot, token, shard_key = nil, compress_mode = :stream, intents = ALL_INTENTS)
76
- @token = token
77
- @bot = bot
78
- @shard_key = shard_key
79
- @ws_success = false
80
- @check_heartbeat_acks = true
81
- @compress_mode = compress_mode
82
- @intents = intents
83
- @send_limiter = OnyxCord::RateLimiter::Gateway.new
84
- @connection = nil
85
- @closed = true
86
- @pipe_broken = false
87
- @missed_heartbeat_acks = 0
88
- end
89
-
90
- # Connect to the gateway server inside an Async reactor.
91
- def run_async
92
- @task = OnyxCord::AsyncRuntime.async { run }
93
-
94
- LOGGER.debug('Gateway task created! Waiting for confirmation...')
95
- loop do
96
- OnyxCord::AsyncRuntime.sleep(0.5)
97
- break if @ws_success
98
- break if @should_reconnect == false
99
- end
100
- end
101
-
102
- def run
103
- @reactor_task = Async::Task.current
104
- connect_loop
105
- LOGGER.warn('The gateway loop exited!')
106
- end
107
-
108
- def sync
109
- @task&.wait
110
- end
111
-
112
- def open?
113
- !@closed && @connection
114
- end
115
-
116
- def stop
117
- @should_reconnect = false
118
- close
119
- nil
120
- end
121
-
122
- def kill
123
- @task&.stop
124
- end
125
-
126
- def notify_ready
127
- @ws_success = true
128
- end
129
-
130
- def inject_reconnect(url = nil)
131
- data = url ? { url: url } : nil
132
- handle_message({ op: Opcodes::RECONNECT, d: data }.to_json)
133
- end
134
-
135
- def inject_resume(seq)
136
- send_resume(raw_token, @session_id, seq || @sequence)
137
- end
138
-
139
- def inject_error(e)
140
- handle_internal_close(e)
141
- end
142
-
143
- def heartbeat
144
- if check_heartbeat_acks
145
- unless @last_heartbeat_acked
146
- @missed_heartbeat_acks += 1
147
- if @missed_heartbeat_acks >= 2
148
- LOGGER.warn('Last heartbeats were not acked — zombie connection! Reconnecting')
149
- @pipe_broken = true
150
- reconnect
151
- return
152
- end
153
-
154
- LOGGER.warn('Last heartbeat was not acked — waiting one more interval before reconnecting')
155
- else
156
- @missed_heartbeat_acks = 0
157
- end
158
- @last_heartbeat_acked = false
159
- end
160
- send_heartbeat(@session ? @session.sequence : 0)
161
- end
162
-
163
- def send_heartbeat(sequence)
164
- send_packet(Opcodes::HEARTBEAT, sequence)
165
- end
166
-
167
- def identify
168
- compress = @compress_mode == :large
169
- send_identify(@token, {
170
- os: RUBY_PLATFORM,
171
- browser: 'onyxcord',
172
- device: 'onyxcord'
173
- }, compress, LARGE_THRESHOLD, @shard_key, @intents)
174
- end
175
-
176
- def send_identify(token, properties, compress, large_threshold, shard_key = nil, intents = ALL_INTENTS)
177
- data = {
178
- token: token,
179
- properties: properties,
180
- compress: compress,
181
- large_threshold: large_threshold,
182
- intents: intents
183
- }
184
- data[:shard] = shard_key if shard_key
185
- send_packet(Opcodes::IDENTIFY, data)
186
- end
187
-
188
- def send_status_update(status, since, game, afk)
189
- send_packet(Opcodes::PRESENCE, { status: status, since: since, game: game, afk: afk })
190
- end
191
-
192
- def send_voice_state_update(server_id, channel_id, self_mute, self_deaf)
193
- send_packet(Opcodes::VOICE_STATE, {
194
- guild_id: server_id, channel_id: channel_id,
195
- self_mute: self_mute, self_deaf: self_deaf
196
- })
197
- end
198
-
199
- def resume
200
- send_resume(@token, @session.session_id, @session.sequence)
201
- end
202
-
203
- def reconnect(attempt_resume = true)
204
- @session.suspend if @session && attempt_resume
205
- @instant_reconnect = true
206
- @should_reconnect = true
207
- close(4000)
208
- end
209
-
210
- def send_resume(token, session_id, seq)
211
- send_packet(Opcodes::RESUME, { token: token, session_id: session_id, seq: seq })
212
- end
213
-
214
- def send_request_members(server_id, query, limit)
215
- send_packet(Opcodes::REQUEST_MEMBERS, { guild_id: server_id, query: query, limit: limit })
216
- end
217
-
218
- def send_packet(opcode, packet)
219
- send({ op: opcode, d: packet }.to_json)
220
- end
221
-
222
- def send_raw(data, _type = :text)
223
- send(data)
224
- end
225
-
226
- private
227
-
228
- def setup_heartbeats(interval)
229
- @last_heartbeat_acked = true
230
- @missed_heartbeat_acks = 0
231
- return if @heartbeat_task
232
-
233
- @heartbeat_interval = interval
234
- @heartbeat_task = @reactor_task&.async do
235
- loop do
236
- if (@session && !@session.suspended?) || !@session
237
- OnyxCord::AsyncRuntime.sleep(@heartbeat_interval)
238
- if !@closed && @connection
239
- @bot.raise_heartbeat_event
240
- heartbeat
241
- else
242
- LOGGER.debug('Tried to heartbeat without connection — skipping.')
243
- end
244
- else
245
- OnyxCord::AsyncRuntime.sleep(1)
246
- end
247
- rescue StandardError => e
248
- LOGGER.error('Error while heartbeating!')
249
- LOGGER.log_exception(e)
250
- end
251
- end
252
- end
253
-
254
- def connect_loop
255
- @falloff = 1.0
256
- @should_reconnect = true
257
-
258
- loop do
259
- connect
260
- break unless @should_reconnect
261
-
262
- if @instant_reconnect
263
- LOGGER.info('Instant reconnection — reconnecting now')
264
- @instant_reconnect = false
265
- else
266
- wait_for_reconnect
267
- end
268
- end
269
- end
270
-
271
- def wait_for_reconnect
272
- LOGGER.debug("Reconnecting in #{@falloff} seconds.")
273
- OnyxCord::AsyncRuntime.sleep(@falloff)
274
- @falloff *= 1.5
275
- @falloff = 115 + (rand * 10) if @falloff > 120
276
- end
277
-
278
- def find_gateway
279
- response = API.gateway(@token)
280
- JSON.parse(response)['url']
281
- end
282
-
283
- def process_gateway
284
- raw_url = @session&.resume_gateway_url || find_gateway
285
- raw_url += '/' unless raw_url.end_with?('/')
286
-
287
- query = if @compress_mode == :stream
288
- "?encoding=json&v=#{GATEWAY_VERSION}&compress=zlib-stream"
289
- else
290
- "?encoding=json&v=#{GATEWAY_VERSION}"
291
- end
292
-
293
- raw_url + query
294
- end
295
-
296
- def connect
297
- LOGGER.debug('Connecting')
298
-
299
- url = process_gateway
300
- LOGGER.debug("Gateway URL: #{url}")
301
-
302
- @zlib_reader = Zlib::Inflate.new
303
- @pipe_broken = false
304
- @closed = false
305
-
306
- endpoint = websocket_endpoint(url)
307
-
308
- Async::WebSocket::Client.connect(endpoint, extensions: nil) do |connection|
309
- @connection = connection
310
- LOGGER.debug('WebSocket connected')
311
-
312
- handle_open
313
-
314
- while (message = connection.read)
315
- handle_message(message.to_str)
316
- end
317
- end
318
- rescue StandardError => e
319
- LOGGER.error('Error connecting to gateway!')
320
- LOGGER.log_exception(e)
321
- ensure
322
- @closed = true
323
- @connection = nil
324
- end
325
-
326
- def websocket_endpoint(url)
327
- Async::HTTP::Endpoint.parse(
328
- url,
329
- protocol: Async::HTTP::Protocol::HTTP11,
330
- alpn_protocols: ['http/1.1']
331
- )
332
- end
333
-
334
- def handle_open; end
335
-
336
- def handle_error(e)
337
- LOGGER.error('Error in gateway loop!')
338
- LOGGER.log_exception(e)
339
- end
340
-
341
- ZLIB_SUFFIX = "\x00\x00\xFF\xFF".b.freeze
342
- private_constant :ZLIB_SUFFIX
343
-
344
- def handle_message(msg)
345
- case @compress_mode
346
- when :large
347
- msg = Zlib::Inflate.inflate(msg) if msg.byteslice(0) == 'x'
348
- when :stream
349
- @zlib_reader << msg
350
- return if msg.bytesize < 4 || msg.byteslice(-4, 4) != ZLIB_SUFFIX
351
-
352
- msg = @zlib_reader.inflate('')
353
- end
354
-
355
- packet = JSON.parse(msg)
356
- op = packet['op'].to_i
357
-
358
- LOGGER.in(packet)
359
-
360
- @session.sequence = packet['s'] if packet['s'] && @session
361
-
362
- case op
363
- when Opcodes::DISPATCH
364
- handle_dispatch(packet)
365
- when Opcodes::HELLO
366
- handle_hello(packet)
367
- when Opcodes::RECONNECT
368
- handle_reconnect
369
- when Opcodes::INVALIDATE_SESSION
370
- handle_invalidate_session(packet)
371
- when Opcodes::HEARTBEAT_ACK
372
- handle_heartbeat_ack(packet)
373
- when Opcodes::HEARTBEAT
374
- handle_heartbeat(packet)
375
- else
376
- LOGGER.warn("Invalid opcode #{op}: #{msg}")
377
- end
378
- end
379
-
380
- # Op 0
381
- def handle_dispatch(packet)
382
- data = packet['d']
383
- type = packet['t'].intern
384
-
385
- case type
386
- when :READY
387
- LOGGER.info("Discord gateway v#{data['v']}, requested: #{GATEWAY_VERSION}")
388
- @session = Session.new(data['session_id'], data['resume_gateway_url'])
389
- @session.sequence = 0
390
- @bot.__send__(:notify_ready) if @intents && @intents.nobits?(INTENTS[:servers])
391
- when :RESUMED
392
- LOGGER.info 'Resumed'
393
- return
394
- end
395
-
396
- @bot.dispatch(packet)
397
- end
398
-
399
- # Op 1
400
- def handle_heartbeat(packet)
401
- send_heartbeat(packet['s'])
402
- end
403
-
404
- # Op 7
405
- def handle_reconnect
406
- LOGGER.debug('Received op 7, reconnecting')
407
- reconnect
408
- end
409
-
410
- # Op 9
411
- def handle_invalidate_session(packet)
412
- LOGGER.debug('Received op 9, invalidating session')
413
- if @session
414
- if packet['d'] == true
415
- reconnect
416
- else
417
- @session.invalidate
418
- end
419
- else
420
- LOGGER.warn('Op 9 without session!')
421
- end
422
- identify
423
- end
424
-
425
- # Op 10
426
- def handle_hello(packet)
427
- LOGGER.debug('Hello!')
428
- interval = packet['d']['heartbeat_interval'].to_f / 1000.0
429
- setup_heartbeats(interval)
430
- LOGGER.debug("Trace: #{packet['d']['_trace']}")
431
- LOGGER.debug("Session: #{@session.inspect}")
432
-
433
- if @session&.should_resume?
434
- @session.resume
435
- resume
436
- else
437
- identify
438
- end
439
- end
440
-
441
- # Op 11
442
- def handle_heartbeat_ack(packet)
443
- LOGGER.debug("Heartbeat ACK: #{packet.inspect}")
444
- if @check_heartbeat_acks
445
- @last_heartbeat_acked = true
446
- @missed_heartbeat_acks = 0
447
- end
448
- end
449
-
450
- def handle_internal_close(e)
451
- close
452
- handle_close(e)
453
- end
454
-
455
- def handle_close(e)
456
- @bot.__send__(:raise_event, Events::DisconnectEvent.new(@bot))
457
-
458
- if e.respond_to?(:code)
459
- LOGGER.error("WebSocket close frame! Code: #{e.code}")
460
- LOGGER.error('Privileged intents not authorized. Enable them in the Discord developer portal.') if e.code == 4014
461
- @should_reconnect = false if FATAL_CLOSE_CODES.include?(e.code)
462
- elsif e.is_a?(Exception)
463
- LOGGER.error('WebSocket closed due to error!')
464
- LOGGER.log_exception(e)
465
- else
466
- LOGGER.error("WebSocket closed: #{e&.inspect || '(no info)'}")
467
- end
468
- end
469
-
470
- def send(data, _type = :text, _code = nil)
471
- LOGGER.out(data)
472
-
473
- raise 'Tried to send to websocket while not connected!' unless @connection && !@closed
474
-
475
- @send_limiter.wait
476
-
477
- @connection.write(data)
478
- @connection.flush
479
- rescue StandardError => e
480
- @pipe_broken = true
481
- handle_internal_close(e)
482
- end
483
-
484
- def close(_code = 1000)
485
- return if @closed
486
-
487
- @session&.suspend
488
- @closed = true
489
-
490
- begin
491
- @connection&.close
492
- rescue StandardError
493
- # Ignore close errors
494
- end
495
-
496
- @connection = nil
497
- handle_close(nil)
498
- end
499
- end
500
- end