simcord 3.2.2__tar.gz → 4.0.0__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 (450) hide show
  1. simcord-4.0.0/.github/workflows/dpy-master.yml +25 -0
  2. {simcord-3.2.2 → simcord-4.0.0}/.github/workflows/release.yml +1 -1
  3. {simcord-3.2.2 → simcord-4.0.0}/.github/workflows/verify.yml +2 -1
  4. {simcord-3.2.2 → simcord-4.0.0}/CHANGELOG.md +36 -0
  5. {simcord-3.2.2 → simcord-4.0.0}/CONTRIBUTING.md +11 -0
  6. {simcord-3.2.2 → simcord-4.0.0}/PKG-INFO +4 -4
  7. {simcord-3.2.2 → simcord-4.0.0}/docs/api.md +35 -0
  8. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/components.md +19 -3
  9. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/diagnostics.md +119 -2
  10. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/fixtures.md +15 -0
  11. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/permissions.md +74 -2
  12. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/testing-slash-commands.md +7 -1
  13. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/threads-reactions-dms.md +23 -2
  14. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/time-control.md +59 -3
  15. simcord-4.0.0/docs/parity-matrix.md +703 -0
  16. {simcord-3.2.2 → simcord-4.0.0}/docs/stability.md +81 -5
  17. {simcord-3.2.2 → simcord-4.0.0}/pyproject.toml +5 -4
  18. simcord-4.0.0/scripts/behavior_reference/__init__.py +4 -0
  19. simcord-4.0.0/scripts/behavior_reference/__main__.py +249 -0
  20. simcord-4.0.0/scripts/behavior_reference/gateway.py +107 -0
  21. simcord-4.0.0/scripts/behavior_reference/ledger.py +372 -0
  22. simcord-4.0.0/scripts/behavior_reference/offline.py +152 -0
  23. simcord-4.0.0/scripts/behavior_reference/oracle.py +86 -0
  24. simcord-4.0.0/scripts/behavior_reference/publish.py +86 -0
  25. simcord-4.0.0/scripts/behavior_reference/runner.py +527 -0
  26. simcord-4.0.0/scripts/behavior_reference/scenarios.py +580 -0
  27. simcord-4.0.0/scripts/behavior_reference/transport.py +388 -0
  28. simcord-4.0.0/src/simcord/_clock.py +35 -0
  29. simcord-4.0.0/src/simcord/_diagnostics.py +160 -0
  30. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/_dpy_internals.py +98 -6
  31. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/_settlement.py +13 -0
  32. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/actors.py +21 -5
  33. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/access.py +29 -0
  34. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/command_access.py +7 -0
  35. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/channel.py +2 -1
  36. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/base.py +116 -19
  37. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/channels.py +38 -2
  38. simcord-4.0.0/src/simcord/backend/ops/commands.py +227 -0
  39. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/guilds.py +45 -0
  40. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/messages.py +12 -6
  41. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/permissions.py +11 -4
  42. simcord-4.0.0/src/simcord/backend/permissions.py +134 -0
  43. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/serializers.py +7 -3
  44. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/enums.py +1 -0
  45. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/env.py +173 -35
  46. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/gateway.py +209 -1
  47. simcord-4.0.0/src/simcord/http/_access.py +14 -0
  48. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/_helpers.py +172 -8
  49. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/client.py +3 -0
  50. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/router.py +36 -8
  51. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/channels.py +127 -21
  52. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/commands.py +37 -9
  53. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/guilds.py +19 -2
  54. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/messages.py +62 -26
  55. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/reactions.py +20 -8
  56. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_actions.py +2 -1
  57. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_commands.py +19 -0
  58. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_messages.py +1 -3
  59. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_snapshot.py +4 -0
  60. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/protocol.schema.json +1 -1
  61. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/backend-diagnostics.json +7 -0
  62. simcord-4.0.0/src/simcord/pytest_plugin.py +382 -0
  63. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/types.py +2 -0
  64. {simcord-3.2.2 → simcord-4.0.0}/tests/conformance/_world.py +4 -0
  65. simcord-4.0.0/tests/conformance/test_behavior_ledger.py +22 -0
  66. {simcord-3.2.2 → simcord-4.0.0}/tests/conformance/test_field_honesty.py +4 -6
  67. {simcord-3.2.2 → simcord-4.0.0}/tests/conformance/test_field_honesty_fuzz.py +5 -6
  68. simcord-4.0.0/tests/fixtures/behavior/known_gaps.json +505 -0
  69. simcord-4.0.0/tests/fixtures/behavior/ledger.json +14206 -0
  70. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_access_boundaries.py +4 -4
  71. simcord-4.0.0/tests/integration/test_channel_behavior_fidelity.py +183 -0
  72. simcord-4.0.0/tests/integration/test_channel_obfuscation.py +317 -0
  73. simcord-4.0.0/tests/integration/test_command_fidelity.py +171 -0
  74. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_command_visibility.py +18 -44
  75. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_forum.py +110 -1
  76. simcord-4.0.0/tests/integration/test_member_fidelity.py +87 -0
  77. simcord-4.0.0/tests/integration/test_message_fidelity.py +486 -0
  78. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_message_routes.py +30 -1
  79. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_messages.py +2 -2
  80. simcord-4.0.0/tests/integration/test_native_typing.py +200 -0
  81. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_commands.py +39 -0
  82. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_fixes.py +17 -7
  83. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_message_actions.py +4 -1
  84. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_security.py +2 -1
  85. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_runtime_executor_ownership.py +2 -0
  86. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_settlement_contract.py +50 -0
  87. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_shared_clock.py +128 -3
  88. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_threads.py +21 -0
  89. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_time.py +97 -0
  90. simcord-4.0.0/tests/unit/test_behavior_reference.py +516 -0
  91. simcord-4.0.0/tests/unit/test_channel_obfuscation_wire.py +93 -0
  92. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_command_access.py +14 -0
  93. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_dpy_internals.py +3 -3
  94. simcord-4.0.0/tests/unit/test_nsfw_channel_policy.py +37 -0
  95. simcord-4.0.0/tests/unit/test_permission_diagnostics.py +317 -0
  96. simcord-4.0.0/tests/unit/test_permission_sweep.py +521 -0
  97. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_pytest_clock.py +13 -1
  98. simcord-4.0.0/tests/unit/test_sweep_error_handlers.py +159 -0
  99. {simcord-3.2.2 → simcord-4.0.0}/uv.lock +26 -50
  100. simcord-3.2.2/.github/workflows/dpy-master.yml +0 -22
  101. simcord-3.2.2/docs/parity-matrix.md +0 -416
  102. simcord-3.2.2/src/simcord/_clock.py +0 -15
  103. simcord-3.2.2/src/simcord/backend/ops/commands.py +0 -122
  104. simcord-3.2.2/src/simcord/backend/permissions.py +0 -71
  105. simcord-3.2.2/src/simcord/pytest_plugin.py +0 -93
  106. {simcord-3.2.2 → simcord-4.0.0}/.github/ISSUE_TEMPLATE/bug.md +0 -0
  107. {simcord-3.2.2 → simcord-4.0.0}/.github/ISSUE_TEMPLATE/feature.md +0 -0
  108. {simcord-3.2.2 → simcord-4.0.0}/.github/ISSUE_TEMPLATE/parity-gap.md +0 -0
  109. {simcord-3.2.2 → simcord-4.0.0}/.github/dependabot.yml +0 -0
  110. {simcord-3.2.2 → simcord-4.0.0}/.github/workflows/ci.yml +0 -0
  111. {simcord-3.2.2 → simcord-4.0.0}/.gitignore +0 -0
  112. {simcord-3.2.2 → simcord-4.0.0}/.pre-commit-config.yaml +0 -0
  113. {simcord-3.2.2 → simcord-4.0.0}/.python-version +0 -0
  114. {simcord-3.2.2 → simcord-4.0.0}/.readthedocs.yaml +0 -0
  115. {simcord-3.2.2 → simcord-4.0.0}/CODE_OF_CONDUCT.md +0 -0
  116. {simcord-3.2.2 → simcord-4.0.0}/LICENSE +0 -0
  117. {simcord-3.2.2 → simcord-4.0.0}/README.md +0 -0
  118. {simcord-3.2.2 → simcord-4.0.0}/SECURITY.md +0 -0
  119. {simcord-3.2.2 → simcord-4.0.0}/benchmarks/_helpers.py +0 -0
  120. {simcord-3.2.2 → simcord-4.0.0}/benchmarks/simulation_attribution.py +0 -0
  121. {simcord-3.2.2 → simcord-4.0.0}/benchmarks/test_dispatch_benchmarks.py +0 -0
  122. {simcord-3.2.2 → simcord-4.0.0}/benchmarks/test_perf_guards.py +0 -0
  123. {simcord-3.2.2 → simcord-4.0.0}/benchmarks/test_simulation_benchmarks.py +0 -0
  124. {simcord-3.2.2 → simcord-4.0.0}/changes/README.md +0 -0
  125. {simcord-3.2.2 → simcord-4.0.0}/docs/architecture.md +0 -0
  126. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/adventure/alpine.png +0 -0
  127. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/adventure/canyon.png +0 -0
  128. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/attachments.png +0 -0
  129. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/buttons.png +0 -0
  130. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/embeds.png +0 -0
  131. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/entities.png +0 -0
  132. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/layouts.png +0 -0
  133. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/markdown.png +0 -0
  134. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/modals.png +0 -0
  135. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/polls.png +0 -0
  136. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/replies.png +0 -0
  137. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/selects.png +0 -0
  138. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/preview/slash-commands.png +0 -0
  139. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/simcord-showcase-poll-refresh.gif +0 -0
  140. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/simcord-terminal-demo.gif +0 -0
  141. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/simcord-transparent.webp +0 -0
  142. {simcord-3.2.2 → simcord-4.0.0}/docs/assets/social-card.png +0 -0
  143. {simcord-3.2.2 → simcord-4.0.0}/docs/changelog.md +0 -0
  144. {simcord-3.2.2 → simcord-4.0.0}/docs/concepts.md +0 -0
  145. {simcord-3.2.2 → simcord-4.0.0}/docs/cookbook.md +0 -0
  146. {simcord-3.2.2 → simcord-4.0.0}/docs/faq.md +0 -0
  147. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/ai-coding-agents.md +0 -0
  148. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/discord-visual-calibration.md +0 -0
  149. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/intents.md +0 -0
  150. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/interactions.md +0 -0
  151. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/messages.md +0 -0
  152. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/mocks-vs-simulation.md +0 -0
  153. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/moderation-and-guild.md +0 -0
  154. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/monetization.md +0 -0
  155. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/preview.md +0 -0
  156. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/test-without-token.md +0 -0
  157. {simcord-3.2.2 → simcord-4.0.0}/docs/guides/testing-discord-py-bots.md +0 -0
  158. {simcord-3.2.2 → simcord-4.0.0}/docs/index.md +0 -0
  159. {simcord-3.2.2 → simcord-4.0.0}/docs/installation.md +0 -0
  160. {simcord-3.2.2 → simcord-4.0.0}/docs/llms.txt +0 -0
  161. {simcord-3.2.2 → simcord-4.0.0}/docs/performance.md +0 -0
  162. {simcord-3.2.2 → simcord-4.0.0}/docs/preview-attestation.md +0 -0
  163. {simcord-3.2.2 → simcord-4.0.0}/docs/quickstart.md +0 -0
  164. {simcord-3.2.2 → simcord-4.0.0}/docs/robots.txt +0 -0
  165. {simcord-3.2.2 → simcord-4.0.0}/examples/README.md +0 -0
  166. {simcord-3.2.2 → simcord-4.0.0}/examples/__init__.py +0 -0
  167. {simcord-3.2.2 → simcord-4.0.0}/examples/bot.py +0 -0
  168. {simcord-3.2.2 → simcord-4.0.0}/examples/conftest.py +0 -0
  169. {simcord-3.2.2 → simcord-4.0.0}/examples/preview_example.py +0 -0
  170. {simcord-3.2.2 → simcord-4.0.0}/examples/test_bot.py +0 -0
  171. {simcord-3.2.2 → simcord-4.0.0}/jsconfig.preview.json +0 -0
  172. {simcord-3.2.2 → simcord-4.0.0}/mkdocs.yml +0 -0
  173. {simcord-3.2.2 → simcord-4.0.0}/overrides/main.html +0 -0
  174. {simcord-3.2.2 → simcord-4.0.0}/package-lock.json +0 -0
  175. {simcord-3.2.2 → simcord-4.0.0}/package.json +0 -0
  176. {simcord-3.2.2 → simcord-4.0.0}/scripts/capture_discord_reference.py +0 -0
  177. {simcord-3.2.2 → simcord-4.0.0}/scripts/capture_monetization_reference.py +0 -0
  178. {simcord-3.2.2 → simcord-4.0.0}/scripts/capture_remaining_monetization.py +0 -0
  179. {simcord-3.2.2 → simcord-4.0.0}/scripts/capture_visual_reference.py +0 -0
  180. {simcord-3.2.2 → simcord-4.0.0}/scripts/check_discoverability.py +0 -0
  181. {simcord-3.2.2 → simcord-4.0.0}/scripts/check_package.py +0 -0
  182. {simcord-3.2.2 → simcord-4.0.0}/scripts/check_reference_modal_offline.py +0 -0
  183. {simcord-3.2.2 → simcord-4.0.0}/scripts/compare_visual_reference.py +0 -0
  184. {simcord-3.2.2 → simcord-4.0.0}/scripts/discord_reference_bot.py +0 -0
  185. {simcord-3.2.2 → simcord-4.0.0}/scripts/monetization_reference_checkout.py +0 -0
  186. {simcord-3.2.2 → simcord-4.0.0}/scripts/monetization_reference_config.py +0 -0
  187. {simcord-3.2.2 → simcord-4.0.0}/scripts/monetization_reference_expiry.py +0 -0
  188. {simcord-3.2.2 → simcord-4.0.0}/scripts/monetization_reference_journal.py +0 -0
  189. {simcord-3.2.2 → simcord-4.0.0}/scripts/monetization_reference_lifecycle.py +0 -0
  190. {simcord-3.2.2 → simcord-4.0.0}/scripts/monetization_reference_transport.py +0 -0
  191. {simcord-3.2.2 → simcord-4.0.0}/scripts/monetization_reference_upgrade.py +0 -0
  192. {simcord-3.2.2 → simcord-4.0.0}/scripts/preview_dogfood.py +0 -0
  193. {simcord-3.2.2 → simcord-4.0.0}/scripts/record_readme_demo.py +0 -0
  194. {simcord-3.2.2 → simcord-4.0.0}/scripts/replay_visual_reference.py +0 -0
  195. {simcord-3.2.2 → simcord-4.0.0}/scripts/validate_discord_reference_pack.py +0 -0
  196. {simcord-3.2.2 → simcord-4.0.0}/scripts/validate_monetization_reference.py +0 -0
  197. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/__init__.py +0 -0
  198. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/_modal.py +0 -0
  199. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/_payload.py +0 -0
  200. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/_runtime.py +0 -0
  201. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/asserts.py +0 -0
  202. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/__init__.py +0 -0
  203. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/_deadlines.py +0 -0
  204. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/cdn.py +0 -0
  205. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/errors.py +0 -0
  206. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/__init__.py +0 -0
  207. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/auditlog.py +0 -0
  208. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/automod.py +0 -0
  209. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/expression.py +0 -0
  210. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/guild.py +0 -0
  211. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/interaction.py +0 -0
  212. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/invite.py +0 -0
  213. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/member.py +0 -0
  214. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/message.py +0 -0
  215. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/monetization.py +0 -0
  216. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/role.py +0 -0
  217. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/scheduled_event.py +0 -0
  218. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/stage_instance.py +0 -0
  219. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/user.py +0 -0
  220. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/voice.py +0 -0
  221. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/models/webhook.py +0 -0
  222. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/__init__.py +0 -0
  223. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/audit.py +0 -0
  224. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/expressions.py +0 -0
  225. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/invites.py +0 -0
  226. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/monetization.py +0 -0
  227. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/polls.py +0 -0
  228. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/reactions.py +0 -0
  229. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/scheduled_events.py +0 -0
  230. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/voice.py +0 -0
  231. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/ops/webhooks.py +0 -0
  232. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/backend/state.py +0 -0
  233. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/builders.py +0 -0
  234. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/components.py +0 -0
  235. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/__init__.py +0 -0
  236. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/__init__.py +0 -0
  237. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/application.py +0 -0
  238. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/auditlogs.py +0 -0
  239. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/automod.py +0 -0
  240. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/expressions.py +0 -0
  241. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/interactions.py +0 -0
  242. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/invites.py +0 -0
  243. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/monetization.py +0 -0
  244. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/scheduled_events.py +0 -0
  245. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/http/routes/stage_instances.py +0 -0
  246. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/intents.py +0 -0
  247. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/interactions.py +0 -0
  248. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/monetization.py +0 -0
  249. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/parity.py +0 -0
  250. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/__init__.py +0 -0
  251. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_action_plans.py +0 -0
  252. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_assets.py +0 -0
  253. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_capture.py +0 -0
  254. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_component_projection.py +0 -0
  255. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_dependencies.py +0 -0
  256. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_diagnostics.py +0 -0
  257. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_identity.py +0 -0
  258. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_markdown.py +0 -0
  259. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_media.py +0 -0
  260. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_media_worker.py +0 -0
  261. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_pages.py +0 -0
  262. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_queries.py +0 -0
  263. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/_server.py +0 -0
  264. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/actions.js +0 -0
  265. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/app.js +0 -0
  266. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/assets.js +0 -0
  267. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/command-catalog.js +0 -0
  268. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/command-drafts.js +0 -0
  269. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/commands.js +0 -0
  270. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/components.js +0 -0
  271. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/composer.js +0 -0
  272. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/connection.js +0 -0
  273. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/diagnostics.js +0 -0
  274. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/dom.js +0 -0
  275. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/focus.js +0 -0
  276. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/OFL-1.1-Noto-Arabic.txt +0 -0
  277. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/OFL-1.1-Noto-CJK.txt +0 -0
  278. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/OFL-1.1-Noto-Devanagari.txt +0 -0
  279. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/OFL-1.1-Noto-Emoji.txt +0 -0
  280. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/OFL-1.1-Noto-Hebrew.txt +0 -0
  281. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/OFL-1.1-Noto.txt +0 -0
  282. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/manifest.json +0 -0
  283. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-color-emoji-v2.051.ttf +0 -0
  284. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-sans-arabic-v2.012.ttf +0 -0
  285. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-sans-devanagari-v2.007.ttf +0 -0
  286. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-sans-hebrew-v3.001.ttf +0 -0
  287. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-sans-latin-italic-v2.015.ttf +0 -0
  288. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-sans-latin-v2.015.ttf +0 -0
  289. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-sans-mono-v2.014.ttf +0 -0
  290. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/fonts/noto-sans-sc-v2.004.ttf +0 -0
  291. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/geometry.js +0 -0
  292. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/highlight-types.d.ts +0 -0
  293. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/index.html +0 -0
  294. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/listbox.js +0 -0
  295. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/media.js +0 -0
  296. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/messages.js +0 -0
  297. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/modal.js +0 -0
  298. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/presentation.js +0 -0
  299. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/preview-types.d.ts +0 -0
  300. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/preview.css +0 -0
  301. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/select-drafts.js +0 -0
  302. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/selects.js +0 -0
  303. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/session.js +0 -0
  304. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/surfaces.js +0 -0
  305. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/text.js +0 -0
  306. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/timeline.js +0 -0
  307. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/transport.js +0 -0
  308. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/LICENSE +0 -0
  309. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/SHA256SUMS +0 -0
  310. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/core.min.js +0 -0
  311. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/bash.min.js +0 -0
  312. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/cpp.min.js +0 -0
  313. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/csharp.min.js +0 -0
  314. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/css.min.js +0 -0
  315. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/diff.min.js +0 -0
  316. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/dockerfile.min.js +0 -0
  317. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/go.min.js +0 -0
  318. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/ini.min.js +0 -0
  319. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/java.min.js +0 -0
  320. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/javascript.min.js +0 -0
  321. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/json.min.js +0 -0
  322. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/markdown.min.js +0 -0
  323. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/python.min.js +0 -0
  324. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/rust.min.js +0 -0
  325. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/sql.min.js +0 -0
  326. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/typescript.min.js +0 -0
  327. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/xml.min.js +0 -0
  328. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/highlight/es/languages/yaml.min.js +0 -0
  329. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/lottie/5.12.2/lottie_light_canvas.min.js +0 -0
  330. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/lottie/LICENSE +0 -0
  331. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/vendor/lottie/SHA256SUMS +0 -0
  332. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/preview/static/workbench.js +0 -0
  333. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/py.typed +0 -0
  334. {simcord-3.2.2 → simcord-4.0.0}/src/simcord/results.py +0 -0
  335. {simcord-3.2.2 → simcord-4.0.0}/tests/conformance/test_route_coverage.py +0 -0
  336. {simcord-3.2.2 → simcord-4.0.0}/tests/conformance/test_serializers.py +0 -0
  337. {simcord-3.2.2 → simcord-4.0.0}/tests/conftest.py +0 -0
  338. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/__init__.py +0 -0
  339. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/commands/option_cases.json +0 -0
  340. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/__init__.py +0 -0
  341. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/catalog.py +0 -0
  342. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/coverage.json +0 -0
  343. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/measurements.json +0 -0
  344. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/moving-square.json +0 -0
  345. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/profiles.json +0 -0
  346. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/states.json +0 -0
  347. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/video.mp4 +0 -0
  348. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/preview/voice.ogg +0 -0
  349. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/sample_bot/__init__.py +0 -0
  350. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/sample_bot/events.py +0 -0
  351. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/sample_bot/general.py +0 -0
  352. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/sample_bot/interactions.py +0 -0
  353. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/sample_bot/moderation.py +0 -0
  354. {simcord-3.2.2 → simcord-4.0.0}/tests/fixtures/sample_bot/picker.py +0 -0
  355. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/preview_helpers.py +0 -0
  356. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_app_commands.py +0 -0
  357. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_application_emojis.py +0 -0
  358. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_asserts.py +0 -0
  359. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_atomic_mutations.py +0 -0
  360. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_audit_logs.py +0 -0
  361. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_automod.py +0 -0
  362. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_bot_users_and_webhooks.py +0 -0
  363. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_callback_thread_safety.py +0 -0
  364. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_channels.py +0 -0
  365. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_command_permissions.py +0 -0
  366. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_common_bot_sweep.py +0 -0
  367. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_components_v2_interactions.py +0 -0
  368. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_components_v2_messages.py +0 -0
  369. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_coverage_sweep.py +0 -0
  370. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_diagnostics.py +0 -0
  371. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_edge_cases.py +0 -0
  372. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_entitlement_journey.py +0 -0
  373. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_error_capture_and_edits.py +0 -0
  374. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_expressions.py +0 -0
  375. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_forwarding.py +0 -0
  376. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_gateway_and_automod_exempt.py +0 -0
  377. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_guilds.py +0 -0
  378. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_intents.py +0 -0
  379. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_interaction_followups.py +0 -0
  380. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_interactions.py +0 -0
  381. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_invites.py +0 -0
  382. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_lifecycle.py +0 -0
  383. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_members.py +0 -0
  384. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_message_content_rest.py +0 -0
  385. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_misc_branches.py +0 -0
  386. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_moderation.py +0 -0
  387. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_observation_contracts.py +0 -0
  388. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_polls.py +0 -0
  389. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview.py +0 -0
  390. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_action_admission.py +0 -0
  391. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_additional.py +0 -0
  392. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_browser_ownership.py +0 -0
  393. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_capture.py +0 -0
  394. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_capture_dx.py +0 -0
  395. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_channel.py +0 -0
  396. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_command_picker.py +0 -0
  397. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_components_v2.py +0 -0
  398. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_coverage.py +0 -0
  399. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_dx.py +0 -0
  400. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_edges.py +0 -0
  401. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_entity_contracts.py +0 -0
  402. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_fonts.py +0 -0
  403. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_identity.py +0 -0
  404. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_modal_contracts.py +0 -0
  405. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_navigation_contracts.py +0 -0
  406. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_observations.py +0 -0
  407. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_provenance.py +0 -0
  408. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_render_contracts.py +0 -0
  409. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_support_report.py +0 -0
  410. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_viewport_ownership.py +0 -0
  411. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_preview_workbench_contracts.py +0 -0
  412. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_reactions.py +0 -0
  413. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_reference_replay.py +0 -0
  414. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_roles_and_ordering.py +0 -0
  415. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_scheduled_events.py +0 -0
  416. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_sdk_compatibility.py +0 -0
  417. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_setup_hook_suspension.py +0 -0
  418. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_sharding.py +0 -0
  419. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_stage_instances.py +0 -0
  420. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_transport_edges.py +0 -0
  421. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_visual_reference_geometry.py +0 -0
  422. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_voice_state.py +0 -0
  423. {simcord-3.2.2 → simcord-4.0.0}/tests/integration/test_world_helpers.py +0 -0
  424. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_assert_helpers.py +0 -0
  425. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_automod_preparation.py +0 -0
  426. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_backend_lookup_errors.py +0 -0
  427. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_backend_performance_contracts.py +0 -0
  428. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_components_v2_validation.py +0 -0
  429. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_errors.py +0 -0
  430. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_intent_rules.py +0 -0
  431. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_lazy_import.py +0 -0
  432. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_local_eliminations.py +0 -0
  433. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_backend.py +0 -0
  434. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_http.py +0 -0
  435. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_reference_capture.py +0 -0
  436. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_reference_checkout.py +0 -0
  437. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_reference_expiry.py +0 -0
  438. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_reference_lifecycle.py +0 -0
  439. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_reference_upgrade.py +0 -0
  440. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_monetization_reference_validation.py +0 -0
  441. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_option_validation.py +0 -0
  442. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_parity.py +0 -0
  443. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_parity_tooling.py +0 -0
  444. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_payload_ownership.py +0 -0
  445. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_permission_validators.py +0 -0
  446. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_permissions.py +0 -0
  447. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_preview_action_specs.py +0 -0
  448. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_pytest_plugin.py +0 -0
  449. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_remaining_monetization_capture.py +0 -0
  450. {simcord-3.2.2 → simcord-4.0.0}/tests/unit/test_router.py +0 -0
@@ -0,0 +1,25 @@
1
+ # Early warning for discord.py internal changes: run the suite against the
2
+ # upstream development branch every week (and on demand).
3
+ name: discord.py master
4
+
5
+ on:
6
+ schedule:
7
+ - cron: "0 6 * * 1"
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v7
18
+ - uses: astral-sh/setup-uv@v7
19
+ - run: |
20
+ uv sync --locked --extra dev --extra screenshot
21
+ uv pip install --no-deps -U "discord.py @ git+https://github.com/Rapptz/discord.py"
22
+ # Do not let project sync replace the upstream checkout with locked discord.py.
23
+ - run: uv run --no-sync python -m playwright install --with-deps chromium
24
+ - run: uv run --no-sync python -c "import discord; print(discord.__version__)"
25
+ - run: uv run --no-sync pytest tests examples -q
@@ -89,4 +89,4 @@ jobs:
89
89
  name: dist
90
90
  path: dist/
91
91
  # Pinned to a commit SHA rather than the mutable release/v1 branch.
92
- - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
92
+ - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - uses: actions/checkout@v7
14
- - uses: actions/setup-node@v6
14
+ - uses: actions/setup-node@v7
15
15
  with:
16
16
  node-version: "24"
17
17
  cache: npm
@@ -40,6 +40,7 @@ jobs:
40
40
  - run: uv sync --locked --extra dev --extra screenshot
41
41
  - run: uv run python -m playwright install --with-deps chromium
42
42
  - run: uv run coverage run -m pytest tests examples -q
43
+ - run: uv run coverage combine
43
44
  - run: uv run coverage report
44
45
 
45
46
  docs:
@@ -4,6 +4,42 @@ This changelog is generated with [towncrier](https://towncrier.readthedocs.io/).
4
4
 
5
5
  <!-- towncrier release notes start -->
6
6
 
7
+ ## 4.0.0 (2026-11-09)
8
+
9
+ ### Features
10
+
11
+ - Add an independently recorded 184-scenario Discord behavior ledger, receipt-safe manual recorder and recovery tooling, permission-endpoint cross-checks, strict offline mismatch/stale-gap ratchets, and a generated dated evidence table. Deliberate metadata and pre-rollout REST differences remain explicitly documented rather than counted as parity.
12
+ - Add permission explanations, structured denial details on HTTP request records and transcripts, observed-permission reports, and explicit degradation assertions. Nonthrowing history and pin-list checks contribute observed flags without inventing HTTP errors. `pytest --simcord-sweep` reruns passing function-scoped tests with authentic permission revocation, reports unhandled bot exceptions/timeouts and application assertions with source locations, and distinguishes unsupported configurations from exercised coverage, including xdist runs.
13
+ - Project the bot's gateway cache through Discord channel obfuscation and omit hidden channels from guild REST channel listings. Role, overwrite and bot-membership changes refresh visibility; authoritative test handles retain their real data. Deferred gateway delivery retains emission-time access rather than authorizing old events against later permission changes. There is no legacy-visibility opt-out. See the permissions guide for the distinction between documented behavior and unrecorded field details.
14
+
15
+ ### Bug fixes
16
+
17
+ - Match independently recorded Discord behavior for message payload permissions, reply/history access, reactions, pins and crossposts; archived and locked threads, creator edit authority, voice/stage text access, mentions and silent effects. Require `pin_messages` rather than `manage_messages` for bot and simulated-user pin/unpin operations. Add announcement threads and required forum tags, enforce captured validation boundaries and error trees, reject invalid timeouts and message targets, and normalize stored application commands with atomic bulk validation. See the 3.x-to-4.0 migration guide and dated behavior ledger for deliberate gaps and bot-versus-actor boundaries.
18
+ Keep permission reads side-effect-free during sweeps, report unsupported role-sharing transitions explicitly, preserve visible voice departures without exposing hidden destinations, and round-trip forum pin flags. Reject NSFW enabling on commands and channels while keeping unrelated previews usable; observe requested permission-dependent send effects and preserve exact Administrator permission explanations.
19
+ Skipped revocation lifecycles no longer count as exercised coverage, and previously captured unhandled bot failures retain precedence over skips.
20
+
21
+ ### Miscellaneous
22
+
23
+ - Allow PyAV 17 and 18 in the optional Preview and screenshot extras, and refresh the locked media dependency to PyAV 18.1.0.
24
+ - Repair the weekly discord.py development-branch check by installing Preview/browser dependencies and retaining the upstream SDK installation instead of resynchronizing it back to the locked release.
25
+
26
+
27
+ ## 3.3.0 (2026-11-02)
28
+
29
+ ### Features
30
+
31
+ - Add the aware `start_time` option to `Env`, `simcord.run` and pytest markers, applying a validated UTC origin before fixtures and startup while preserving the deterministic default and elapsed time across bot restarts. Application and SQL clocks still require explicit application clock injection.
32
+
33
+ ### Bug fixes
34
+
35
+ - Recognize discord.py's native typing interval as a virtual-only wait while preserving the initial typing request, every five-second refresh, HTTP work, and normal cancellation and lifecycle cleanup.
36
+ - Deliver already-real-due native asyncio cancellation deadlines through blocked input during automatic clock advancement, preserving chronological resource/timer processing, strict settlement, executor ownership, and undelivered settlement failures.
37
+
38
+ ### Documentation
39
+
40
+ - Clarify the guild-versus-DM handle scopes enforced since 3.2.1, with actionable setup errors and migration examples: use `member.user.send_dm(...)` for DM messages and `member.user` for DM slash commands, autocomplete, components, and modals. The existing `member.send_dm(...)` convenience remains supported; accepted and rejected actions are unchanged.
41
+
42
+
7
43
  ## 3.2.2 (2026-10-31)
8
44
 
9
45
  ### Bug fixes
@@ -16,6 +16,13 @@ uv run ruff check src tests examples benchmarks scripts && uv run ruff format --
16
16
  uv run pyright src
17
17
  ```
18
18
 
19
+ Coverage includes the pytest plugin's real subprocess runs. To measure the full
20
+ suite, run `uv run coverage run -m pytest tests examples`, then
21
+ `uv run coverage combine` before `uv run coverage report`. The
22
+ [native subprocess patch](https://coverage.readthedocs.io/en/stable/subprocess.html)
23
+ requires coverage.py 7.10 or newer; do not replace those lifecycle checks with
24
+ mocked pytest calls or lower the coverage ratchet.
25
+
19
26
  The Python hooks run ruff on commit and pyright on push using the project's
20
27
  pinned tools. Run the frontend check below separately; passing Python hooks
21
28
  does not replace the JavaScript CI gate.
@@ -32,6 +39,10 @@ uv sync --extra dev --extra screenshot # matches the CI lint job
32
39
  uv run playwright install --with-deps chromium # needed once for browser capture tests + the example
33
40
  ```
34
41
 
42
+ The weekly `discord.py master` workflow installs the same screenshot dependencies
43
+ and Chromium before exercising upstream discord.py. After installing that checkout,
44
+ use `uv run --no-sync` so the project lock does not replace it with the released SDK.
45
+
35
46
  Frontend contributors also need Node 24:
36
47
 
37
48
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simcord
3
- Version: 3.2.2
3
+ Version: 4.0.0
4
4
  Summary: Offline discord.py bot testing with pytest. No network, token, or test server.
5
5
  Project-URL: Homepage, https://github.com/SilentHacks/simcord
6
6
  Project-URL: Documentation, https://simcord.readthedocs.io/
@@ -29,7 +29,7 @@ Requires-Dist: pytest-asyncio>=0.24; extra == 'bench'
29
29
  Requires-Dist: pytest-benchmark>=4; extra == 'bench'
30
30
  Requires-Dist: pytest>=8; extra == 'bench'
31
31
  Provides-Extra: dev
32
- Requires-Dist: coverage[toml]>=7; extra == 'dev'
32
+ Requires-Dist: coverage[toml]>=7.10; extra == 'dev'
33
33
  Requires-Dist: hypothesis>=6; extra == 'dev'
34
34
  Requires-Dist: jsonschema>=4; extra == 'dev'
35
35
  Requires-Dist: pre-commit>=3; extra == 'dev'
@@ -45,7 +45,7 @@ Requires-Dist: mkdocs-material>=9; extra == 'docs'
45
45
  Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
46
46
  Provides-Extra: preview
47
47
  Requires-Dist: aiohttp>=3.9; extra == 'preview'
48
- Requires-Dist: av<17,>=14; extra == 'preview'
48
+ Requires-Dist: av<19,>=14; extra == 'preview'
49
49
  Requires-Dist: linkify-it-py>=2.0.3; extra == 'preview'
50
50
  Requires-Dist: markdown-it-py>=4.1; extra == 'preview'
51
51
  Requires-Dist: pillow>=10; extra == 'preview'
@@ -56,7 +56,7 @@ Requires-Dist: pytest-asyncio>=0.24; extra == 'pytest'
56
56
  Requires-Dist: pytest>=8; extra == 'pytest'
57
57
  Provides-Extra: screenshot
58
58
  Requires-Dist: aiohttp>=3.9; extra == 'screenshot'
59
- Requires-Dist: av<17,>=14; extra == 'screenshot'
59
+ Requires-Dist: av<19,>=14; extra == 'screenshot'
60
60
  Requires-Dist: linkify-it-py>=2.0.3; extra == 'screenshot'
61
61
  Requires-Dist: markdown-it-py>=4.1; extra == 'screenshot'
62
62
  Requires-Dist: pillow>=10; extra == 'screenshot'
@@ -19,6 +19,17 @@ top-level `simcord` package unless a module-qualified path is shown.
19
19
 
20
20
  ::: simcord.Env
21
21
 
22
+ ### `Env(start_time=None)`
23
+
24
+ Sets the immutable initial virtual UTC instant before backend IDs, fixtures and startup.
25
+ `None` preserves 2026-01-01 UTC. Supply an aware `datetime`; offsets normalize to UTC.
26
+ Invalid values raise `SetupError`. Supported UTC instants are at least
27
+ 2015-01-01 00:00:00 and strictly before 2154-05-15 07:35:11.104.
28
+ Elapsed time advances normally and survives bot restarts. `simcord.run` and
29
+ `@pytest.mark.simcord` accept the same option. This does not synchronize application or
30
+ SQL wall clocks; bind application time seams to `env.now` before startup when needed.
31
+ See [time control](guides/time-control.md#choosing-the-initial-utc-instant).
32
+
22
33
  ### `Env(settle_timeout=5.0)`
23
34
 
24
35
  `settle_timeout` is the default maximum time spent joining runnable bot work. A direct
@@ -47,6 +58,30 @@ encoded headers; uploaded files are not captured. The `json` field preserves the
47
58
  JSON-compatible shape, including top-level arrays. `params` and `json` are detached deep
48
59
  snapshots, so later mutations to source containers do not alter a record.
49
60
 
61
+ Permission refusals additionally populate `denial` with structured authorization
62
+ detail; successful requests leave it `None`. This diagnostic is separate from the
63
+ authentic Discord error body.
64
+
65
+ ### Permission diagnostics
66
+
67
+ `Env.explain_permissions(actor, channel)` returns a JSON-friendly mapping with
68
+ `actor_id`, `guild_id`, the requested `channel_id`, `permission_channel_id`,
69
+ ordered `steps`, and final integer `effective` bits. Each step includes `source`,
70
+ `before`, `allow`, `deny` and `after`, satisfying `after = (before & ~deny) | allow`;
71
+ roles and overwrites include their target identifiers. Administrator resets to the
72
+ canonical all-permissions mask, discarding unknown bits. Thread explanations resolve
73
+ inherited parent overwrites.
74
+
75
+ `Env.permission_checks` is a detached snapshot of bot authorization checks.
76
+ `Env.observed_permissions()` returns a `discord.Permissions` union of exercised
77
+ flags, including denied checks and nonthrowing checks for requested send effects:
78
+ enabled TTS, known external emoji references, and policy-permitted mass mentions
79
+ or unmentionable role pings. Refused effects still degrade successfully and do not
80
+ populate a successful request's `denial`. Plain text, suppressed mentions, and DMs
81
+ do not add irrelevant guild effect flags. This reports exercised paths, not the
82
+ permissions needed by every possible bot execution. See
83
+ [permission diagnostics](guides/diagnostics.md#permission-diagnostics).
84
+
50
85
  ## Builders
51
86
 
52
87
  Synchronous, omnipotent handles for arranging the virtual Discord. Returned by `env`/`guild`
@@ -26,9 +26,25 @@ inspect remote media dimensions or contents.
26
26
 
27
27
  Nested buttons and selects dispatch through their real discord.py callbacks. The same
28
28
  `click`, `select`, and `submit_modal` methods are available on `UserHandle` for component
29
- flows in DMs. Use `component_text=` and `component_custom_id=` with `assert_message`,
30
- `assert_sent`, or `assert_responded` to inspect layouts without treating TextDisplay text
31
- as legacy message content.
29
+ flows in DMs. Since 3.2.1, use `member.user` for these DM actions rather than the
30
+ guild-scoped `MemberActor`:
31
+
32
+ ```python
33
+ opened = await alice.user.click(dm_message, custom_id="feedback:open")
34
+ submitted = await alice.user.submit_modal(opened, {"comment": "Great bot"})
35
+ # For a DM select menu:
36
+ selected = await alice.user.select(dm_menu, ["green"], custom_id="color")
37
+ ```
38
+
39
+ DM callbacks receive SDK interactions with `interaction.guild is None`. Keep using
40
+ `alice.click(...)`, `alice.select(...)`, and `alice.submit_modal(...)` for interactions in
41
+ `alice`'s guild. A `UserHandle` cannot act on guild messages; use a `MemberActor` belonging
42
+ to the target guild instead. These are SimCord handle scopes, not limitations on what the
43
+ same human can do in Discord.
44
+
45
+ Use `component_text=` and `component_custom_id=` with `assert_message`, `assert_sent`,
46
+ or `assert_responded` to inspect layouts without treating TextDisplay text as legacy
47
+ message content.
32
48
 
33
49
  ## Clicking buttons
34
50
 
@@ -131,14 +131,131 @@ assert len(posts) == 1
131
131
  assert posts[0].json["content"] == "Pong!"
132
132
  ```
133
133
 
134
- Each record exposes `method`, `path`, `params`, `json`, and `reason`. These values are the
135
- arguments discord.py passed to its transport: query parameters are not wire-normalized
134
+ Each record exposes `method`, `path`, `params`, `json`, `reason`, and `denial`.
135
+ Transport arguments retain discord.py's values: query parameters are not wire-normalized
136
136
  strings, and `reason` is not an encoded HTTP header value. Uploaded files are not captured.
137
137
  The `json` field preserves every supplied JSON-compatible shape, including top-level arrays.
138
138
  `params` and `json` are detached deep snapshots, so later mutations to source containers do
139
139
  not alter a record. The request record is appended before fault injection and route handling,
140
140
  so failed and unimplemented requests remain observable.
141
141
 
142
+ ## Permission diagnostics
143
+
144
+ Explain effective permission bits independently of a failed operation:
145
+
146
+ ```python
147
+ trace = simcord_env.explain_permissions(simcord_env.bot.user, channel)
148
+ effective = discord.Permissions(trace["effective"])
149
+ for step in trace["steps"]:
150
+ print(step["source"], step["before"], step["after"])
151
+ ```
152
+
153
+ The ordered trace covers base roles, owner/Administrator bypasses, `@everyone`,
154
+ aggregated role and member overwrites, and active timeout masking. Threads use
155
+ their parent's overwrites. This explains permission bits, not every REST policy:
156
+ thread state, channel access and role hierarchy can independently refuse an action.
157
+
158
+ Bot refusals add `DENIAL` entries to the transcript and structured `denial` detail
159
+ to the failing HTTP request record. Discord exception text and error JSON remain
160
+ unchanged. `env.permission_checks` returns detached check records with actor,
161
+ guild and channel IDs, required/missing flags, effective bits, outcome and reason.
162
+ Non-permission authorization refusals, such as role hierarchy, are attributed too.
163
+
164
+ ```python
165
+ observed = simcord_env.observed_permissions()
166
+ print([name for name, used in observed if used])
167
+ ```
168
+
169
+ Observed flags include refused checks. They describe only paths this environment
170
+ exercised, not a least-privilege guarantee for your entire bot.
171
+ Implicit guild-channel access contributes `view_channel` (reported as
172
+ `read_messages` when iterating discord.py's native permissions). DM checks do not
173
+ invent guild permission requirements.
174
+ History and pin listings also record their nonthrowing `read_message_history`
175
+ checks. Without that permission, the listings still succeed with an empty result;
176
+ the check is refused, but no HTTP exception or request denial is invented.
177
+
178
+ ### Silent degradations
179
+
180
+ Some successful requests lose an effect without a Discord error:
181
+
182
+ ```python
183
+ for degradation in simcord_env.degradations:
184
+ print(degradation["kind"])
185
+ simcord_env.assert_no_degradations() # opt-in assertion
186
+ ```
187
+
188
+ Records are detached snapshots. Modeled ordinary-send cases are missing TTS
189
+ permission (`tts_permission_dropped`), unmentionable role pings
190
+ (`unmentionable_role_ping_dropped`), ignored ephemeral flags
191
+ (`normal_message_ephemeral_ignored`) and known external custom emojis without
192
+ the external-emoji permission (`external_emoji_permission_stripped`).
193
+ An explicit allowed-mentions policy suppressing a ping is intentional, not a
194
+ degradation. Unknown emoji IDs are not guessed or rewritten. Successful TTS
195
+ creates retain `tts=True` in the create result/event but ordinary bot messages
196
+ read later have `tts=False`, matching the captured behavior.
197
+
198
+ ### Suite reports and failure sweeps
199
+
200
+ ```bash
201
+ pytest --simcord-observed-permissions
202
+ pytest --simcord-sweep
203
+ ```
204
+
205
+ The observed-permissions option aggregates exercised flags across environments
206
+ and xdist workers. The sweep first runs each test normally, then reruns passing
207
+ tests with fresh function-scoped fixtures once per observed permission.
208
+ It discovers both `simcord_env` and environments created directly or through
209
+ `simcord.run`.
210
+
211
+ Revocation changes real bot-exclusive roles and bot member overwrites, not HTTP
212
+ fault responses. REST enforcement, interaction `app_permissions` and cached
213
+ `channel.permissions_for(guild.me)` therefore agree. A channel overwrite can
214
+ revoke a channel permission while `guild.me.guild_permissions` correctly retains
215
+ the guild-level grant.
216
+
217
+ Existing worlds are projected once when registered. Canonical builder and REST
218
+ permission mutations project the affected guild/channel before returning models,
219
+ serializing responses or publishing events, including silent construction.
220
+ Permission reads never project, mutate state or emit events. Raw model/dictionary
221
+ permission writes are unsupported; use the builder or backend mutation methods.
222
+
223
+ Test-driver assertions that no longer hold under revocation are ignored.
224
+ Assertions raised by application code are findings, like other unhandled bot
225
+ exceptions and timeouts, and are reported with the permission and source location;
226
+ findings make pytest exit unsuccessfully. Handled refusals are not findings.
227
+ Baseline failures remain ordinary test failures and are not swept.
228
+
229
+ Successful custom event, prefix-command, application-command, View and Modal
230
+ error handlers consume their errors for sweep purposes. A handler that raises
231
+ or reraises still produces a finding. Routed errors remain in `Env.errors` for
232
+ normal diagnostics and its existing assertion policy; the sweep distinguishes
233
+ those observations from unhandled runtime failures.
234
+
235
+ Env-owned callback/task errors and settlement timeouts carry bot-ownership
236
+ evidence. Direct product calls can also produce findings when their traceback
237
+ reaches application code under pytest's root, outside the configured `testpaths`.
238
+ Keep test helpers under those test paths: a direct SDK helper refusal, or a
239
+ failure without application ownership evidence, is unsupported rather than an
240
+ invented bot failure. Broader external application code is still covered when
241
+ executed as Env-owned bot work. A baseline using only permissionless interaction
242
+ callbacks may observe no flags; that is not permission-revocation coverage.
243
+
244
+ The report separates exercised and unsupported cases. Broader-scoped user
245
+ fixtures, reused environments, bot-owned guilds, Administrator bypasses,
246
+ and guild-scoped grants that cannot be removed without changing other members
247
+ are unsupported. Removing Administrator could revoke unrelated permissions, so
248
+ it is never presented as a faithful one-flag sweep. Sharing a role after its
249
+ guild-scoped grant was suppressed restores that owned bit and stops the rerun
250
+ as unsupported; pytest still tears down its fixtures. Explicit later permission
251
+ edits relinquish ownership of the old suppressed bit; unrelated role/overwrite
252
+ edits are retained. No roles are cloned and no whole-world rollback is used.
253
+ Harness/setup failures are unsupported rather than invented bot failures.
254
+ Skipped revocation setup or test calls are unsupported, not exercised coverage;
255
+ any already-captured unhandled bot errors remain findings.
256
+ An unsupported case is not evidence that your bot handles revocation.
257
+ Missing xdist worker reports explicitly mark coverage incomplete.
258
+
142
259
  ## Injecting API failures
143
260
 
144
261
  Real bots have to survive Discord hiccups. `env.inject_error` makes matching REST calls fail
@@ -100,6 +100,7 @@ Both `simcord.run(bot, **options)` and the underlying `Env` accept these keyword
100
100
  | `approved_intents` | `None` (all approved) | Simulates developer-portal privileged-intent toggles and REST Message Content access, independently of the Gateway request bit. Explicit `Intents.none()` denies approval. |
101
101
  | `shard_count` | client setting | Supplies the Get Gateway Bot recommendation when an `AutoShardedClient` does not configure `shard_count` itself. |
102
102
  | `settle_timeout` | `5.0` seconds | Default maximum time for joining runnable bot work; a direct settle `timeout=` overrides it. |
103
+ | `start_time` | `None` (2026-01-01 UTC) | Aware initial UTC instant, normalized from any offset before fixtures/startup; see [time control](time-control.md#choosing-the-initial-utc-instant) for supported bounds and application clock wiring. |
103
104
 
104
105
  `settle_timeout` does not change startup's fixed five-second READY settlement budget.
105
106
  Restart drains existing work using `settle_timeout`, then gets a fresh fixed five-second
@@ -181,6 +182,20 @@ async def test_command_logic_in_isolation(simcord_env):
181
182
  ...
182
183
  ```
183
184
 
185
+ The same marker accepts an initial clock origin:
186
+
187
+ ```python
188
+ from datetime import UTC, datetime
189
+
190
+ @pytest.mark.simcord(start_time=datetime(2030, 4, 5, tzinfo=UTC))
191
+ async def test_future_entitlement(simcord_env):
192
+ assert simcord_env.now() == datetime(2030, 4, 5, tzinfo=UTC)
193
+ ```
194
+
195
+ For application clock injection needed by `setup_hook`, construct `Env` and bind
196
+ `env.now` before manual startup in your fixture; the
197
+ [time-control example](time-control.md#choosing-the-initial-utc-instant) includes teardown.
198
+
184
199
  In 3.0, error snapshots with prefix acknowledgement and detached mutable result
185
200
  payloads are unconditional. Remove the temporary 2.3 `future_behavior` marker/run
186
201
  option; see [Migration](../stability.md#migrating-from-23-to-30).
@@ -61,8 +61,12 @@ state and cross-guild role/member targets are rejected before channel creation.
61
61
  (`50013`). It also can't delete the `@everyone` role.
62
62
  - **Editing others' messages** — the bot can't edit another user's message (`50005`).
63
63
  - **Timeouts** — a timed-out member loses everything except viewing and reading history.
64
- - **Reactions** — `add_reactions` is required to introduce a new emoji, not to join an
65
- existing reaction. Both require access to the message and its history.
64
+ - **Bot reactions** — `add_reactions` is required to introduce a new emoji, not
65
+ to join an existing reaction or remove your own. Read Message History is not
66
+ required on these bot REST paths. Simulated users and Preview retain their
67
+ separate history-access checks.
68
+ - **Pins** — pinning and unpinning require `pin_messages`, for both bots and
69
+ simulated users. `manage_messages` does not grant this permission.
66
70
 
67
71
  ```python
68
72
  async def test_cannot_ban_higher_role(simcord_env):
@@ -81,6 +85,74 @@ async def test_cannot_ban_higher_role(simcord_env):
81
85
  assert guild.get_ban(boss) is None
82
86
  ```
83
87
 
88
+ ## Bot-visible channels
89
+
90
+ The bot's cache follows Discord's [channel-obfuscation contract](https://docs.discord.com/developers/resources/channel#channel-object-obfuscated-channels):
91
+ channels it cannot view have the name `___hidden___`, the obfuscated flag, neutral
92
+ sensitive fields and a synthetic `@everyone` overwrite denying `view_channel`.
93
+ Guild channel listings omit those channels; fetching one directly is denied.
94
+ Role and overwrite changes update this projection, including threads under a
95
+ hidden parent. Categories remain visible when a child is visible, and a connected
96
+ voice channel remains visible to the bot.
97
+
98
+ Builders, actors and backend state retain their authoritative data. Use a
99
+ `ChannelHandle` to inspect test setup, not the bot cache. Interaction wire payloads
100
+ remain unobfuscated; discord.py 2.7.1 may nevertheless resolve `Interaction.channel`
101
+ from its existing cache, and does not expose the wire obfuscation flag on every
102
+ channel class.
103
+
104
+ Our [live evidence](../parity-matrix.md#behavior-evidence) includes opt-in gateway
105
+ captures for text, voice, forum and category channels. Structural fields such as
106
+ slowmode and voice limits are retained. REST list filtering follows Discord's
107
+ documented mandatory behavior; it was not yet enforced in the dated live capture
108
+ and is an explicit known gap, not a claim of matching that pre-rollout response.
109
+ Uncaptured neutral-field details and category/voice visibility edge rules remain
110
+ documented simulator policy.
111
+
112
+ Visible voice occupants are projected per connection at event admission, including
113
+ events waiting for FIFO parser delivery. Moving into a hidden channel (or losing
114
+ visibility while stationary) clears the SDK voice cache with a departure built
115
+ only from the last authorized snapshot and `channel_id: null`. Hidden-to-hidden
116
+ movement discloses nothing; a reveal restores current authorized occupants.
117
+ This departure wire shape is an intentional privacy/cache-safety inference, not
118
+ a recorded live Discord observation. Canonical backend voice state is unchanged,
119
+ and normal voice intent and shard routing still apply.
120
+
121
+ ## Bot REST behavior
122
+
123
+ - Embeds, uploads and polls on ordinary messages require `embed_links`,
124
+ `attach_files` and `send_polls`. Replies require history (`160002`); fetching a
125
+ message, pinning and crossposting also check history. Listing pins without
126
+ history succeeds with an empty list.
127
+ - Voice and stage text actions require `connect` (`50001`). Pinning there is
128
+ refused with `50019`, even when `pin_messages` is granted.
129
+ - Archived-thread edit, delete, bulk delete, reaction and pin actions fail with
130
+ `50083`. Sending automatically unarchives an unlocked thread after validating
131
+ the payload. Locked sends need `manage_threads`; locked unarchive without it
132
+ fails with `160005`.
133
+ - Thread creators may rename, archive and lock their threads; other members
134
+ need `manage_threads`. Edits and unarchive also need
135
+ `send_messages_in_threads`. **Deleting requires `manage_threads`, including
136
+ for the creator**, as confirmed by our own recording. Leaving needs no view
137
+ access.
138
+ - Forum thread pinning and unpinning require `manage_threads`, even for the
139
+ creator. The pinned flag is returned consistently by edits, fetches, gateway
140
+ events and active/archived listings. Pinning non-forum threads and changing
141
+ unsupported thread flag bits are rejected before mutation; existing unrelated
142
+ bits are preserved.
143
+ - NSFW channels are not modeled: channel creation/edit and thread creation reject
144
+ enabling NSFW. Omitted or literal `False` metadata is accepted inertly; there is
145
+ no way to enable NSFW.
146
+ - `AllowedMentions` controls the returned mention arrays. Missing TTS permission,
147
+ unmentionable role pings, ignored ephemeral flags on normal sends and denied
148
+ known external custom emojis are modeled as successful requests with reduced
149
+ effects, reported through [`env.degradations`](diagnostics.md#silent-degradations).
150
+
151
+ These bot recordings do not establish the rules for simulated human actors.
152
+ Actor behavior is unchanged except for the documented pin-permission split and
153
+ the docs-based locked-thread send guard.
154
+
155
+
84
156
  ## Application command visibility
85
157
 
86
158
  Before an actor dispatches a slash command, autocomplete request, or context menu, SimCord checks whether that user could see the command in that guild and channel. This is separate from callback checks such as `@app_commands.checks.has_permissions(...)`; hidden commands raise `SetupError` instead of dispatching an interaction. See [slash command visibility](testing-slash-commands.md#command-visibility) for scope/context rules, permission overrides, reason codes, and fixture setup.
@@ -90,13 +90,19 @@ uploaded = await alice.slash(channel, "upload", file=("photo.png", b"image bytes
90
90
 
91
91
  ## Bot-DM slash commands and visible command lists
92
92
 
93
- `UserHandle.slash(name, **options)` and `UserHandle.autocomplete(name, option, value, **filled)` invoke global commands whose contexts allow `BOT_DM`. Their interactions have `context == 1`:
93
+ `UserHandle.slash(name, **options)` and `UserHandle.autocomplete(name, option, value, **filled)` invoke global commands whose contexts allow `BOT_DM`. Their interactions have `context == 1`, and the SDK callback receives `interaction.guild is None`:
94
94
 
95
95
  ```python
96
96
  user = simcord_env.create_user("alice")
97
97
  await user.slash("help") # The bot's callback receives interaction.context == 1.
98
98
  ```
99
99
 
100
+ For an existing guild member, use `await member.user.slash("help")` or
101
+ `await member.user.autocomplete("tag", "name", "py")` for DM commands. Since 3.2.1,
102
+ passing `member.user.dm_channel` to `member.slash(...)` or `member.autocomplete(...)`
103
+ is rejected: `MemberActor` is guild-scoped. Keep `member.slash(channel, ...)` and
104
+ `member.autocomplete(channel, ...)` for its guild.
105
+
100
106
  Use `member.available_commands(channel)` or `user.available_commands()` to get a tuple of leaf invocation strings that are visible there and accepted by `slash()` (for example, `"config set"`):
101
107
 
102
108
  ```python
@@ -70,11 +70,32 @@ reply = alice.user.dm_channel.history() # what the bot DM'd back
70
70
  assert reply[-1].content.startswith("Hi")
71
71
  ```
72
72
 
73
+ `alice` above is a `MemberActor`; `alice.send_dm(...)` remains a supported convenience
74
+ that delegates to `alice.user.send_dm(...)`. Other guild-scoped actor methods do not
75
+ target DMs. Use the `UserHandle` (`alice.user`) for DM messages, commands, components,
76
+ and modals; keep `alice` for operations in its guild.
77
+
78
+ Since 3.2.1, migrate calls that passed a DM channel to a guild actor:
79
+
80
+ ```python
81
+ # Before 3.2.1: a guild actor was used to target a DM.
82
+ await alice.send(alice.user.dm_channel, "hello bot")
83
+
84
+ # Now: send through the user's DM-scoped handle.
85
+ await alice.user.send_dm("hello bot")
86
+ # This existing convenience also remains supported:
87
+ await alice.send_dm("hello bot")
88
+
89
+ # Guild operations still use the guild member.
90
+ await alice.send(channel, "hello guild")
91
+ ```
92
+
73
93
  `alice.user.dm_channel` is the user's DM channel with the bot; `.history()` returns the
74
94
  conversation as real `discord.Message` objects. A `UserHandle` can also invoke global slash
75
95
  commands and autocomplete in that DM when their command contexts allow `BOT_DM`; these
76
- interactions carry `context == 1`. Use `user.available_commands()` to list the slash invocations
77
- visible there. See [slash command visibility and DM commands](testing-slash-commands.md#bot-dm-slash-commands-and-visible-command-lists).
96
+ interactions carry `context == 1` and the SDK callback receives `interaction.guild is None`.
97
+ Use `user.available_commands()` to list the slash invocations visible there. See
98
+ [slash command visibility and DM commands](testing-slash-commands.md#bot-dm-slash-commands-and-visible-command-lists).
78
99
 
79
100
  Guild actors can edit/delete their messages and pin with `set_pinned`. In the user's bot DM,
80
101
  `UserHandle` supports their own message edits/deletes, desired reactions, and poll votes:
@@ -9,6 +9,47 @@ Time-dependent behaviour — `View` timeouts, command cooldowns, `asyncio.sleep`
9
9
  — is normally painful to test: you either wait in real time (slow, flaky) or mock the clock
10
10
  by hand (fiddly, leaky). SimCord gives you a **virtual clock** you fast-forward in one call.
11
11
 
12
+ ## Choosing the initial UTC instant
13
+
14
+ `Env(bot, start_time=...)` and `simcord.run(bot, start_time=...)` accept an aware
15
+ `datetime`. The default remains **2026-01-01 00:00:00 UTC**. Offsets normalize to UTC;
16
+ naive datetimes, non-datetimes and unsupported instants raise `SetupError` before backend
17
+ IDs or runtime hooks are created. The supported initial UTC range is
18
+ **2015-01-01 00:00:00 inclusive through 2154-05-15 07:35:11.104 exclusive**.
19
+ The origin is fixed at construction, already applies to pre-start fixtures and backend
20
+ timestamps, and survives bot restarts along with elapsed virtual time. There is no rewind
21
+ or `set_time` operation.
22
+
23
+ Bind your application's existing clock seam **before** startup so `setup_hook` and
24
+ fixture writes use the same clock. For example, a custom pytest fixture can use manual
25
+ lifecycle control:
26
+
27
+ ```python
28
+ from datetime import UTC, datetime
29
+
30
+ import pytest_asyncio
31
+ import simcord
32
+
33
+ @pytest_asyncio.fixture
34
+ async def world():
35
+ bot = create_bot()
36
+ env = simcord.Env(bot, start_time=datetime(2030, 4, 5, 12, tzinfo=UTC))
37
+ bot.service.now = env.now # application-owned callable used by startup and writes
38
+ await env.start()
39
+ try:
40
+ yield env
41
+ finally:
42
+ await env.shutdown()
43
+ ```
44
+
45
+ The standard pytest fixture also accepts
46
+ `@pytest.mark.simcord(start_time=datetime(2030, 4, 5, tzinfo=UTC))`.
47
+ Use a manual fixture when application wiring must happen before `env.start()`.
48
+ Selecting an initial origin does **not** synchronize Python wall time, SQL
49
+ `CURRENT_TIMESTAMP`/`now()`, database defaults or application clocks. Inject `env.now`
50
+ through existing application time seams, including relevant persisted writes, rather
51
+ than globally monkeypatching datetime, SQL or SDK functions.
52
+
12
53
  ## `env.advance_time(seconds)`
13
54
 
14
55
  ```python
@@ -35,6 +76,9 @@ Anything the bot scheduled against the loop's clock:
35
76
  - **Resource deadlines** — polls, scheduled events and explicitly scheduled entitlement ends.
36
77
  - **Supported SDK UTC helpers** — entitlement expiry, `discord.utils.sleep_until`, and
37
78
  relative/calendar `discord.ext.tasks` loops use the selected virtual clock.
79
+ - **Typing refresh intervals** — the native SDK typing task's direct five-second sleep
80
+ is virtual-only. Its initial HTTP request and every refresh still execute and join
81
+ normally; HTTP waits and retries are not treated as parked typing intervals.
38
82
 
39
83
  ```python
40
84
  async def test_cooldown_resets(simcord_env):
@@ -109,13 +153,17 @@ and retry shutdown.
109
153
 
110
154
  ## Determinism
111
155
 
112
- Snowflake IDs and UTC start from a fixed virtual epoch. ID allocation does not advance
113
- time: IDs use a sequence within the current millisecond and explicit IDs are reserved
114
- against collisions. Exhausting the sequence fails instead of silently moving UTC.
156
+ Snowflake IDs and UTC start from the configured origin (the fixed default is unchanged).
157
+ ID allocation does not advance time: IDs use a sequence within the current millisecond
158
+ and explicit IDs are reserved against collisions. Exhausting the sequence fails instead
159
+ of silently moving UTC.
115
160
  Shard-pinned guild fixtures can use an earlier timestamp so they route to the requested
116
161
  shard without time travel. Their `created_at` can precede arrangement time, and fixture
117
162
  creation order need not match numeric ID order. Normal snowflake timestamps have
118
163
  millisecond precision, while backend UTC retains microseconds.
164
+ At the Discord epoch, allocation skips ID zero. A shard request requiring a timestamp
165
+ before that epoch raises `SetupError` without reserving IDs. Allocation beyond the
166
+ 64-bit snowflake timestamp range also rejects rather than generating an invalid ID.
119
167
  Settlement joins runnable work before and after each timer step. Deadline timers behind
120
168
  recognized waits — `wait_for` timeouts on listener waits, View/Modal expiry, `ext.tasks`
121
169
  loop intervals and wake-up timers scheduled inside `env.external_wait` — never fire on the
@@ -124,6 +172,14 @@ recognized only when its live loop timer resolves the awaited future; unrelated
124
172
  timers do not make an unknown wait look parked. `advance_time()` is itself exclusive with
125
173
  other public operations and rejects invalid or negative durations.
126
174
 
175
+ Unrecognized waits can have real-due timers handled by the automatic coordinator. That
176
+ fallback advances through its due horizon even when an unrelated input remains blocked,
177
+ still processing resource deadlines before tied bot timers. It does not declare the
178
+ blocked input settled, relax executor ownership or suppress settlement failures.
179
+ Recognized intervals such as typing refresh sleeps remain virtual-only. Explicit
180
+ `advance_time()` still uses strict settlement and reports committed partial progress on
181
+ failure.
182
+
127
183
  If an advance times out, earlier transitions remain committed. The exception notes
128
184
  report requested and reached UTC instants. Resolve the outstanding work and advance
129
185
  only the remaining duration; do not repeat the original duration or replay an action.