shoal 0.1.0a1__tar.gz

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 (465) hide show
  1. shoal-0.1.0a1/.gitignore +28 -0
  2. shoal-0.1.0a1/CHANGELOG.md +241 -0
  3. shoal-0.1.0a1/LICENSE +21 -0
  4. shoal-0.1.0a1/PKG-INFO +218 -0
  5. shoal-0.1.0a1/README.md +173 -0
  6. shoal-0.1.0a1/docs/examples/README.md +64 -0
  7. shoal-0.1.0a1/docs/examples/bigbot/README.md +79 -0
  8. shoal-0.1.0a1/docs/examples/clusters/README.md +56 -0
  9. shoal-0.1.0a1/docs/examples/cogs/README.md +27 -0
  10. shoal-0.1.0a1/docs/examples/compat/README.md +37 -0
  11. shoal-0.1.0a1/docs/examples/components/README.md +26 -0
  12. shoal-0.1.0a1/docs/examples/emojis/README.md +23 -0
  13. shoal-0.1.0a1/docs/examples/forms/README.md +28 -0
  14. shoal-0.1.0a1/docs/examples/giveaways/README.md +24 -0
  15. shoal-0.1.0a1/docs/examples/hello/README.md +16 -0
  16. shoal-0.1.0a1/docs/examples/http/README.md +31 -0
  17. shoal-0.1.0a1/docs/examples/hybrid/README.md +22 -0
  18. shoal-0.1.0a1/docs/examples/leveling/README.md +25 -0
  19. shoal-0.1.0a1/docs/examples/logs/README.md +22 -0
  20. shoal-0.1.0a1/docs/examples/moderation/README.md +23 -0
  21. shoal-0.1.0a1/docs/examples/paginator/README.md +22 -0
  22. shoal-0.1.0a1/docs/examples/polls/README.md +21 -0
  23. shoal-0.1.0a1/docs/examples/prefix/README.md +25 -0
  24. shoal-0.1.0a1/docs/examples/reminders/README.md +24 -0
  25. shoal-0.1.0a1/docs/examples/rest/README.md +21 -0
  26. shoal-0.1.0a1/docs/examples/roles/buttons/README.md +24 -0
  27. shoal-0.1.0a1/docs/examples/roles/reactions/README.md +23 -0
  28. shoal-0.1.0a1/docs/examples/slash/README.md +26 -0
  29. shoal-0.1.0a1/docs/examples/starboard/README.md +21 -0
  30. shoal-0.1.0a1/docs/examples/tickets/README.md +26 -0
  31. shoal-0.1.0a1/docs/examples/voice/messages/README.md +26 -0
  32. shoal-0.1.0a1/docs/examples/voice/music/README.md +27 -0
  33. shoal-0.1.0a1/docs/examples/voice/recorder/README.md +26 -0
  34. shoal-0.1.0a1/docs/examples/welcome/README.md +21 -0
  35. shoal-0.1.0a1/docs/examples/workers/README.md +62 -0
  36. shoal-0.1.0a1/pyproject.toml +267 -0
  37. shoal-0.1.0a1/src/shoal/__init__.py +122 -0
  38. shoal-0.1.0a1/src/shoal/_version.py +3 -0
  39. shoal-0.1.0a1/src/shoal/app_commands/__init__.py +144 -0
  40. shoal-0.1.0a1/src/shoal/app_commands/checks.py +145 -0
  41. shoal-0.1.0a1/src/shoal/app_commands/commands.py +1069 -0
  42. shoal-0.1.0a1/src/shoal/app_commands/errors.py +152 -0
  43. shoal-0.1.0a1/src/shoal/app_commands/models.py +96 -0
  44. shoal-0.1.0a1/src/shoal/app_commands/parameters.py +374 -0
  45. shoal-0.1.0a1/src/shoal/app_commands/transformers.py +114 -0
  46. shoal-0.1.0a1/src/shoal/app_commands/translator.py +125 -0
  47. shoal-0.1.0a1/src/shoal/app_commands/tree.py +585 -0
  48. shoal-0.1.0a1/src/shoal/cache/__init__.py +33 -0
  49. shoal-0.1.0a1/src/shoal/cache/collection.py +269 -0
  50. shoal-0.1.0a1/src/shoal/cache/defaults.py +21 -0
  51. shoal-0.1.0a1/src/shoal/cache/entry.py +45 -0
  52. shoal-0.1.0a1/src/shoal/cache/guild.py +22 -0
  53. shoal-0.1.0a1/src/shoal/cache/policy.py +105 -0
  54. shoal-0.1.0a1/src/shoal/cache/store.py +776 -0
  55. shoal-0.1.0a1/src/shoal/cache/trim.py +66 -0
  56. shoal-0.1.0a1/src/shoal/cache/updater.py +820 -0
  57. shoal-0.1.0a1/src/shoal/client/__init__.py +36 -0
  58. shoal-0.1.0a1/src/shoal/client/client.py +1355 -0
  59. shoal-0.1.0a1/src/shoal/client/defaults.py +21 -0
  60. shoal-0.1.0a1/src/shoal/client/events.py +579 -0
  61. shoal-0.1.0a1/src/shoal/client/interaction.py +634 -0
  62. shoal-0.1.0a1/src/shoal/client/views.py +186 -0
  63. shoal-0.1.0a1/src/shoal/commands/__init__.py +311 -0
  64. shoal-0.1.0a1/src/shoal/commands/bot.py +663 -0
  65. shoal-0.1.0a1/src/shoal/commands/checks.py +401 -0
  66. shoal-0.1.0a1/src/shoal/commands/cog.py +257 -0
  67. shoal-0.1.0a1/src/shoal/commands/context.py +301 -0
  68. shoal-0.1.0a1/src/shoal/commands/converters.py +807 -0
  69. shoal-0.1.0a1/src/shoal/commands/core.py +855 -0
  70. shoal-0.1.0a1/src/shoal/commands/errors.py +513 -0
  71. shoal-0.1.0a1/src/shoal/commands/flags.py +473 -0
  72. shoal-0.1.0a1/src/shoal/commands/help.py +442 -0
  73. shoal-0.1.0a1/src/shoal/commands/hybrid.py +326 -0
  74. shoal-0.1.0a1/src/shoal/commands/parameters.py +540 -0
  75. shoal-0.1.0a1/src/shoal/commands/view.py +133 -0
  76. shoal-0.1.0a1/src/shoal/compat/__init__.py +441 -0
  77. shoal-0.1.0a1/src/shoal/compat/app_commands.py +162 -0
  78. shoal-0.1.0a1/src/shoal/compat/commands.py +416 -0
  79. shoal-0.1.0a1/src/shoal/compat/utils.py +141 -0
  80. shoal-0.1.0a1/src/shoal/core/__init__.py +55 -0
  81. shoal-0.1.0a1/src/shoal/core/backoff.py +22 -0
  82. shoal-0.1.0a1/src/shoal/core/clock.py +84 -0
  83. shoal-0.1.0a1/src/shoal/core/codec.py +165 -0
  84. shoal-0.1.0a1/src/shoal/core/cooldown.py +162 -0
  85. shoal-0.1.0a1/src/shoal/core/defaults.py +9 -0
  86. shoal-0.1.0a1/src/shoal/core/dispatch.py +50 -0
  87. shoal-0.1.0a1/src/shoal/core/enums.py +52 -0
  88. shoal-0.1.0a1/src/shoal/core/errors.py +15 -0
  89. shoal-0.1.0a1/src/shoal/core/files.py +57 -0
  90. shoal-0.1.0a1/src/shoal/core/flags.py +169 -0
  91. shoal-0.1.0a1/src/shoal/core/formatting.py +126 -0
  92. shoal-0.1.0a1/src/shoal/core/metrics.py +338 -0
  93. shoal-0.1.0a1/src/shoal/core/ogg.py +270 -0
  94. shoal-0.1.0a1/src/shoal/core/replies.py +15 -0
  95. shoal-0.1.0a1/src/shoal/core/snowflake.py +113 -0
  96. shoal-0.1.0a1/src/shoal/core/tasks.py +45 -0
  97. shoal-0.1.0a1/src/shoal/diagnostics/__init__.py +11 -0
  98. shoal-0.1.0a1/src/shoal/diagnostics/defaults.py +9 -0
  99. shoal-0.1.0a1/src/shoal/diagnostics/loop.py +222 -0
  100. shoal-0.1.0a1/src/shoal/emojis/__init__.py +34 -0
  101. shoal-0.1.0a1/src/shoal/emojis/__main__.py +74 -0
  102. shoal-0.1.0a1/src/shoal/emojis/defaults.py +17 -0
  103. shoal-0.1.0a1/src/shoal/emojis/registry.py +271 -0
  104. shoal-0.1.0a1/src/shoal/emojis/sync.py +379 -0
  105. shoal-0.1.0a1/src/shoal/endpoint/__init__.py +15 -0
  106. shoal-0.1.0a1/src/shoal/endpoint/defaults.py +19 -0
  107. shoal-0.1.0a1/src/shoal/endpoint/server.py +330 -0
  108. shoal-0.1.0a1/src/shoal/endpoint/signature.py +59 -0
  109. shoal-0.1.0a1/src/shoal/gateway/__init__.py +82 -0
  110. shoal-0.1.0a1/src/shoal/gateway/cluster.py +194 -0
  111. shoal-0.1.0a1/src/shoal/gateway/compression.py +133 -0
  112. shoal-0.1.0a1/src/shoal/gateway/connection.py +127 -0
  113. shoal-0.1.0a1/src/shoal/gateway/defaults.py +68 -0
  114. shoal-0.1.0a1/src/shoal/gateway/heartbeat.py +68 -0
  115. shoal-0.1.0a1/src/shoal/gateway/manager.py +573 -0
  116. shoal-0.1.0a1/src/shoal/gateway/ratelimit.py +51 -0
  117. shoal-0.1.0a1/src/shoal/gateway/session.py +154 -0
  118. shoal-0.1.0a1/src/shoal/gateway/shard.py +907 -0
  119. shoal-0.1.0a1/src/shoal/gateway/sharding.py +130 -0
  120. shoal-0.1.0a1/src/shoal/ipc/__init__.py +17 -0
  121. shoal-0.1.0a1/src/shoal/ipc/client.py +285 -0
  122. shoal-0.1.0a1/src/shoal/ipc/defaults.py +26 -0
  123. shoal-0.1.0a1/src/shoal/ipc/protocol.py +137 -0
  124. shoal-0.1.0a1/src/shoal/ipc/server.py +328 -0
  125. shoal-0.1.0a1/src/shoal/metrics/__init__.py +17 -0
  126. shoal-0.1.0a1/src/shoal/metrics/prometheus.py +96 -0
  127. shoal-0.1.0a1/src/shoal/models/__init__.py +102 -0
  128. shoal-0.1.0a1/src/shoal/models/activity.py +42 -0
  129. shoal-0.1.0a1/src/shoal/models/asset.py +185 -0
  130. shoal-0.1.0a1/src/shoal/models/base.py +81 -0
  131. shoal-0.1.0a1/src/shoal/models/channel.py +892 -0
  132. shoal-0.1.0a1/src/shoal/models/defaults.py +11 -0
  133. shoal-0.1.0a1/src/shoal/models/embed.py +244 -0
  134. shoal-0.1.0a1/src/shoal/models/emoji.py +261 -0
  135. shoal-0.1.0a1/src/shoal/models/guild.py +849 -0
  136. shoal-0.1.0a1/src/shoal/models/member.py +400 -0
  137. shoal-0.1.0a1/src/shoal/models/message.py +677 -0
  138. shoal-0.1.0a1/src/shoal/models/messageable.py +298 -0
  139. shoal-0.1.0a1/src/shoal/models/permissions.py +146 -0
  140. shoal-0.1.0a1/src/shoal/models/role.py +242 -0
  141. shoal-0.1.0a1/src/shoal/models/sending.py +314 -0
  142. shoal-0.1.0a1/src/shoal/models/state.py +231 -0
  143. shoal-0.1.0a1/src/shoal/models/thread.py +280 -0
  144. shoal-0.1.0a1/src/shoal/models/user.py +224 -0
  145. shoal-0.1.0a1/src/shoal/models/voice.py +464 -0
  146. shoal-0.1.0a1/src/shoal/payloads/__init__.py +1352 -0
  147. shoal-0.1.0a1/src/shoal/payloads/api.py +177 -0
  148. shoal-0.1.0a1/src/shoal/payloads/application.py +332 -0
  149. shoal-0.1.0a1/src/shoal/payloads/application_commands.py +366 -0
  150. shoal-0.1.0a1/src/shoal/payloads/application_identity.py +152 -0
  151. shoal-0.1.0a1/src/shoal/payloads/audit_log.py +200 -0
  152. shoal-0.1.0a1/src/shoal/payloads/auto_moderation.py +160 -0
  153. shoal-0.1.0a1/src/shoal/payloads/base.py +81 -0
  154. shoal-0.1.0a1/src/shoal/payloads/channel.py +541 -0
  155. shoal-0.1.0a1/src/shoal/payloads/components.py +968 -0
  156. shoal-0.1.0a1/src/shoal/payloads/emoji.py +145 -0
  157. shoal-0.1.0a1/src/shoal/payloads/errors.py +78 -0
  158. shoal-0.1.0a1/src/shoal/payloads/forum.py +58 -0
  159. shoal-0.1.0a1/src/shoal/payloads/gateway.py +268 -0
  160. shoal-0.1.0a1/src/shoal/payloads/gateway_events.py +1177 -0
  161. shoal-0.1.0a1/src/shoal/payloads/guild.py +901 -0
  162. shoal-0.1.0a1/src/shoal/payloads/guild_template.py +58 -0
  163. shoal-0.1.0a1/src/shoal/payloads/interactions.py +543 -0
  164. shoal-0.1.0a1/src/shoal/payloads/invite.py +231 -0
  165. shoal-0.1.0a1/src/shoal/payloads/lobby.py +207 -0
  166. shoal-0.1.0a1/src/shoal/payloads/message.py +897 -0
  167. shoal-0.1.0a1/src/shoal/payloads/monetization.py +182 -0
  168. shoal-0.1.0a1/src/shoal/payloads/permissions.py +149 -0
  169. shoal-0.1.0a1/src/shoal/payloads/poll.py +123 -0
  170. shoal-0.1.0a1/src/shoal/payloads/rate_limits.py +72 -0
  171. shoal-0.1.0a1/src/shoal/payloads/scheduled_event.py +244 -0
  172. shoal-0.1.0a1/src/shoal/payloads/soundboard.py +80 -0
  173. shoal-0.1.0a1/src/shoal/payloads/stage_instance.py +64 -0
  174. shoal-0.1.0a1/src/shoal/payloads/sticker.py +123 -0
  175. shoal-0.1.0a1/src/shoal/payloads/user.py +260 -0
  176. shoal-0.1.0a1/src/shoal/payloads/voice.py +76 -0
  177. shoal-0.1.0a1/src/shoal/payloads/voice_gateway.py +362 -0
  178. shoal-0.1.0a1/src/shoal/payloads/webhook.py +171 -0
  179. shoal-0.1.0a1/src/shoal/payloads/webhook_events.py +177 -0
  180. shoal-0.1.0a1/src/shoal/py.typed +0 -0
  181. shoal-0.1.0a1/src/shoal/redis/__init__.py +48 -0
  182. shoal-0.1.0a1/src/shoal/redis/base.py +29 -0
  183. shoal-0.1.0a1/src/shoal/redis/cache.py +314 -0
  184. shoal-0.1.0a1/src/shoal/redis/cluster.py +154 -0
  185. shoal-0.1.0a1/src/shoal/redis/defaults.py +32 -0
  186. shoal-0.1.0a1/src/shoal/redis/events.py +309 -0
  187. shoal-0.1.0a1/src/shoal/redis/identify.py +84 -0
  188. shoal-0.1.0a1/src/shoal/redis/jobs.py +222 -0
  189. shoal-0.1.0a1/src/shoal/redis/ratelimit.py +327 -0
  190. shoal-0.1.0a1/src/shoal/redis/sessions.py +68 -0
  191. shoal-0.1.0a1/src/shoal/rest/__init__.py +85 -0
  192. shoal-0.1.0a1/src/shoal/rest/client.py +549 -0
  193. shoal-0.1.0a1/src/shoal/rest/defaults.py +74 -0
  194. shoal-0.1.0a1/src/shoal/rest/endpoints.py +1690 -0
  195. shoal-0.1.0a1/src/shoal/rest/errors.py +181 -0
  196. shoal-0.1.0a1/src/shoal/rest/files.py +291 -0
  197. shoal-0.1.0a1/src/shoal/rest/proxy.py +330 -0
  198. shoal-0.1.0a1/src/shoal/rest/ratelimit.py +517 -0
  199. shoal-0.1.0a1/src/shoal/rest/requester.py +285 -0
  200. shoal-0.1.0a1/src/shoal/rest/resources/__init__.py +64 -0
  201. shoal-0.1.0a1/src/shoal/rest/resources/application.py +159 -0
  202. shoal-0.1.0a1/src/shoal/rest/resources/application_commands.py +249 -0
  203. shoal-0.1.0a1/src/shoal/rest/resources/application_identity.py +128 -0
  204. shoal-0.1.0a1/src/shoal/rest/resources/audit_log.py +28 -0
  205. shoal-0.1.0a1/src/shoal/rest/resources/auto_moderation.py +91 -0
  206. shoal-0.1.0a1/src/shoal/rest/resources/channel.py +394 -0
  207. shoal-0.1.0a1/src/shoal/rest/resources/emoji.py +139 -0
  208. shoal-0.1.0a1/src/shoal/rest/resources/gateway.py +34 -0
  209. shoal-0.1.0a1/src/shoal/rest/resources/guild.py +545 -0
  210. shoal-0.1.0a1/src/shoal/rest/resources/guild_template.py +78 -0
  211. shoal-0.1.0a1/src/shoal/rest/resources/interactions.py +239 -0
  212. shoal-0.1.0a1/src/shoal/rest/resources/invite.py +194 -0
  213. shoal-0.1.0a1/src/shoal/rest/resources/lobby.py +202 -0
  214. shoal-0.1.0a1/src/shoal/rest/resources/message.py +322 -0
  215. shoal-0.1.0a1/src/shoal/rest/resources/monetization.py +130 -0
  216. shoal-0.1.0a1/src/shoal/rest/resources/scheduled_event.py +113 -0
  217. shoal-0.1.0a1/src/shoal/rest/resources/soundboard.py +115 -0
  218. shoal-0.1.0a1/src/shoal/rest/resources/stage_instance.py +59 -0
  219. shoal-0.1.0a1/src/shoal/rest/resources/sticker.py +118 -0
  220. shoal-0.1.0a1/src/shoal/rest/resources/user.py +79 -0
  221. shoal-0.1.0a1/src/shoal/rest/resources/voice.py +76 -0
  222. shoal-0.1.0a1/src/shoal/rest/resources/webhook.py +307 -0
  223. shoal-0.1.0a1/src/shoal/rest/route.py +150 -0
  224. shoal-0.1.0a1/src/shoal/rest/transport.py +131 -0
  225. shoal-0.1.0a1/src/shoal/tasks/__init__.py +32 -0
  226. shoal-0.1.0a1/src/shoal/tasks/cron.py +156 -0
  227. shoal-0.1.0a1/src/shoal/tasks/defaults.py +22 -0
  228. shoal-0.1.0a1/src/shoal/tasks/jobs.py +182 -0
  229. shoal-0.1.0a1/src/shoal/tasks/loop.py +303 -0
  230. shoal-0.1.0a1/src/shoal/tasks/scheduler.py +269 -0
  231. shoal-0.1.0a1/src/shoal/ui/__init__.py +72 -0
  232. shoal-0.1.0a1/src/shoal/ui/action_row.py +243 -0
  233. shoal-0.1.0a1/src/shoal/ui/button.py +144 -0
  234. shoal-0.1.0a1/src/shoal/ui/content.py +220 -0
  235. shoal-0.1.0a1/src/shoal/ui/defaults.py +6 -0
  236. shoal-0.1.0a1/src/shoal/ui/inputs.py +317 -0
  237. shoal-0.1.0a1/src/shoal/ui/item.py +323 -0
  238. shoal-0.1.0a1/src/shoal/ui/label.py +89 -0
  239. shoal-0.1.0a1/src/shoal/ui/layout.py +247 -0
  240. shoal-0.1.0a1/src/shoal/ui/lifetime.py +106 -0
  241. shoal-0.1.0a1/src/shoal/ui/modal.py +265 -0
  242. shoal-0.1.0a1/src/shoal/ui/select.py +559 -0
  243. shoal-0.1.0a1/src/shoal/ui/text_input.py +118 -0
  244. shoal-0.1.0a1/src/shoal/ui/view.py +417 -0
  245. shoal-0.1.0a1/src/shoal/voice/__init__.py +41 -0
  246. shoal-0.1.0a1/src/shoal/voice/audio.py +296 -0
  247. shoal-0.1.0a1/src/shoal/voice/connection.py +645 -0
  248. shoal-0.1.0a1/src/shoal/voice/crypto.py +137 -0
  249. shoal-0.1.0a1/src/shoal/voice/dave.py +263 -0
  250. shoal-0.1.0a1/src/shoal/voice/defaults.py +54 -0
  251. shoal-0.1.0a1/src/shoal/voice/receive/__init__.py +38 -0
  252. shoal-0.1.0a1/src/shoal/voice/receive/opus.py +224 -0
  253. shoal-0.1.0a1/src/shoal/voice/receive/stream.py +199 -0
  254. shoal-0.1.0a1/src/shoal/voice/rtp.py +100 -0
  255. shoal-0.1.0a1/src/shoal/voice/udp.py +166 -0
  256. shoal-0.1.0a1/tests/__init__.py +1 -0
  257. shoal-0.1.0a1/tests/app_commands/__init__.py +1 -0
  258. shoal-0.1.0a1/tests/app_commands/checks.py +147 -0
  259. shoal-0.1.0a1/tests/app_commands/commands.py +483 -0
  260. shoal-0.1.0a1/tests/app_commands/errors.py +26 -0
  261. shoal-0.1.0a1/tests/app_commands/models.py +37 -0
  262. shoal-0.1.0a1/tests/app_commands/parameters.py +197 -0
  263. shoal-0.1.0a1/tests/app_commands/transformers.py +167 -0
  264. shoal-0.1.0a1/tests/app_commands/translator.py +186 -0
  265. shoal-0.1.0a1/tests/app_commands/tree.py +603 -0
  266. shoal-0.1.0a1/tests/cache/__init__.py +1 -0
  267. shoal-0.1.0a1/tests/cache/collection.py +112 -0
  268. shoal-0.1.0a1/tests/cache/fixtures/snapshot-4.msgpack +0 -0
  269. shoal-0.1.0a1/tests/cache/guild.py +19 -0
  270. shoal-0.1.0a1/tests/cache/policy.py +29 -0
  271. shoal-0.1.0a1/tests/cache/store.py +461 -0
  272. shoal-0.1.0a1/tests/cache/trim.py +97 -0
  273. shoal-0.1.0a1/tests/cache/updater.py +1025 -0
  274. shoal-0.1.0a1/tests/client/__init__.py +1 -0
  275. shoal-0.1.0a1/tests/client/client.py +1168 -0
  276. shoal-0.1.0a1/tests/client/events.py +497 -0
  277. shoal-0.1.0a1/tests/client/interaction.py +260 -0
  278. shoal-0.1.0a1/tests/client/views.py +132 -0
  279. shoal-0.1.0a1/tests/commands/__init__.py +1 -0
  280. shoal-0.1.0a1/tests/commands/bot.py +400 -0
  281. shoal-0.1.0a1/tests/commands/checks.py +176 -0
  282. shoal-0.1.0a1/tests/commands/cog.py +328 -0
  283. shoal-0.1.0a1/tests/commands/context.py +168 -0
  284. shoal-0.1.0a1/tests/commands/converters.py +339 -0
  285. shoal-0.1.0a1/tests/commands/core.py +432 -0
  286. shoal-0.1.0a1/tests/commands/errors.py +87 -0
  287. shoal-0.1.0a1/tests/commands/flags.py +228 -0
  288. shoal-0.1.0a1/tests/commands/help.py +190 -0
  289. shoal-0.1.0a1/tests/commands/hybrid.py +313 -0
  290. shoal-0.1.0a1/tests/commands/parameters.py +232 -0
  291. shoal-0.1.0a1/tests/commands/view.py +53 -0
  292. shoal-0.1.0a1/tests/compat/__init__.py +1 -0
  293. shoal-0.1.0a1/tests/compat/utils.py +93 -0
  294. shoal-0.1.0a1/tests/conventions.py +536 -0
  295. shoal-0.1.0a1/tests/core/__init__.py +1 -0
  296. shoal-0.1.0a1/tests/core/backoff.py +22 -0
  297. shoal-0.1.0a1/tests/core/clock.py +88 -0
  298. shoal-0.1.0a1/tests/core/codec.py +66 -0
  299. shoal-0.1.0a1/tests/core/cooldown.py +131 -0
  300. shoal-0.1.0a1/tests/core/dispatch.py +23 -0
  301. shoal-0.1.0a1/tests/core/enums.py +36 -0
  302. shoal-0.1.0a1/tests/core/errors.py +8 -0
  303. shoal-0.1.0a1/tests/core/files.py +33 -0
  304. shoal-0.1.0a1/tests/core/flags.py +96 -0
  305. shoal-0.1.0a1/tests/core/formatting.py +66 -0
  306. shoal-0.1.0a1/tests/core/metrics.py +48 -0
  307. shoal-0.1.0a1/tests/core/ogg.py +102 -0
  308. shoal-0.1.0a1/tests/core/snowflake.py +87 -0
  309. shoal-0.1.0a1/tests/core/tasks.py +50 -0
  310. shoal-0.1.0a1/tests/diagnostics/__init__.py +1 -0
  311. shoal-0.1.0a1/tests/diagnostics/loop.py +118 -0
  312. shoal-0.1.0a1/tests/emojis/__init__.py +1 -0
  313. shoal-0.1.0a1/tests/emojis/__main__.py +55 -0
  314. shoal-0.1.0a1/tests/emojis/registry.py +132 -0
  315. shoal-0.1.0a1/tests/emojis/sync.py +278 -0
  316. shoal-0.1.0a1/tests/endpoint/__init__.py +1 -0
  317. shoal-0.1.0a1/tests/endpoint/server.py +357 -0
  318. shoal-0.1.0a1/tests/endpoint/signature.py +45 -0
  319. shoal-0.1.0a1/tests/gateway/__init__.py +1 -0
  320. shoal-0.1.0a1/tests/gateway/cluster.py +70 -0
  321. shoal-0.1.0a1/tests/gateway/compression.py +55 -0
  322. shoal-0.1.0a1/tests/gateway/connection.py +78 -0
  323. shoal-0.1.0a1/tests/gateway/heartbeat.py +27 -0
  324. shoal-0.1.0a1/tests/gateway/manager.py +620 -0
  325. shoal-0.1.0a1/tests/gateway/ratelimit.py +20 -0
  326. shoal-0.1.0a1/tests/gateway/session.py +82 -0
  327. shoal-0.1.0a1/tests/gateway/shard.py +832 -0
  328. shoal-0.1.0a1/tests/gateway/sharding.py +64 -0
  329. shoal-0.1.0a1/tests/ipc/__init__.py +1 -0
  330. shoal-0.1.0a1/tests/ipc/client.py +98 -0
  331. shoal-0.1.0a1/tests/ipc/conftest.py +56 -0
  332. shoal-0.1.0a1/tests/ipc/protocol.py +73 -0
  333. shoal-0.1.0a1/tests/ipc/server.py +206 -0
  334. shoal-0.1.0a1/tests/metrics/__init__.py +1 -0
  335. shoal-0.1.0a1/tests/metrics/prometheus.py +49 -0
  336. shoal-0.1.0a1/tests/models/__init__.py +1 -0
  337. shoal-0.1.0a1/tests/models/activity.py +15 -0
  338. shoal-0.1.0a1/tests/models/asset.py +64 -0
  339. shoal-0.1.0a1/tests/models/base.py +36 -0
  340. shoal-0.1.0a1/tests/models/channel.py +348 -0
  341. shoal-0.1.0a1/tests/models/embed.py +99 -0
  342. shoal-0.1.0a1/tests/models/emoji.py +106 -0
  343. shoal-0.1.0a1/tests/models/guild.py +286 -0
  344. shoal-0.1.0a1/tests/models/member.py +203 -0
  345. shoal-0.1.0a1/tests/models/message.py +264 -0
  346. shoal-0.1.0a1/tests/models/messageable.py +189 -0
  347. shoal-0.1.0a1/tests/models/permissions.py +166 -0
  348. shoal-0.1.0a1/tests/models/role.py +89 -0
  349. shoal-0.1.0a1/tests/models/sending.py +172 -0
  350. shoal-0.1.0a1/tests/models/state.py +22 -0
  351. shoal-0.1.0a1/tests/models/thread.py +84 -0
  352. shoal-0.1.0a1/tests/models/user.py +99 -0
  353. shoal-0.1.0a1/tests/models/voice.py +352 -0
  354. shoal-0.1.0a1/tests/payloads/__init__.py +1 -0
  355. shoal-0.1.0a1/tests/payloads/api.py +28 -0
  356. shoal-0.1.0a1/tests/payloads/application_commands.py +129 -0
  357. shoal-0.1.0a1/tests/payloads/base.py +68 -0
  358. shoal-0.1.0a1/tests/payloads/channel.py +27 -0
  359. shoal-0.1.0a1/tests/payloads/components.py +216 -0
  360. shoal-0.1.0a1/tests/payloads/documented_examples.py +168 -0
  361. shoal-0.1.0a1/tests/payloads/emoji.py +33 -0
  362. shoal-0.1.0a1/tests/payloads/fixtures/discord_component_examples.json +667 -0
  363. shoal-0.1.0a1/tests/payloads/fixtures/discord_examples.json +481 -0
  364. shoal-0.1.0a1/tests/payloads/fixtures/discord_interaction_examples.json +561 -0
  365. shoal-0.1.0a1/tests/payloads/gateway.py +48 -0
  366. shoal-0.1.0a1/tests/payloads/gateway_events.py +146 -0
  367. shoal-0.1.0a1/tests/payloads/interactions.py +319 -0
  368. shoal-0.1.0a1/tests/payloads/message.py +32 -0
  369. shoal-0.1.0a1/tests/payloads/permissions.py +26 -0
  370. shoal-0.1.0a1/tests/payloads/rate_limits.py +11 -0
  371. shoal-0.1.0a1/tests/payloads/voice_gateway.py +55 -0
  372. shoal-0.1.0a1/tests/payloads/webhook_events.py +137 -0
  373. shoal-0.1.0a1/tests/redis/__init__.py +1 -0
  374. shoal-0.1.0a1/tests/redis/base.py +11 -0
  375. shoal-0.1.0a1/tests/redis/cache.py +149 -0
  376. shoal-0.1.0a1/tests/redis/cluster.py +79 -0
  377. shoal-0.1.0a1/tests/redis/conftest.py +67 -0
  378. shoal-0.1.0a1/tests/redis/events.py +212 -0
  379. shoal-0.1.0a1/tests/redis/identify.py +57 -0
  380. shoal-0.1.0a1/tests/redis/jobs.py +129 -0
  381. shoal-0.1.0a1/tests/redis/ratelimit.py +233 -0
  382. shoal-0.1.0a1/tests/redis/sessions.py +39 -0
  383. shoal-0.1.0a1/tests/rest/__init__.py +1 -0
  384. shoal-0.1.0a1/tests/rest/client.py +393 -0
  385. shoal-0.1.0a1/tests/rest/endpoints.py +119 -0
  386. shoal-0.1.0a1/tests/rest/errors.py +64 -0
  387. shoal-0.1.0a1/tests/rest/files.py +173 -0
  388. shoal-0.1.0a1/tests/rest/proxy.py +277 -0
  389. shoal-0.1.0a1/tests/rest/ratelimit.py +391 -0
  390. shoal-0.1.0a1/tests/rest/requester.py +106 -0
  391. shoal-0.1.0a1/tests/rest/resources/__init__.py +1 -0
  392. shoal-0.1.0a1/tests/rest/resources/application.py +135 -0
  393. shoal-0.1.0a1/tests/rest/resources/application_commands.py +159 -0
  394. shoal-0.1.0a1/tests/rest/resources/application_identity.py +121 -0
  395. shoal-0.1.0a1/tests/rest/resources/audit_log.py +71 -0
  396. shoal-0.1.0a1/tests/rest/resources/auto_moderation.py +86 -0
  397. shoal-0.1.0a1/tests/rest/resources/channel.py +321 -0
  398. shoal-0.1.0a1/tests/rest/resources/emoji.py +104 -0
  399. shoal-0.1.0a1/tests/rest/resources/gateway.py +32 -0
  400. shoal-0.1.0a1/tests/rest/resources/guild.py +632 -0
  401. shoal-0.1.0a1/tests/rest/resources/guild_template.py +74 -0
  402. shoal-0.1.0a1/tests/rest/resources/interactions.py +185 -0
  403. shoal-0.1.0a1/tests/rest/resources/invite.py +115 -0
  404. shoal-0.1.0a1/tests/rest/resources/lobby.py +191 -0
  405. shoal-0.1.0a1/tests/rest/resources/message.py +298 -0
  406. shoal-0.1.0a1/tests/rest/resources/monetization.py +111 -0
  407. shoal-0.1.0a1/tests/rest/resources/scheduled_event.py +105 -0
  408. shoal-0.1.0a1/tests/rest/resources/soundboard.py +93 -0
  409. shoal-0.1.0a1/tests/rest/resources/stage_instance.py +55 -0
  410. shoal-0.1.0a1/tests/rest/resources/sticker.py +104 -0
  411. shoal-0.1.0a1/tests/rest/resources/user.py +142 -0
  412. shoal-0.1.0a1/tests/rest/resources/voice.py +56 -0
  413. shoal-0.1.0a1/tests/rest/resources/webhook.py +204 -0
  414. shoal-0.1.0a1/tests/rest/route.py +100 -0
  415. shoal-0.1.0a1/tests/rest/transport.py +79 -0
  416. shoal-0.1.0a1/tests/scripts/__init__.py +1 -0
  417. shoal-0.1.0a1/tests/scripts/check_docs.py +81 -0
  418. shoal-0.1.0a1/tests/scripts/generate_exports.py +25 -0
  419. shoal-0.1.0a1/tests/scripts/mirror_upstream_prs.py +1159 -0
  420. shoal-0.1.0a1/tests/scripts/release_notes.py +57 -0
  421. shoal-0.1.0a1/tests/support/__init__.py +1 -0
  422. shoal-0.1.0a1/tests/support/calls.py +12 -0
  423. shoal-0.1.0a1/tests/support/client.py +101 -0
  424. shoal-0.1.0a1/tests/support/commands.py +110 -0
  425. shoal-0.1.0a1/tests/support/dispatches.py +285 -0
  426. shoal-0.1.0a1/tests/support/endpoint.py +27 -0
  427. shoal-0.1.0a1/tests/support/fakes.py +209 -0
  428. shoal-0.1.0a1/tests/support/interactions.py +223 -0
  429. shoal-0.1.0a1/tests/support/json_payloads.py +348 -0
  430. shoal-0.1.0a1/tests/support/models.py +350 -0
  431. shoal-0.1.0a1/tests/support/multipart.py +33 -0
  432. shoal-0.1.0a1/tests/support/redis.py +15 -0
  433. shoal-0.1.0a1/tests/support/rest.py +113 -0
  434. shoal-0.1.0a1/tests/support/server.py +29 -0
  435. shoal-0.1.0a1/tests/support/signing.py +12 -0
  436. shoal-0.1.0a1/tests/support/snapshot.py +66 -0
  437. shoal-0.1.0a1/tests/support/voice.py +230 -0
  438. shoal-0.1.0a1/tests/tasks/__init__.py +0 -0
  439. shoal-0.1.0a1/tests/tasks/cron.py +86 -0
  440. shoal-0.1.0a1/tests/tasks/jobs.py +39 -0
  441. shoal-0.1.0a1/tests/tasks/loop.py +222 -0
  442. shoal-0.1.0a1/tests/tasks/scheduler.py +350 -0
  443. shoal-0.1.0a1/tests/ui/__init__.py +1 -0
  444. shoal-0.1.0a1/tests/ui/action_row.py +141 -0
  445. shoal-0.1.0a1/tests/ui/button.py +76 -0
  446. shoal-0.1.0a1/tests/ui/content.py +86 -0
  447. shoal-0.1.0a1/tests/ui/inputs.py +103 -0
  448. shoal-0.1.0a1/tests/ui/item.py +26 -0
  449. shoal-0.1.0a1/tests/ui/label.py +50 -0
  450. shoal-0.1.0a1/tests/ui/layout.py +101 -0
  451. shoal-0.1.0a1/tests/ui/lifetime.py +87 -0
  452. shoal-0.1.0a1/tests/ui/modal.py +306 -0
  453. shoal-0.1.0a1/tests/ui/select.py +156 -0
  454. shoal-0.1.0a1/tests/ui/text_input.py +41 -0
  455. shoal-0.1.0a1/tests/ui/view.py +359 -0
  456. shoal-0.1.0a1/tests/voice/__init__.py +0 -0
  457. shoal-0.1.0a1/tests/voice/audio.py +204 -0
  458. shoal-0.1.0a1/tests/voice/connection.py +413 -0
  459. shoal-0.1.0a1/tests/voice/crypto.py +69 -0
  460. shoal-0.1.0a1/tests/voice/dave.py +127 -0
  461. shoal-0.1.0a1/tests/voice/receive/__init__.py +0 -0
  462. shoal-0.1.0a1/tests/voice/receive/opus.py +84 -0
  463. shoal-0.1.0a1/tests/voice/receive/stream.py +159 -0
  464. shoal-0.1.0a1/tests/voice/rtp.py +49 -0
  465. shoal-0.1.0a1/tests/voice/udp.py +108 -0
@@ -0,0 +1,28 @@
1
+ # Local assistant instructions stay local
2
+ CLAUDE.md
3
+ .claude/
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ .venv/
9
+ build/
10
+ dist/
11
+ *.egg-info/
12
+
13
+ # Tool caches and reports
14
+ .coverage
15
+ .coverage.*
16
+ coverage.xml
17
+ htmlcov/
18
+ .mypy_cache/
19
+ .pytest_cache/
20
+ .ruff_cache/
21
+ .hypothesis/
22
+ .import_linter_cache/
23
+
24
+ # Docs build
25
+ site/
26
+
27
+ # Secrets
28
+ .env
@@ -0,0 +1,241 @@
1
+ # Changelog
2
+
3
+ All notable changes are recorded here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
4
+ and the project uses [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.1.0a1] - 2026-10-29
9
+
10
+ The first pre-release.
11
+
12
+ ### Added
13
+
14
+ - Releases: pushing a `vX.Y.Z` tag publishes to PyPI through trusted publishing and creates the
15
+ GitHub release from the changelog (`.github/workflows/release.yml`).
16
+ - Content scanning flags (Discord, October 28, 2026): `AttachmentFlags` and `EmbedFlags` gain
17
+ `contains_explicit_content`, `contains_gore_content` and `contains_self_harm_content`, and
18
+ `UnfurledMediaItem.content_scan_metadata` carries `ContentScanMetadataFlags`.
19
+ - `shoal.tasks`: `tasks.loop` (intervals, times of day, hooks), cron expressions with time zones, and
20
+ `client.scheduler` for jobs at a time, after a delay or on a cron schedule, kept in memory or in
21
+ Redis (`RedisJobStore`: survives restarts, each due job claimed by one process, leased).
22
+ - Voice receive decoding: `VoiceClient.audio()` yields PCM per user, reordered, with losses
23
+ concealed, through a small libopus binding (`shoal.voice.receive`); packets from a sender Discord
24
+ has not named yet are held until it does instead of dropped.
25
+ - Per-field cache trimming: `CachePolicy(fields=...)` stores only the named fields of a kind.
26
+ - `shoal.core`: `Snowflake`, immutable `BaseFlags` bitfields, open enums that accept values Discord adds
27
+ later, the msgspec JSON and msgpack codec, the `Clock` protocol with an interruptible sleep, a shared
28
+ backoff function, owner-only atomic file writes, and the metrics catalog with the `Metrics` protocol.
29
+ - `shoal.payloads`: typed payloads, one module per Discord domain (API constants, users, permissions,
30
+ emoji, guilds, channels, messages, voice, gateway, gateway events, rate limits, errors), each linked to
31
+ its section of Discord's docs. Includes message search parameters and results, presences, voice states, reactions and
32
+ thread events, and handling for obfuscated channels (`Channel.is_obfuscated`, `Channel.visible_name`).
33
+ - `shoal.rest`: `RestClient` with bot and bearer tokens, typed routes with docs links, per-bucket and
34
+ global rate limiting, the invalid request guard, stop-after-401, and Search Guild Messages with
35
+ retries while Discord builds the index.
36
+ - Every HTTP endpoint Discord documents (237), grouped by resource with short names:
37
+ `rest.messages.create`, `rest.members.ban`, `rest.commands.overwrite_global`. The coverage page
38
+ lists the Shoal method for each Discord endpoint. Resources: `applications`, `audit_logs`,
39
+ `automod`, `channels`, `commands`, `emojis`, `entitlements`, `gateway`, `guilds` (prune, widget,
40
+ onboarding, welcome screen and the guild's other settings), `identities`, `interactions`, `invites`,
41
+ `lobbies`, `members` (bans
42
+ included), `messages`, `oauth2`, `polls`, `reactions`, `role_connections`, `roles`,
43
+ `scheduled_events`, `skus`, `soundboard`, `stage_instances`, `stickers`, `subscriptions`,
44
+ `templates`, `threads`, `users`, `voice`, `voice_states` and `webhooks`, each with its payloads. The
45
+ four endpoints Discord has deprecated or disabled are left out.
46
+ - Every gateway event Discord documents (82) is typed: `DISPATCH_PAYLOADS` maps each dispatch name
47
+ to the type its data decodes into, and the coverage page lists them. Includes Auto Moderation,
48
+ audit log entries, bans, stickers, integrations, invites, scheduled event subscribers, soundboard
49
+ sounds, polls, typing, voice channel effects and status, voice server updates and `RATE_LIMITED`
50
+ (which the shard logs). Shards send the new Request Soundboard Sounds and Request Channel Info
51
+ commands; `ShardManager.request_soundboard_sounds` splits a guild list across shards.
52
+ - The cache holds stickers, scheduled events, stage instances and soundboard sounds, follows poll
53
+ votes, scheduled event subscriber counts and the bot's thread membership, and can be read
54
+ synchronously with `CacheStore.peek` and `peek_scoped` (snapshot format 3; older snapshots are
55
+ ignored and the cache starts cold once).
56
+ - File uploads: `File` (bytes, or read off the event loop with `File.from_path`) on every endpoint
57
+ that takes files. Message-like requests describe new files in `attachments` themselves; edits only
58
+ append, so existing attachments are never dropped by accident. `image_data` and `sound_data` build
59
+ the data URIs Discord's image and sound fields take.
60
+ - Payloads that hold a credential hide it in their `repr` (`REDACTED_FIELDS`), and invite and
61
+ template codes join tokens among the path values kept out of logs.
62
+ - `shoal.gateway`: zlib-stream and zstd-stream decompression, shard routing, `max_concurrency` identify
63
+ scheduling, persistent sessions, heartbeat tracking, the send limiter, event delay tracking, a
64
+ resumable `Shard` that falls back from a failing resume URL to the main gateway, and `ShardManager`
65
+ with cluster assignment (`cluster_shard_ids`).
66
+ - `shoal.cache`: per-entity cache policies, per-shard freshness tracking, scope-indexed collections,
67
+ snapshots that line up with resumed sessions, restart strategies, and a `CacheUpdater` that applies
68
+ every cache-relevant gateway event (guilds, channels, threads, roles, emojis, members, users,
69
+ messages, reactions, presences and voice states).
70
+ - `shoal.ipc`: `EventServer` and `EventClient` to stream gateway events between processes over TCP, with
71
+ token authentication, optional TLS, event filtering and reconnects.
72
+ - Application commands: typed commands, options, choices and permissions, and the 15 live command
73
+ endpoints. Creating or editing one guild command takes its own type, without the global-only
74
+ fields.
75
+ - Components: every message and modal component, including Components V2 and the newer modal inputs,
76
+ with layout unions that make invalid nesting a type error. Received components decode lazily
77
+ (`parse_components`), so a component type newer than Shoal never breaks a message.
78
+ - Interactions: one class per interaction type and per response type, `parse_interaction` with an
79
+ `UnknownInteraction` fallback, modal submissions, resolved data, and the eight response and
80
+ follow-up endpoints. Interaction tokens never appear in logs, errors or metrics.
81
+ - `shoal.endpoint`: receive interactions over HTTP without a gateway, with Ed25519
82
+ signature checks, inline or callback answers, and handler timing against the 3-second deadline
83
+ (`pip install 'shoal[interactions]'`). `client.endpoint(public_key)` serves a client's views,
84
+ modals and commands over HTTP; the first response becomes the HTTP reply.
85
+ - `shoal.models`: guilds, members, roles, channels, threads, messages, users, emojis and embeds, with
86
+ synchronous lookups that read the cache (`guild.get_member`, `get_member_named`) and actions that
87
+ call REST (`send`, `edit`, `ban`, `add_roles`, `history`, `typing`).
88
+ - `shoal.client`: `Client` with login, sharded connect and `run`; events as models with the earlier
89
+ state read from the cache; raw events that are never dropped; readiness that does not wait for
90
+ member chunking; `wait_for`; interactions with `response` and `followup`.
91
+ - `shoal.ui`: `View`, `Modal`, `Button`, the five selects and `TextInput`, with persistent views,
92
+ timeouts and interaction checks.
93
+ - `shoal.app_commands`: `CommandTree`, slash commands, groups and context menus with options from type
94
+ hints, autocomplete, checks, cooldowns, error handlers and sync; Discord's limits are checked where
95
+ the decorator is written.
96
+ - `shoal.emojis`: the application's emojis by name (`client.app_emojis.approve`), loaded at login,
97
+ with declared names and unicode fallbacks, and a sync from a folder of images (in the bot, or
98
+ `python -m shoal.emojis sync`) that skips unchanged images and never re-uploads what it lost track of.
99
+ - `shoal.commands`: prefix and hybrid commands. `Bot` (a `Client` with commands and a slash command
100
+ tree), `Context`, commands and groups with argument conversion from type hints (`Greedy`, `*words`,
101
+ the rest of the message, converters), checks, cooldowns and `max_concurrency`, cogs with listeners,
102
+ extensions that load, unload and reload (taking their slash commands with them), a help command,
103
+ and hybrid commands and groups used both as `!name` and `/name`, with their checks run on both
104
+ sides before any option is converted.
105
+ - `shoal.core.cooldown`: one sliding-window cooldown for both command frameworks, which can be asked
106
+ how long to wait without recording a use (`get_retry_after`).
107
+ - `Client` options for large bots: `metrics`, `session_store`, `cache_snapshot` (loaded before
108
+ connecting, saved after the shards close), `identify_limiter`, `clock`, and the shards' connection
109
+ settings (`compression`, `large_threshold`, `capabilities`, ...). `commands.Bot` takes them too.
110
+ - The most used names at the top level (`shoal.Client`, `shoal.Embed`, `shoal.app_commands`), loaded
111
+ on first use, so importing one layer still loads nothing else.
112
+ - `guild.chunk()` and `guild.query_members()`: members over the gateway. Concurrent `chunk()` calls
113
+ share one request, the cache drops members who are not in the full list, and a request Discord rate
114
+ limits fails at once (`MemberRequestRateLimitedError`).
115
+ - `chunk_guilds_at_startup`: with the Server Members intent, every guild that arrives without its
116
+ member list is chunked as it streams in, one guild at a time per shard, retrying after
117
+ `RATE_LIMITED`; each shard is ready once its startup guilds are chunked, and joined guilds are
118
+ chunked too. On by default with that intent; pass `False` to chunk on demand.
119
+ - `docs/examples/`: bots to copy and run, from a one-file ping bot to a big bot with Prometheus and
120
+ Grafana, each tested against Discord.
121
+ - `shoal_events_dropped_total`: gateway payloads and dispatches dropped because they did not decode.
122
+ - Webhook events: every event Discord posts to an app's Webhook Events URL is typed
123
+ (`WebhookEventPayload`, `WEBHOOK_EVENT_PAYLOADS`), and `Endpoint(..., events=handler)` receives them,
124
+ acknowledging each at once. `client.endpoint()` emits them as events (`on_application_authorized`,
125
+ `on_entitlement_create`, `on_lobby_message_create`, ...). `shoal_http_webhook_events_total` counts
126
+ them.
127
+ - Components V2 in `shoal.ui`: `LayoutView` (the whole message), `ActionRow` (with `@row.button`
128
+ and `@row.select`), `Container`, `Section`, `TextDisplay`, `Thumbnail`, `MediaGallery`, `File` and
129
+ `Separator`; items declared on a view or container class are copied per instance. Modals take
130
+ `Label`s around any input, `TextDisplay`s, and the new `FileUpload`, `RadioGroup`, `CheckboxGroup`
131
+ and `Checkbox`. Every documented limit is checked before sending, and a Components V2 message sent
132
+ with content, embeds, a poll or stickers raises `TypeError`.
133
+ - `File.uri`: the `attachment://` reference embeds and components use for a file sent with them
134
+ (with the `SPOILER_` prefix a spoiler file is uploaded under).
135
+ - `pip install "shoal[all]"` installs every optional dependency.
136
+ - `shoal.compat`: familiar names for migrating bots (`import shoal.compat as discord`): the error
137
+ names without `Error` (`Forbidden`, `commands.MissingRequiredArgument`), renamed types under their
138
+ old names, `compat.utils` (`get`, `find`, `oauth_url`, `snowflake_time`, `as_chunks`, ...).
139
+ - Prefix commands: `FlagConverter` and `flag()` (also as hybrid options), `param()` with context
140
+ defaults (`Author`, `CurrentChannel`, `CurrentGuild`) and descriptions, `clean_content`, a converter
141
+ class for every conversion and new ones for colours, invites, stickers, scheduled events, soundboard
142
+ sounds and timestamps, `Attachment` parameters, `run_converters`, `dynamic_cooldown`,
143
+ `bot_has_any_role`, `GroupCog`, `MinimalHelpCommand`, `Paginator` and `AutoShardedBot`.
144
+ - Slash commands: `Transformer` and `Transform` (run after the checks), `Translator` and `locale_str`
145
+ (localized names and descriptions on `sync`), `dynamic_cooldown`, and `guild_install`,
146
+ `user_install`, `dm_only` and `private_channel_only`. New errors: `CommandAlreadyRegisteredError`,
147
+ `CommandLimitReachedError`, `CommandSignatureMismatchError`, `CommandSyncFailureError` and
148
+ `MissingApplicationIDError`, each a subclass of what was raised before.
149
+ - `shoal.core.formatting`: `escape_markdown`, `remove_markdown`, `escape_mentions`, `format_dt` and the
150
+ mention and timestamp patterns; `Message.clean_content`.
151
+ - `PrivilegedIntentsRequiredError`: the gateway's close for intents the application has not enabled,
152
+ saying where to enable them.
153
+ - `interaction.namespace` is typed (`OptionNamespace`), so `interaction.namespace.member` type-checks.
154
+ - `shoal.redis` (`shoal[redis]`): state shared by a bot's processes. `RedisIdentifyLimiter` and
155
+ `RedisSessionStore` for clusters, `RedisRateLimiter` (route buckets, the global limit and the
156
+ invalid request window counted across processes) and `RedisCollections` for a cache in Redis.
157
+ `CacheStore(collections=...)` takes any `CollectionFactory`.
158
+ - Events through a Redis stream (`RedisEventStream`, `RedisEventConsumer`): a broker for IPC, with
159
+ consumer groups that share the events and read again what a crashed worker left unfinished.
160
+ - IPC over Unix sockets: `EventServer(path=...)` and `EventClient.unix(path, ...)`.
161
+ - Cluster coordination: a `ShardDirectory` where every `ShardManager` reports its shards, and a
162
+ `CommandRouter` that sends a gateway command on the shard of its guild whichever cluster runs it
163
+ (`ShardManager.send_command`, `send_to_guild`); in memory and in Redis (`RedisShardDirectory`,
164
+ `RedisCommandRouter`). `Client` takes them as `shard_directory=` and `command_router=`, and
165
+ `Client.shard_manager` reaches the shards.
166
+ - Voice messages: `send(voice_message=VoiceMessage.from_ogg(...))` on channels, replies, contexts,
167
+ interaction responses and follow-ups, with the duration read from the Ogg Opus stream and
168
+ `waveform_of` for the preview; `File` carries `duration` and `waveform` into the attachment.
169
+ - Voice (`shoal[voice]`): `await channel.connect()` joins a voice channel and returns the guild's
170
+ `VoiceClient`, which plays (`OggOpusSource`, `FFmpegSource`), pauses, moves and listens
171
+ (`listen()`), and joins again by itself when the session is lost. Calls are end-to-end encrypted
172
+ with DAVE (through `davey`); packets are sealed with AES-256-GCM or XChaCha20-Poly1305. The voice
173
+ gateway (v8, buffered resume), IP discovery and the payloads are in `shoal.voice` and
174
+ `shoal.payloads.voice_gateway`; `shoal.core.ogg` reads and writes Ogg Opus.
175
+ - Resharding without downtime: `ShardManager.reshard` and `Client.reshard` start the new shard count
176
+ beside the old one and switch in every cluster once all of it is ready.
177
+ - `RestProxy`: one process holds a bot's token and rate limits, and every other process sends REST
178
+ through it with only the proxy's secret (`RestClient(None, base_url=..., proxy_secret=...)`).
179
+ `RouteTable` finds the documented route of a method and path; `rate_limit_of` reads a response's
180
+ rate limits.
181
+ - `GatewayRequiredError` and `MemberRequestRateLimitedError` are exported from `shoal.models`.
182
+ - `CollectionFactory.shared` and `CacheStore.resharded()`: a store over shared collections numbers
183
+ its writes on from the clock and keeps no snapshots; after a reshard, entries keep their freshness.
184
+ - `shoal.metrics.prometheus`: `PrometheusMetrics`, exporting every metric in the catalog.
185
+ - `shoal.diagnostics`: `LoopMonitor`, which names the code blocking the event loop while it blocks.
186
+ - Documentation site with generated API reference, guides (including writing a bot), and a Discord
187
+ API coverage page checked weekly against Discord's docs.
188
+
189
+ ### Changed
190
+
191
+ - `SessionStore.delete` takes the shard count too, and sessions are kept per shard count
192
+ (`FileSessionStore` writes `shard-<count>-<id>.json`), so a reshard's two shard counts never
193
+ overwrite each other's sessions. Session files from earlier versions are not read: each shard
194
+ identifies once after upgrading.
195
+ - A shard builds its identify once it holds its identify slot, so a presence set while it waited
196
+ goes out with it.
197
+ - `Context.reply` takes every keyword `Context.send` does.
198
+
199
+ - `ui.Item` is the base of every component; the custom ID, `row` and callbacks moved to
200
+ `ui.InteractiveItem`. `TextInput.to_component()` is the bare input (a modal puts it in a label),
201
+ and its `label` is optional inside a `ui.Label`.
202
+ - A slash command missing a required option, or sent one it does not have, raises
203
+ `CommandSignatureMismatchError` (sync the tree) instead of `TransformerError`.
204
+ - `to_partial_emoji` raises `PartialEmojiConversionFailureError` and `to_object`
205
+ `ObjectNotFoundError`; both are still `BadArgumentError`s.
206
+ - Renamed for the naming rules: `rest.channels.set_voice_status` is `edit_voice_status`
207
+ (`EditVoiceStatusParams`), `rest.guilds.edit_widget` is `edit_widget_settings`
208
+ (`EditWidgetSettingsParams`), `BulkLobbyMemberParams` is `BulkEditLobbyMemberParams` and
209
+ `EditRoleConnectionParams` is `EditCurrentRoleConnectionParams`. `Cooldown.retry_after` is
210
+ `update_rate_limit`. `GATEWAY_DOMAIN` moved to `shoal.gateway.defaults`.
211
+ - `~flags` inverts all 64 bits, so `permissions & ~other` keeps bits Discord added that Shoal does not
212
+ know yet; it used to clear them.
213
+ - A view sent with `response.send_message` answers only on the message it was sent with, and views and
214
+ modals are forgotten as soon as they stop. A user has at most five modals waiting; opening another
215
+ stops the oldest.
216
+ - An interaction counts as answered only once Discord accepted the response, so a failed response can
217
+ be sent again. Over HTTP, code after the response runs once the reply is written.
218
+ - Channels from `GUILD_CREATE` get their guild's ID when Discord leaves it out.
219
+ - Offline presences are not cached (an absent presence reads as offline).
220
+ - A close code that ends the session backs off before identifying again, as other failures do.
221
+ - `edit(attachments=...)` on messages is the list of attachments to keep, as existing `Attachment`s
222
+ and new `File`s; attachments left out are removed. Leaving it out still keeps them all.
223
+ - `max_rate_limit_wait` also bounds the wait before sending (an empty bucket, a global pause, the
224
+ invalid request guard): a longer wait raises `RateLimitedError` at once instead of sleeping.
225
+ - A 429 whose JSON body says `global` pauses every route, even without the `X-RateLimit-Global`
226
+ header.
227
+ - `Guild.get_user` is removed; use `Client.get_user`. `shoal.models.channel.resolve_guild_channel`
228
+ is `resolve_cached_channel` (it also returns DM channels).
229
+ - `Client.connect()` takes the token `login` used when none is given.
230
+ - Cache snapshots record the shard count; `load_snapshot(shard_count=...)` starts cold after
231
+ resharding. `Client(cache_snapshot=...)` passes it.
232
+ - `Identify`, `Resume` and the IPC `EventFrame` keep the token out of their repr, and `User` keeps its
233
+ email out of it.
234
+ - The IPC client resets its reconnect backoff only after a subscription lasted a minute, so a worker
235
+ dropped as too slow over and over backs off.
236
+
237
+ ### Fixed
238
+
239
+ - A cooldown's `retry_after` could round a hair above its window (60.00000000000003 for 60 s).
240
+ - `EventServer.close()` no longer hangs when the event loop hands over a connection after the
241
+ others were disconnected (seen on macOS).
shoal-0.1.0a1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shoal contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
shoal-0.1.0a1/PKG-INFO ADDED
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.5
2
+ Name: shoal
3
+ Version: 0.1.0a1
4
+ Summary: A fully typed Discord API library for Python, built to scale from one guild to millions.
5
+ Project-URL: Documentation, https://shoal.readthedocs.io/
6
+ Project-URL: Repository, https://github.com/relateful/shoal
7
+ Project-URL: Issues, https://github.com/relateful/shoal/issues
8
+ Author: Shoal contributors
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: asyncio,bot,discord,discord-api,gateway,sharding
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Topic :: Internet
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.12
25
+ Requires-Dist: aiohttp>=3.13
26
+ Requires-Dist: msgspec>=0.20
27
+ Provides-Extra: all
28
+ Requires-Dist: cryptography>=50.0.2; extra == 'all'
29
+ Requires-Dist: davey>=0.1.7; extra == 'all'
30
+ Requires-Dist: prometheus-client>=0.21; extra == 'all'
31
+ Requires-Dist: redis>=5.2; extra == 'all'
32
+ Requires-Dist: zstandard>=0.23; (python_version < '3.14') and extra == 'all'
33
+ Provides-Extra: interactions
34
+ Requires-Dist: cryptography>=50.0.2; extra == 'interactions'
35
+ Provides-Extra: prometheus
36
+ Requires-Dist: prometheus-client>=0.21; extra == 'prometheus'
37
+ Provides-Extra: redis
38
+ Requires-Dist: redis>=5.2; extra == 'redis'
39
+ Provides-Extra: voice
40
+ Requires-Dist: cryptography>=50.0.2; extra == 'voice'
41
+ Requires-Dist: davey>=0.1.7; extra == 'voice'
42
+ Provides-Extra: zstd
43
+ Requires-Dist: zstandard>=0.23; (python_version < '3.14') and extra == 'zstd'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # Shoal
47
+
48
+ A fully typed Discord API library for Python, built for bots that grow. It keeps the names you know
49
+ from discord.py (`commands.Bot`, `interaction.response.send_message`, `ui.View`, `tree.sync()`) and
50
+ fixes what makes discord.py painful once a bot gets big.
51
+
52
+ > **Status: pre-release.** Everything below is built and tested, but names can still change before
53
+ > 1.0. Install the pre-release with `pip install --pre shoal`. See the [roadmap](https://github.com/relateful/shoal/blob/main/docs/roadmap.md).
54
+
55
+ ## Why not discord.py?
56
+
57
+ discord.py is a good library for a bot in a few servers. Shoal is for bots that keep growing, and it
58
+ is just as easy for the small ones.
59
+
60
+ ### It starts fast, however many shards you run
61
+
62
+ discord.py identifies its shards one after another, about five seconds apart, and ignores the
63
+ `max_concurrency` Discord gives large bots. Shoal identifies `max_concurrency` shards at a time, as
64
+ Discord documents. At 512 shards with a concurrency of 16, that is under three minutes instead of
65
+ about 43.
66
+
67
+ ### Restarts and deploys don't cost you the gateway
68
+
69
+ - **Sessions survive restarts.** discord.py keeps the session only in memory, so every restart
70
+ identifies every shard again, and Discord sends every guild again. Shoal saves sessions (to files,
71
+ or Redis) and resumes them on the next start.
72
+ - **The cache survives restarts.** Shoal restores the cache from a snapshot, marked stale until
73
+ Discord confirms each entry, so the bot answers from a warm cache from the first second.
74
+ - **A failed resume still resumes.** discord.py falls back to the main gateway only when the resume
75
+ URL answers with a 5xx, and then starts a new session. Shoal falls back on any failure (a timeout,
76
+ a 5xx, an early close) and resumes there, as Discord recommends.
77
+ - **Change shard counts without downtime.** `bot.reshard(new_count)` connects the new shards beside
78
+ the running ones and switches over once they are all ready. discord.py has no way to do this short
79
+ of a restart.
80
+
81
+ ### More than one process is built in
82
+
83
+ discord.py leaves IPC to you. Shoal includes it:
84
+
85
+ - **Gateway and workers.** One process holds the shards and streams events (`shoal.ipc`, over TCP
86
+ or a Unix socket, or through a Redis stream). Workers run your commands and can restart as often
87
+ as you deploy.
88
+ - **Clusters.** Several processes or hosts run one bot through Redis. They share one identify
89
+ limiter, the REST rate limits, sessions, a directory of every shard, a command router (join a
90
+ voice channel from any cluster) and the cache.
91
+ - **A REST proxy.** One process holds the token and the rate limits for all the others.
92
+
93
+ ### It protects the bot from bans
94
+
95
+ - Discord bans an IP for 10,000 invalid requests in 10 minutes. discord.py does not count them.
96
+ Shoal counts the 401, 403 and 429 responses Discord counts, and holds requests back before the
97
+ limit.
98
+ - After a 401 (a revoked token), discord.py keeps sending requests that fail. Shoal stops at the
99
+ first one.
100
+
101
+ ### You can see what it is doing
102
+
103
+ - **Prometheus metrics** for gateway events and latency per shard, REST requests and rate limits by
104
+ route, the cache, IPC, voice and event loop health. discord.py has none.
105
+ - **A blocked event loop is caught as it happens.** discord.py warns once the heartbeat has been
106
+ stuck for 10 seconds, and again every 10 seconds after. Shoal's watchdog
107
+ (`async with LoopMonitor():`) reports blocks over 0.25 seconds (configurable), names the line of
108
+ your code that is blocking while it blocks, logs how long it lasted, and records it in metrics.
109
+ - **Late events are noticed.** Shoal measures how long each message and interaction took to arrive,
110
+ and warns when they arrive more than five seconds late: the sign that events queue up before your
111
+ code sees them.
112
+
113
+ ### Typed, and true to Discord's docs
114
+
115
+ - Strict pyright and mypy over the library and its tests, with `Any` banned.
116
+ - Every type and endpoint links to its section of Discord's docs, and CI checks the links. Every
117
+ HTTP endpoint Discord documents and every gateway event is covered, as the
118
+ [coverage page](https://github.com/relateful/shoal/blob/main/docs/coverage.md), generated from Discord's docs, shows.
119
+ - A field that is absent and a field that is `null` stay different (`UNSET` and `None`), so an edit
120
+ can tell "leave it" from "clear it". discord.py turns most of them into `None`.
121
+ - When Discord adds an enum value, Shoal decodes it as a real member of the enum, with its value
122
+ kept. discord.py makes an `unknown_<value>` stand-in that is not a member.
123
+ - REST is public and standalone: `RestClient` covers every endpoint as `rest.<resource>.<verb>`, with
124
+ bot or OAuth2 bearer tokens, for workers and dashboards. discord.py's `Client.http` is internal and
125
+ undocumented.
126
+
127
+ ### And what a bot needs today
128
+
129
+ - Slash, prefix and hybrid commands, cogs and extensions, views, modals and Components V2 layouts.
130
+ - Interactions over the gateway, or over HTTP with no gateway at all (signed, verified endpoint).
131
+ discord.py only has the gateway.
132
+ - Voice: play and **listen**, end-to-end encrypted (DAVE), as Opus packets or decoded PCM per user
133
+ (decoding needs libopus). discord.py can play, but receiving audio takes a third-party extension.
134
+ - Voice messages, polls, the application's own emojis synced from a folder, and member chunking as
135
+ guilds arrive (`chunk_guilds_at_startup`).
136
+ - A cache with a policy per entity (size, TTL, whether to snapshot, which fields to keep), instead
137
+ of one fixed cache.
138
+ - Loops (`tasks.loop`) and a scheduler for reminders and cron jobs that survive restarts with
139
+ Redis, without a sleeping task per job.
140
+
141
+ > **Coming from discord.py?** `import shoal.compat as discord` gives you the old names, so a bot can move
142
+ > file by file. See the [compat example](https://github.com/relateful/shoal/tree/main/docs/examples/compat/) and the
143
+ > [migration guide](https://github.com/relateful/shoal/blob/main/docs/guide/migrating-from-discord-py.md).
144
+
145
+ ## Install
146
+
147
+ Shoal needs Python 3.12 or newer. While it is in pre-release, pip needs `--pre`:
148
+
149
+ ```bash
150
+ pip install --pre shoal # the core
151
+ pip install --pre "shoal[voice]" # join voice channels, play and listen
152
+ pip install --pre "shoal[redis]" # share limits, sessions, cache, events and shards across processes
153
+ pip install --pre "shoal[prometheus]" # Prometheus metrics
154
+ pip install --pre "shoal[interactions]" # receive interactions over HTTP, without a gateway
155
+ pip install --pre "shoal[zstd]" # zstd gateway compression on Python 3.12 and 3.13
156
+ pip install --pre "shoal[all]" # everything above
157
+ ```
158
+
159
+ To work on Shoal itself, install it from a clone with `pip install -e ".[all]"`.
160
+
161
+ Each extra adds a dependency only some bots need, so the core install stays small.
162
+
163
+ ## A taste
164
+
165
+ ```python
166
+ import os
167
+
168
+ import shoal
169
+ from shoal import commands
170
+
171
+ bot = commands.Bot("!", intents=shoal.Intents.default().with_flags(message_content=True))
172
+
173
+
174
+ @bot.hybrid_command(description="Say hello")
175
+ async def hello(ctx: commands.Context, who: shoal.Member | None = None) -> None:
176
+ await ctx.send(f"Hello {(who or ctx.author).mention}!")
177
+
178
+
179
+ bot.run(os.environ["DISCORD_TOKEN"])
180
+ ```
181
+
182
+ `!hello` and `/hello` both work (sync the slash command once with `await bot.tree.sync()`).
183
+
184
+ ## Examples
185
+
186
+ [29 runnable bots](https://github.com/relateful/shoal/tree/main/docs/examples/), each with its own README and each run against Discord, from
187
+ the [smallest bot](https://github.com/relateful/shoal/tree/main/docs/examples/hello/) to [role menus](https://github.com/relateful/shoal/tree/main/docs/examples/roles/buttons/),
188
+ [tickets](https://github.com/relateful/shoal/tree/main/docs/examples/tickets/), [music](https://github.com/relateful/shoal/tree/main/docs/examples/voice/music/) and
189
+ [a big bot](https://github.com/relateful/shoal/tree/main/docs/examples/bigbot/) with Prometheus, Grafana, warm restarts and resharding, plus
190
+ [clusters](https://github.com/relateful/shoal/tree/main/docs/examples/clusters/) and [workers](https://github.com/relateful/shoal/tree/main/docs/examples/workers/).
191
+
192
+ ## Documentation
193
+
194
+ - Guides: [writing a bot](https://github.com/relateful/shoal/blob/main/docs/guide/bot.md), [commands and interactions](https://github.com/relateful/shoal/blob/main/docs/guide/interactions.md),
195
+ [caching and restarts](https://github.com/relateful/shoal/blob/main/docs/guide/caching.md), [scaling, sharding and IPC](https://github.com/relateful/shoal/blob/main/docs/guide/scaling.md),
196
+ [voice](https://github.com/relateful/shoal/blob/main/docs/guide/voice.md), [loops and scheduled jobs](https://github.com/relateful/shoal/blob/main/docs/guide/tasks.md),
197
+ [dashboards and REST](https://github.com/relateful/shoal/blob/main/docs/guide/dashboards.md),
198
+ [metrics and Grafana](https://github.com/relateful/shoal/blob/main/docs/guide/metrics.md), [diagnosing a slow bot](https://github.com/relateful/shoal/blob/main/docs/guide/diagnostics.md),
199
+ [architecture](https://github.com/relateful/shoal/blob/main/docs/guide/architecture.md),
200
+ [migrating from discord.py](https://github.com/relateful/shoal/blob/main/docs/guide/migrating-from-discord-py.md).
201
+ - The API reference is generated from the docstrings: `uv run --group docs mkdocs serve`.
202
+ - [Discord API coverage](https://github.com/relateful/shoal/blob/main/docs/coverage.md) lists every endpoint Discord documents and the Shoal method
203
+ for it, generated from Discord's docs and checked weekly.
204
+
205
+ ## Quality
206
+
207
+ - Every module with behaviour has a test module, and coverage is held above 95%.
208
+ - `tests/conventions.py` enforces the project's rules across the whole tree: docs links, ordering,
209
+ constants defined once, slots, exports, test layout.
210
+ - CI runs on Python 3.12, 3.13 and 3.14, on Linux, Windows and macOS.
211
+
212
+ ## Contributing
213
+
214
+ See [CONTRIBUTING.md](https://github.com/relateful/shoal/blob/main/CONTRIBUTING.md) for the rules, the layout and every check CI runs.
215
+
216
+ ## License
217
+
218
+ [MIT](https://github.com/relateful/shoal/blob/main/LICENSE)