onyxcord 2.1.0 → 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/{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
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,505 +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
- @session.invalidate if attempt_resume && @session && !@received_hello
206
- @instant_reconnect = true
207
- @should_reconnect = true
208
- close(4000)
209
- end
210
-
211
- def send_resume(token, session_id, seq)
212
- send_packet(Opcodes::RESUME, { token: token, session_id: session_id, seq: seq })
213
- end
214
-
215
- def send_request_members(server_id, query, limit)
216
- send_packet(Opcodes::REQUEST_MEMBERS, { guild_id: server_id, query: query, limit: limit })
217
- end
218
-
219
- def send_packet(opcode, packet)
220
- send({ op: opcode, d: packet }.to_json)
221
- end
222
-
223
- def send_raw(data, _type = :text)
224
- send(data)
225
- end
226
-
227
- private
228
-
229
- def setup_heartbeats(interval)
230
- @last_heartbeat_acked = true
231
- @missed_heartbeat_acks = 0
232
- return if @heartbeat_task
233
-
234
- @heartbeat_interval = interval
235
- @heartbeat_task = @reactor_task&.async do
236
- loop do
237
- if (@session && !@session.suspended?) || !@session
238
- OnyxCord::AsyncRuntime.sleep(@heartbeat_interval)
239
- if !@closed && @connection
240
- @bot.raise_heartbeat_event
241
- heartbeat
242
- else
243
- LOGGER.debug('Tried to heartbeat without connection — skipping.')
244
- end
245
- else
246
- OnyxCord::AsyncRuntime.sleep(1)
247
- end
248
- rescue StandardError => e
249
- LOGGER.error('Error while heartbeating!')
250
- LOGGER.log_exception(e)
251
- end
252
- end
253
- end
254
-
255
- def connect_loop
256
- @falloff = 1.0
257
- @should_reconnect = true
258
-
259
- loop do
260
- connect
261
- break unless @should_reconnect
262
-
263
- if @instant_reconnect
264
- LOGGER.info('Instant reconnection — reconnecting now')
265
- @instant_reconnect = false
266
- else
267
- wait_for_reconnect
268
- end
269
- end
270
- end
271
-
272
- def wait_for_reconnect
273
- LOGGER.debug("Reconnecting in #{@falloff} seconds.")
274
- OnyxCord::AsyncRuntime.sleep(@falloff)
275
- @falloff *= 1.5
276
- @falloff = 115 + (rand * 10) if @falloff > 120
277
- end
278
-
279
- def find_gateway
280
- response = API.gateway(@token)
281
- JSON.parse(response)['url']
282
- end
283
-
284
- def process_gateway
285
- raw_url = @session&.resume_gateway_url || find_gateway
286
- raw_url += '/' unless raw_url.end_with?('/')
287
-
288
- query = if @compress_mode == :stream
289
- "?encoding=json&v=#{GATEWAY_VERSION}&compress=zlib-stream"
290
- else
291
- "?encoding=json&v=#{GATEWAY_VERSION}"
292
- end
293
-
294
- raw_url + query
295
- end
296
-
297
- def connect
298
- LOGGER.debug('Connecting')
299
-
300
- url = process_gateway
301
- LOGGER.debug("Gateway URL: #{url}")
302
-
303
- @zlib_reader = Zlib::Inflate.new
304
- @pipe_broken = false
305
- @received_hello = false
306
- @closed = false
307
-
308
- endpoint = websocket_endpoint(url)
309
-
310
- Async::WebSocket::Client.connect(endpoint, extensions: nil) do |connection|
311
- @connection = connection
312
- LOGGER.debug('WebSocket connected')
313
-
314
- handle_open
315
-
316
- while (message = connection.read)
317
- handle_message(message.to_str)
318
- end
319
- end
320
- rescue StandardError => e
321
- @session&.invalidate unless @received_hello
322
- LOGGER.error('Error connecting to gateway!')
323
- LOGGER.log_exception(e)
324
- ensure
325
- @session&.invalidate unless @received_hello
326
- @closed = true
327
- @connection = nil
328
- end
329
-
330
- def websocket_endpoint(url)
331
- Async::HTTP::Endpoint.parse(
332
- url,
333
- protocol: Async::HTTP::Protocol::HTTP11,
334
- alpn_protocols: ['http/1.1']
335
- )
336
- end
337
-
338
- def handle_open; end
339
-
340
- def handle_error(e)
341
- LOGGER.error('Error in gateway loop!')
342
- LOGGER.log_exception(e)
343
- end
344
-
345
- ZLIB_SUFFIX = "\x00\x00\xFF\xFF".b.freeze
346
- private_constant :ZLIB_SUFFIX
347
-
348
- def handle_message(msg)
349
- case @compress_mode
350
- when :large
351
- msg = Zlib::Inflate.inflate(msg) if msg.byteslice(0) == 'x'
352
- when :stream
353
- @zlib_reader << msg
354
- return if msg.bytesize < 4 || msg.byteslice(-4, 4) != ZLIB_SUFFIX
355
-
356
- msg = @zlib_reader.inflate('')
357
- end
358
-
359
- packet = JSON.parse(msg)
360
- op = packet['op'].to_i
361
-
362
- LOGGER.in(packet)
363
-
364
- @session.sequence = packet['s'] if packet['s'] && @session
365
-
366
- case op
367
- when Opcodes::DISPATCH
368
- handle_dispatch(packet)
369
- when Opcodes::HELLO
370
- handle_hello(packet)
371
- when Opcodes::RECONNECT
372
- handle_reconnect
373
- when Opcodes::INVALIDATE_SESSION
374
- handle_invalidate_session(packet)
375
- when Opcodes::HEARTBEAT_ACK
376
- handle_heartbeat_ack(packet)
377
- when Opcodes::HEARTBEAT
378
- handle_heartbeat(packet)
379
- else
380
- LOGGER.warn("Invalid opcode #{op}: #{msg}")
381
- end
382
- end
383
-
384
- # Op 0
385
- def handle_dispatch(packet)
386
- data = packet['d']
387
- type = packet['t'].intern
388
-
389
- case type
390
- when :READY
391
- LOGGER.info("Discord gateway v#{data['v']}, requested: #{GATEWAY_VERSION}")
392
- @session = Session.new(data['session_id'], data['resume_gateway_url'])
393
- @session.sequence = 0
394
- @bot.__send__(:notify_ready) if @intents && @intents.nobits?(INTENTS[:servers])
395
- when :RESUMED
396
- LOGGER.info 'Resumed'
397
- return
398
- end
399
-
400
- @bot.dispatch(packet)
401
- end
402
-
403
- # Op 1
404
- def handle_heartbeat(packet)
405
- send_heartbeat(packet['s'])
406
- end
407
-
408
- # Op 7
409
- def handle_reconnect
410
- LOGGER.debug('Received op 7, reconnecting')
411
- reconnect
412
- end
413
-
414
- # Op 9
415
- def handle_invalidate_session(packet)
416
- LOGGER.debug('Received op 9, invalidating session')
417
- if @session
418
- if packet['d'] == true
419
- reconnect
420
- else
421
- @session.invalidate
422
- end
423
- else
424
- LOGGER.warn('Op 9 without session!')
425
- end
426
- identify
427
- end
428
-
429
- # Op 10
430
- def handle_hello(packet)
431
- @received_hello = true
432
- LOGGER.debug('Hello!')
433
- interval = packet['d']['heartbeat_interval'].to_f / 1000.0
434
- setup_heartbeats(interval)
435
- LOGGER.debug("Trace: #{packet['d']['_trace']}")
436
- LOGGER.debug("Session: #{@session.inspect}")
437
-
438
- if @session&.should_resume?
439
- @session.resume
440
- resume
441
- else
442
- identify
443
- end
444
- end
445
-
446
- # Op 11
447
- def handle_heartbeat_ack(packet)
448
- LOGGER.debug("Heartbeat ACK: #{packet.inspect}")
449
- if @check_heartbeat_acks
450
- @last_heartbeat_acked = true
451
- @missed_heartbeat_acks = 0
452
- end
453
- end
454
-
455
- def handle_internal_close(e)
456
- close
457
- handle_close(e)
458
- end
459
-
460
- def handle_close(e)
461
- @bot.__send__(:raise_event, Events::DisconnectEvent.new(@bot))
462
-
463
- if e.respond_to?(:code)
464
- LOGGER.error("WebSocket close frame! Code: #{e.code}")
465
- LOGGER.error('Privileged intents not authorized. Enable them in the Discord developer portal.') if e.code == 4014
466
- @should_reconnect = false if FATAL_CLOSE_CODES.include?(e.code)
467
- elsif e.is_a?(Exception)
468
- LOGGER.error('WebSocket closed due to error!')
469
- LOGGER.log_exception(e)
470
- else
471
- LOGGER.error("WebSocket closed: #{e&.inspect || '(no info)'}")
472
- end
473
- end
474
-
475
- def send(data, _type = :text, _code = nil)
476
- LOGGER.out(data)
477
-
478
- raise 'Tried to send to websocket while not connected!' unless @connection && !@closed
479
-
480
- @send_limiter.wait
481
-
482
- @connection.write(data)
483
- @connection.flush
484
- rescue StandardError => e
485
- @pipe_broken = true
486
- handle_internal_close(e)
487
- end
488
-
489
- def close(_code = 1000)
490
- return if @closed
491
-
492
- @session&.suspend
493
- @closed = true
494
-
495
- begin
496
- @connection&.close
497
- rescue StandardError
498
- # Ignore close errors
499
- end
500
-
501
- @connection = nil
502
- handle_close(nil)
503
- end
504
- end
505
- end