async-matrix 0.1.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +131 -0
  3. data/data/discord-api-spec/openapi.json +40404 -0
  4. data/data/matrix-spec/api/client-server/account-data.yaml +389 -0
  5. data/data/matrix-spec/api/client-server/account_deactivation.yaml +149 -0
  6. data/data/matrix-spec/api/client-server/admin.yaml +512 -0
  7. data/data/matrix-spec/api/client-server/administrative_contact.yaml +620 -0
  8. data/data/matrix-spec/api/client-server/appservice_ping.yaml +183 -0
  9. data/data/matrix-spec/api/client-server/appservice_room_directory.yaml +104 -0
  10. data/data/matrix-spec/api/client-server/authed-content-repo.yaml +590 -0
  11. data/data/matrix-spec/api/client-server/banning.yaml +177 -0
  12. data/data/matrix-spec/api/client-server/capabilities.yaml +235 -0
  13. data/data/matrix-spec/api/client-server/content-repo.yaml +887 -0
  14. data/data/matrix-spec/api/client-server/create_room.yaml +306 -0
  15. data/data/matrix-spec/api/client-server/cross_signing.yaml +300 -0
  16. data/data/matrix-spec/api/client-server/definitions/auth_data.yaml +34 -0
  17. data/data/matrix-spec/api/client-server/definitions/auth_response.yaml +62 -0
  18. data/data/matrix-spec/api/client-server/definitions/client_device.yaml +44 -0
  19. data/data/matrix-spec/api/client-server/definitions/client_event.yaml +49 -0
  20. data/data/matrix-spec/api/client-server/definitions/client_event_without_room_id.yaml +147 -0
  21. data/data/matrix-spec/api/client-server/definitions/cross_signing_key.yaml +57 -0
  22. data/data/matrix-spec/api/client-server/definitions/device_keys.yaml +70 -0
  23. data/data/matrix-spec/api/client-server/definitions/errors/error.yaml +26 -0
  24. data/data/matrix-spec/api/client-server/definitions/errors/rate_limited.yaml +34 -0
  25. data/data/matrix-spec/api/client-server/definitions/event_batch.yaml +22 -0
  26. data/data/matrix-spec/api/client-server/definitions/event_filter.yaml +55 -0
  27. data/data/matrix-spec/api/client-server/definitions/invite_3pid.yaml +45 -0
  28. data/data/matrix-spec/api/client-server/definitions/key_backup_auth_data.yaml +36 -0
  29. data/data/matrix-spec/api/client-server/definitions/key_backup_data.yaml +50 -0
  30. data/data/matrix-spec/api/client-server/definitions/key_backup_session_data.yaml +57 -0
  31. data/data/matrix-spec/api/client-server/definitions/m.login.terms_params.yaml +83 -0
  32. data/data/matrix-spec/api/client-server/definitions/m.mentions.yaml +33 -0
  33. data/data/matrix-spec/api/client-server/definitions/m.oauth_params.yaml +30 -0
  34. data/data/matrix-spec/api/client-server/definitions/m.relates_to.yaml +45 -0
  35. data/data/matrix-spec/api/client-server/definitions/megolm_export_session_data.yaml +39 -0
  36. data/data/matrix-spec/api/client-server/definitions/olm_payload.yaml +88 -0
  37. data/data/matrix-spec/api/client-server/definitions/one_time_keys.yaml +27 -0
  38. data/data/matrix-spec/api/client-server/definitions/openid_token.yaml +37 -0
  39. data/data/matrix-spec/api/client-server/definitions/protocol.yaml +44 -0
  40. data/data/matrix-spec/api/client-server/definitions/public_rooms_chunk.yaml +74 -0
  41. data/data/matrix-spec/api/client-server/definitions/public_rooms_response.yaml +59 -0
  42. data/data/matrix-spec/api/client-server/definitions/push_condition.yaml +53 -0
  43. data/data/matrix-spec/api/client-server/definitions/push_rule.yaml +53 -0
  44. data/data/matrix-spec/api/client-server/definitions/push_ruleset.yaml +206 -0
  45. data/data/matrix-spec/api/client-server/definitions/recent_emoji.yaml +29 -0
  46. data/data/matrix-spec/api/client-server/definitions/request_email_validation.yaml +36 -0
  47. data/data/matrix-spec/api/client-server/definitions/request_msisdn_validation.yaml +36 -0
  48. data/data/matrix-spec/api/client-server/definitions/request_token_response.yaml +46 -0
  49. data/data/matrix-spec/api/client-server/definitions/room_event_filter.yaml +60 -0
  50. data/data/matrix-spec/api/client-server/definitions/room_key_backup.yaml +37 -0
  51. data/data/matrix-spec/api/client-server/definitions/room_summary.yaml +44 -0
  52. data/data/matrix-spec/api/client-server/definitions/security.yaml +47 -0
  53. data/data/matrix-spec/api/client-server/definitions/sso_login_flow.yaml +98 -0
  54. data/data/matrix-spec/api/client-server/definitions/state_event_batch.yaml +21 -0
  55. data/data/matrix-spec/api/client-server/definitions/sync_filter.yaml +86 -0
  56. data/data/matrix-spec/api/client-server/definitions/tag.yaml +24 -0
  57. data/data/matrix-spec/api/client-server/definitions/third_party_signed.yaml +45 -0
  58. data/data/matrix-spec/api/client-server/definitions/timeline_batch.yaml +36 -0
  59. data/data/matrix-spec/api/client-server/definitions/user_identifier.yaml +27 -0
  60. data/data/matrix-spec/api/client-server/definitions/wellknown/full.yaml +38 -0
  61. data/data/matrix-spec/api/client-server/definitions/wellknown/homeserver.yaml +25 -0
  62. data/data/matrix-spec/api/client-server/definitions/wellknown/identity_server.yaml +25 -0
  63. data/data/matrix-spec/api/client-server/device_management.yaml +314 -0
  64. data/data/matrix-spec/api/client-server/directory.yaml +318 -0
  65. data/data/matrix-spec/api/client-server/event_context.yaml +161 -0
  66. data/data/matrix-spec/api/client-server/filter.yaml +224 -0
  67. data/data/matrix-spec/api/client-server/inviting.yaml +149 -0
  68. data/data/matrix-spec/api/client-server/joining.yaml +238 -0
  69. data/data/matrix-spec/api/client-server/key_backup.yaml +993 -0
  70. data/data/matrix-spec/api/client-server/keys.yaml +482 -0
  71. data/data/matrix-spec/api/client-server/kicking.yaml +110 -0
  72. data/data/matrix-spec/api/client-server/knocking.yaml +152 -0
  73. data/data/matrix-spec/api/client-server/leaving.yaml +163 -0
  74. data/data/matrix-spec/api/client-server/list_joined_rooms.yaml +68 -0
  75. data/data/matrix-spec/api/client-server/list_public_rooms.yaml +280 -0
  76. data/data/matrix-spec/api/client-server/login.yaml +321 -0
  77. data/data/matrix-spec/api/client-server/login_token.yaml +138 -0
  78. data/data/matrix-spec/api/client-server/logout.yaml +86 -0
  79. data/data/matrix-spec/api/client-server/message_pagination.yaml +194 -0
  80. data/data/matrix-spec/api/client-server/notifications.yaml +152 -0
  81. data/data/matrix-spec/api/client-server/oauth_server_metadata.yaml +238 -0
  82. data/data/matrix-spec/api/client-server/old_sync.yaml +379 -0
  83. data/data/matrix-spec/api/client-server/openid.yaml +98 -0
  84. data/data/matrix-spec/api/client-server/password_management.yaml +246 -0
  85. data/data/matrix-spec/api/client-server/peeking_events.yaml +122 -0
  86. data/data/matrix-spec/api/client-server/policy_server.yaml +82 -0
  87. data/data/matrix-spec/api/client-server/presence.yaml +169 -0
  88. data/data/matrix-spec/api/client-server/profile.yaml +366 -0
  89. data/data/matrix-spec/api/client-server/pusher.yaml +303 -0
  90. data/data/matrix-spec/api/client-server/pushrules.yaml +633 -0
  91. data/data/matrix-spec/api/client-server/read_markers.yaml +103 -0
  92. data/data/matrix-spec/api/client-server/receipts.yaml +139 -0
  93. data/data/matrix-spec/api/client-server/redaction.yaml +116 -0
  94. data/data/matrix-spec/api/client-server/refresh.yaml +119 -0
  95. data/data/matrix-spec/api/client-server/registration.yaml +488 -0
  96. data/data/matrix-spec/api/client-server/registration_tokens.yaml +93 -0
  97. data/data/matrix-spec/api/client-server/relations.yaml +382 -0
  98. data/data/matrix-spec/api/client-server/report_content.yaml +283 -0
  99. data/data/matrix-spec/api/client-server/room_event_by_timestamp.yaml +147 -0
  100. data/data/matrix-spec/api/client-server/room_initial_sync.yaml +188 -0
  101. data/data/matrix-spec/api/client-server/room_send.yaml +130 -0
  102. data/data/matrix-spec/api/client-server/room_state.yaml +159 -0
  103. data/data/matrix-spec/api/client-server/room_summary.yaml +138 -0
  104. data/data/matrix-spec/api/client-server/room_upgrades.yaml +130 -0
  105. data/data/matrix-spec/api/client-server/rooms.yaml +380 -0
  106. data/data/matrix-spec/api/client-server/search.yaml +385 -0
  107. data/data/matrix-spec/api/client-server/space_hierarchy.yaml +237 -0
  108. data/data/matrix-spec/api/client-server/sso_login_redirect.yaml +135 -0
  109. data/data/matrix-spec/api/client-server/support.yaml +142 -0
  110. data/data/matrix-spec/api/client-server/sync.yaml +692 -0
  111. data/data/matrix-spec/api/client-server/tags.yaml +183 -0
  112. data/data/matrix-spec/api/client-server/third_party_lookup.yaml +324 -0
  113. data/data/matrix-spec/api/client-server/third_party_membership.yaml +139 -0
  114. data/data/matrix-spec/api/client-server/threads_list.yaml +167 -0
  115. data/data/matrix-spec/api/client-server/to_device.yaml +104 -0
  116. data/data/matrix-spec/api/client-server/typing.yaml +103 -0
  117. data/data/matrix-spec/api/client-server/users.yaml +136 -0
  118. data/data/matrix-spec/api/client-server/versions.yaml +108 -0
  119. data/data/matrix-spec/api/client-server/voip.yaml +93 -0
  120. data/data/matrix-spec/api/client-server/wellknown.yaml +60 -0
  121. data/data/matrix-spec/api/client-server/whoami.yaml +121 -0
  122. data/data/matrix-spec/event-schemas/examples/core/event.json +6 -0
  123. data/data/matrix-spec/event-schemas/examples/core/room_edu.json +3 -0
  124. data/data/matrix-spec/event-schemas/examples/core/room_event.json +11 -0
  125. data/data/matrix-spec/event-schemas/examples/core/state_event.json +4 -0
  126. data/data/matrix-spec/event-schemas/examples/invite_room_state.json +18 -0
  127. data/data/matrix-spec/event-schemas/examples/knock_room_state.json +18 -0
  128. data/data/matrix-spec/event-schemas/examples/m.accepted_terms.yaml +10 -0
  129. data/data/matrix-spec/event-schemas/examples/m.call.answer.yaml +21 -0
  130. data/data/matrix-spec/event-schemas/examples/m.call.candidates.yaml +16 -0
  131. data/data/matrix-spec/event-schemas/examples/m.call.hangup.yaml +10 -0
  132. data/data/matrix-spec/event-schemas/examples/m.call.invite.yaml +22 -0
  133. data/data/matrix-spec/event-schemas/examples/m.call.negotiate.yaml +22 -0
  134. data/data/matrix-spec/event-schemas/examples/m.call.reject.yaml +9 -0
  135. data/data/matrix-spec/event-schemas/examples/m.call.sdp_stream_metadata_changed.yaml +16 -0
  136. data/data/matrix-spec/event-schemas/examples/m.call.select_answer.yaml +10 -0
  137. data/data/matrix-spec/event-schemas/examples/m.direct.yaml +10 -0
  138. data/data/matrix-spec/event-schemas/examples/m.dummy.yaml +4 -0
  139. data/data/matrix-spec/event-schemas/examples/m.forwarded_room_key.yaml +14 -0
  140. data/data/matrix-spec/event-schemas/examples/m.fully_read.yaml +7 -0
  141. data/data/matrix-spec/event-schemas/examples/m.identity_server.yaml +7 -0
  142. data/data/matrix-spec/event-schemas/examples/m.ignored_user_list.yaml +9 -0
  143. data/data/matrix-spec/event-schemas/examples/m.invite_permission_config.yaml +7 -0
  144. data/data/matrix-spec/event-schemas/examples/m.key.verification.accept.yaml +12 -0
  145. data/data/matrix-spec/event-schemas/examples/m.key.verification.cancel.yaml +8 -0
  146. data/data/matrix-spec/event-schemas/examples/m.key.verification.done.yaml +6 -0
  147. data/data/matrix-spec/event-schemas/examples/m.key.verification.key.yaml +7 -0
  148. data/data/matrix-spec/event-schemas/examples/m.key.verification.mac.yaml +10 -0
  149. data/data/matrix-spec/event-schemas/examples/m.key.verification.ready.yaml +10 -0
  150. data/data/matrix-spec/event-schemas/examples/m.key.verification.request.yaml +11 -0
  151. data/data/matrix-spec/event-schemas/examples/m.key.verification.start$m.sas.v1.yaml +12 -0
  152. data/data/matrix-spec/event-schemas/examples/m.key.verification.start.yaml +8 -0
  153. data/data/matrix-spec/event-schemas/examples/m.key_backup.yaml +7 -0
  154. data/data/matrix-spec/event-schemas/examples/m.marked_unread.yaml +7 -0
  155. data/data/matrix-spec/event-schemas/examples/m.policy.rule.room.yaml +10 -0
  156. data/data/matrix-spec/event-schemas/examples/m.policy.rule.server.yaml +10 -0
  157. data/data/matrix-spec/event-schemas/examples/m.policy.rule.user.yaml +10 -0
  158. data/data/matrix-spec/event-schemas/examples/m.presence.yaml +12 -0
  159. data/data/matrix-spec/event-schemas/examples/m.push_rules.yaml +177 -0
  160. data/data/matrix-spec/event-schemas/examples/m.reaction.yaml +11 -0
  161. data/data/matrix-spec/event-schemas/examples/m.receipt.yaml +18 -0
  162. data/data/matrix-spec/event-schemas/examples/m.recent_emoji.yaml +16 -0
  163. data/data/matrix-spec/event-schemas/examples/m.room.avatar.yaml +14 -0
  164. data/data/matrix-spec/event-schemas/examples/m.room.canonical_alias.yaml +12 -0
  165. data/data/matrix-spec/event-schemas/examples/m.room.create.yaml +13 -0
  166. data/data/matrix-spec/event-schemas/examples/m.room.encrypted$megolm.yaml +11 -0
  167. data/data/matrix-spec/event-schemas/examples/m.room.encrypted$olm.yaml +14 -0
  168. data/data/matrix-spec/event-schemas/examples/m.room.encryption.yaml +10 -0
  169. data/data/matrix-spec/event-schemas/examples/m.room.guest_access.yaml +8 -0
  170. data/data/matrix-spec/event-schemas/examples/m.room.history_visibility.yaml +8 -0
  171. data/data/matrix-spec/event-schemas/examples/m.room.join_rules$restricted.yaml +18 -0
  172. data/data/matrix-spec/event-schemas/examples/m.room.join_rules.yaml +8 -0
  173. data/data/matrix-spec/event-schemas/examples/m.room.member$invite_room_state.yaml +15 -0
  174. data/data/matrix-spec/event-schemas/examples/m.room.member$join_authorised_via_users_server.yaml +12 -0
  175. data/data/matrix-spec/event-schemas/examples/m.room.member$knock_room_state.yaml +15 -0
  176. data/data/matrix-spec/event-schemas/examples/m.room.member$third_party_invite.yaml +20 -0
  177. data/data/matrix-spec/event-schemas/examples/m.room.member.yaml +12 -0
  178. data/data/matrix-spec/event-schemas/examples/m.room.message$m.audio.yaml +14 -0
  179. data/data/matrix-spec/event-schemas/examples/m.room.message$m.emote.yaml +10 -0
  180. data/data/matrix-spec/event-schemas/examples/m.room.message$m.file.yaml +14 -0
  181. data/data/matrix-spec/event-schemas/examples/m.room.message$m.image.yaml +16 -0
  182. data/data/matrix-spec/event-schemas/examples/m.room.message$m.key.verification.request.yaml +19 -0
  183. data/data/matrix-spec/event-schemas/examples/m.room.message$m.location.yaml +18 -0
  184. data/data/matrix-spec/event-schemas/examples/m.room.message$m.notice.yaml +10 -0
  185. data/data/matrix-spec/event-schemas/examples/m.room.message$m.server_notice.yaml +11 -0
  186. data/data/matrix-spec/event-schemas/examples/m.room.message$m.text.yaml +10 -0
  187. data/data/matrix-spec/event-schemas/examples/m.room.message$m.video.yaml +23 -0
  188. data/data/matrix-spec/event-schemas/examples/m.room.name.yaml +8 -0
  189. data/data/matrix-spec/event-schemas/examples/m.room.pinned_events.yaml +8 -0
  190. data/data/matrix-spec/event-schemas/examples/m.room.policy.yaml +11 -0
  191. data/data/matrix-spec/event-schemas/examples/m.room.power_levels.yaml +24 -0
  192. data/data/matrix-spec/event-schemas/examples/m.room.redaction.yaml +8 -0
  193. data/data/matrix-spec/event-schemas/examples/m.room.server_acl.yaml +10 -0
  194. data/data/matrix-spec/event-schemas/examples/m.room.third_party_invite.yaml +14 -0
  195. data/data/matrix-spec/event-schemas/examples/m.room.tombstone.yaml +9 -0
  196. data/data/matrix-spec/event-schemas/examples/m.room.topic.yaml +16 -0
  197. data/data/matrix-spec/event-schemas/examples/m.room_key.withheld.yaml +12 -0
  198. data/data/matrix-spec/event-schemas/examples/m.room_key.yaml +10 -0
  199. data/data/matrix-spec/event-schemas/examples/m.room_key_request$cancel_request.yaml +8 -0
  200. data/data/matrix-spec/event-schemas/examples/m.room_key_request$request.yaml +14 -0
  201. data/data/matrix-spec/event-schemas/examples/m.secret.request.yaml +9 -0
  202. data/data/matrix-spec/event-schemas/examples/m.secret.send.yaml +7 -0
  203. data/data/matrix-spec/event-schemas/examples/m.space.child.yaml +10 -0
  204. data/data/matrix-spec/event-schemas/examples/m.space.parent.yaml +9 -0
  205. data/data/matrix-spec/event-schemas/examples/m.sticker.yaml +22 -0
  206. data/data/matrix-spec/event-schemas/examples/m.tag.yaml +9 -0
  207. data/data/matrix-spec/event-schemas/examples/m.typing.yaml +7 -0
  208. data/data/matrix-spec/event-schemas/moderation_policy_rule.yaml +32 -0
  209. data/data/matrix-spec/event-schemas/schema/components/m_text_content_block.yaml +28 -0
  210. data/data/matrix-spec/event-schemas/schema/components/sdp_stream_metadata.yaml +43 -0
  211. data/data/matrix-spec/event-schemas/schema/components/signed_third_party_invite.yaml +48 -0
  212. data/data/matrix-spec/event-schemas/schema/core-event-schema/call_event.yaml +27 -0
  213. data/data/matrix-spec/event-schemas/schema/core-event-schema/event.yaml +16 -0
  214. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/avatar_info.yaml +30 -0
  215. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml +52 -0
  216. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/thumbnail_info.yaml +22 -0
  217. data/data/matrix-spec/event-schemas/schema/core-event-schema/room_event.yaml +17 -0
  218. data/data/matrix-spec/event-schemas/schema/core-event-schema/state_event.yaml +8 -0
  219. data/data/matrix-spec/event-schemas/schema/core-event-schema/stripped_state.yaml +48 -0
  220. data/data/matrix-spec/event-schemas/schema/core-event-schema/sync_room_event.yaml +49 -0
  221. data/data/matrix-spec/event-schemas/schema/core-event-schema/sync_state_event.yaml +36 -0
  222. data/data/matrix-spec/event-schemas/schema/core-event-schema/unsigned_prop.yaml +23 -0
  223. data/data/matrix-spec/event-schemas/schema/m.accepted_terms.yaml +25 -0
  224. data/data/matrix-spec/event-schemas/schema/m.call.answer.yaml +44 -0
  225. data/data/matrix-spec/event-schemas/schema/m.call.candidates.yaml +52 -0
  226. data/data/matrix-spec/event-schemas/schema/m.call.hangup.yaml +57 -0
  227. data/data/matrix-spec/event-schemas/schema/m.call.invite.yaml +53 -0
  228. data/data/matrix-spec/event-schemas/schema/m.call.negotiate.yaml +78 -0
  229. data/data/matrix-spec/event-schemas/schema/m.call.reject.yaml +29 -0
  230. data/data/matrix-spec/event-schemas/schema/m.call.sdp_stream_metadata_changed.yaml +24 -0
  231. data/data/matrix-spec/event-schemas/schema/m.call.select_answer.yaml +29 -0
  232. data/data/matrix-spec/event-schemas/schema/m.direct.yaml +29 -0
  233. data/data/matrix-spec/event-schemas/schema/m.dummy.yaml +25 -0
  234. data/data/matrix-spec/event-schemas/schema/m.forwarded_room_key.yaml +70 -0
  235. data/data/matrix-spec/event-schemas/schema/m.fully_read.yaml +26 -0
  236. data/data/matrix-spec/event-schemas/schema/m.identity_server.yaml +28 -0
  237. data/data/matrix-spec/event-schemas/schema/m.ignored_user_list.yaml +30 -0
  238. data/data/matrix-spec/event-schemas/schema/m.invite_permission_config.yaml +21 -0
  239. data/data/matrix-spec/event-schemas/schema/m.key.verification.accept.yaml +63 -0
  240. data/data/matrix-spec/event-schemas/schema/m.key.verification.cancel.yaml +72 -0
  241. data/data/matrix-spec/event-schemas/schema/m.key.verification.done.yaml +24 -0
  242. data/data/matrix-spec/event-schemas/schema/m.key.verification.key.yaml +31 -0
  243. data/data/matrix-spec/event-schemas/schema/m.key.verification.m.relates_to.yaml +23 -0
  244. data/data/matrix-spec/event-schemas/schema/m.key.verification.mac.yaml +43 -0
  245. data/data/matrix-spec/event-schemas/schema/m.key.verification.ready.yaml +41 -0
  246. data/data/matrix-spec/event-schemas/schema/m.key.verification.request.yaml +47 -0
  247. data/data/matrix-spec/event-schemas/schema/m.key.verification.start$m.reciprocate.v1.yaml +45 -0
  248. data/data/matrix-spec/event-schemas/schema/m.key.verification.start$m.sas.v1.yaml +76 -0
  249. data/data/matrix-spec/event-schemas/schema/m.key.verification.start.yaml +46 -0
  250. data/data/matrix-spec/event-schemas/schema/m.key_backup.yaml +24 -0
  251. data/data/matrix-spec/event-schemas/schema/m.marked_unread.yaml +26 -0
  252. data/data/matrix-spec/event-schemas/schema/m.policy.rule.room.yaml +17 -0
  253. data/data/matrix-spec/event-schemas/schema/m.policy.rule.server.yaml +17 -0
  254. data/data/matrix-spec/event-schemas/schema/m.policy.rule.user.yaml +17 -0
  255. data/data/matrix-spec/event-schemas/schema/m.presence.yaml +50 -0
  256. data/data/matrix-spec/event-schemas/schema/m.push_rules.yaml +23 -0
  257. data/data/matrix-spec/event-schemas/schema/m.reaction.yaml +45 -0
  258. data/data/matrix-spec/event-schemas/schema/m.receipt.yaml +65 -0
  259. data/data/matrix-spec/event-schemas/schema/m.recent_emoji.yaml +29 -0
  260. data/data/matrix-spec/event-schemas/schema/m.room.avatar.yaml +29 -0
  261. data/data/matrix-spec/event-schemas/schema/m.room.canonical_alias.yaml +40 -0
  262. data/data/matrix-spec/event-schemas/schema/m.room.create.yaml +86 -0
  263. data/data/matrix-spec/event-schemas/schema/m.room.encrypted.yaml +91 -0
  264. data/data/matrix-spec/event-schemas/schema/m.room.encryption.yaml +38 -0
  265. data/data/matrix-spec/event-schemas/schema/m.room.guest_access.yaml +28 -0
  266. data/data/matrix-spec/event-schemas/schema/m.room.history_visibility.yaml +30 -0
  267. data/data/matrix-spec/event-schemas/schema/m.room.join_rules.yaml +78 -0
  268. data/data/matrix-spec/event-schemas/schema/m.room.member.yaml +174 -0
  269. data/data/matrix-spec/event-schemas/schema/m.room.message$m.audio.yaml +73 -0
  270. data/data/matrix-spec/event-schemas/schema/m.room.message$m.emote.yaml +36 -0
  271. data/data/matrix-spec/event-schemas/schema/m.room.message$m.file.yaml +85 -0
  272. data/data/matrix-spec/event-schemas/schema/m.room.message$m.image.yaml +63 -0
  273. data/data/matrix-spec/event-schemas/schema/m.room.message$m.key.verification.request.yaml +70 -0
  274. data/data/matrix-spec/event-schemas/schema/m.room.message$m.location.yaml +50 -0
  275. data/data/matrix-spec/event-schemas/schema/m.room.message$m.notice.yaml +36 -0
  276. data/data/matrix-spec/event-schemas/schema/m.room.message$m.server_notice.yaml +41 -0
  277. data/data/matrix-spec/event-schemas/schema/m.room.message$m.text.yaml +36 -0
  278. data/data/matrix-spec/event-schemas/schema/m.room.message$m.video.yaml +95 -0
  279. data/data/matrix-spec/event-schemas/schema/m.room.message.yaml +25 -0
  280. data/data/matrix-spec/event-schemas/schema/m.room.name.yaml +35 -0
  281. data/data/matrix-spec/event-schemas/schema/m.room.pinned_events.yaml +27 -0
  282. data/data/matrix-spec/event-schemas/schema/m.room.policy.yaml +41 -0
  283. data/data/matrix-spec/event-schemas/schema/m.room.power_levels.yaml +139 -0
  284. data/data/matrix-spec/event-schemas/schema/m.room.redaction.yaml +29 -0
  285. data/data/matrix-spec/event-schemas/schema/m.room.server_acl.yaml +88 -0
  286. data/data/matrix-spec/event-schemas/schema/m.room.third_party_invite.yaml +79 -0
  287. data/data/matrix-spec/event-schemas/schema/m.room.tombstone.yaml +29 -0
  288. data/data/matrix-spec/event-schemas/schema/m.room.topic.yaml +53 -0
  289. data/data/matrix-spec/event-schemas/schema/m.room_key.withheld.yaml +88 -0
  290. data/data/matrix-spec/event-schemas/schema/m.room_key.yaml +38 -0
  291. data/data/matrix-spec/event-schemas/schema/m.room_key_request.yaml +73 -0
  292. data/data/matrix-spec/event-schemas/schema/m.secret.request.yaml +42 -0
  293. data/data/matrix-spec/event-schemas/schema/m.secret.send.yaml +35 -0
  294. data/data/matrix-spec/event-schemas/schema/m.space.child.yaml +50 -0
  295. data/data/matrix-spec/event-schemas/schema/m.space.parent.yaml +36 -0
  296. data/data/matrix-spec/event-schemas/schema/m.sticker.yaml +36 -0
  297. data/data/matrix-spec/event-schemas/schema/m.tag.yaml +28 -0
  298. data/data/matrix-spec/event-schemas/schema/m.typing.yaml +29 -0
  299. data/lib/async/discord/api/path_tree.rb +130 -0
  300. data/lib/async/discord/api.rb +156 -0
  301. data/lib/async/discord/client.rb +286 -0
  302. data/lib/async/discord/error.rb +88 -0
  303. data/lib/async/discord/gateway.rb +362 -0
  304. data/lib/async/discord.rb +16 -0
  305. data/lib/async/matrix/api/chain.rb +584 -0
  306. data/lib/async/matrix/api/concat.rb +105 -0
  307. data/lib/async/matrix/api/path_tree.rb +208 -0
  308. data/lib/async/matrix/api.rb +204 -0
  309. data/lib/async/matrix/application_service/bot.rb +235 -0
  310. data/lib/async/matrix/application_service/config/schema/analytics.json +21 -0
  311. data/lib/async/matrix/application_service/config/schema/appservice.json +82 -0
  312. data/lib/async/matrix/application_service/config/schema/backfill.json +91 -0
  313. data/lib/async/matrix/application_service/config/schema/bridge.json +209 -0
  314. data/lib/async/matrix/application_service/config/schema/config.json +61 -0
  315. data/lib/async/matrix/application_service/config/schema/database.json +38 -0
  316. data/lib/async/matrix/application_service/config/schema/direct_media.json +35 -0
  317. data/lib/async/matrix/application_service/config/schema/double_puppet.json +24 -0
  318. data/lib/async/matrix/application_service/config/schema/encryption.json +164 -0
  319. data/lib/async/matrix/application_service/config/schema/homeserver.json +58 -0
  320. data/lib/async/matrix/application_service/config/schema/logging.json +50 -0
  321. data/lib/async/matrix/application_service/config/schema/management_room_texts.json +25 -0
  322. data/lib/async/matrix/application_service/config/schema/matrix.json +45 -0
  323. data/lib/async/matrix/application_service/config/schema/permissions.json +54 -0
  324. data/lib/async/matrix/application_service/config/schema/provisioning.json +23 -0
  325. data/lib/async/matrix/application_service/config/schema/public_media.json +39 -0
  326. data/lib/async/matrix/application_service/config/schema/relay.json +43 -0
  327. data/lib/async/matrix/application_service/config/vivify.rb +113 -0
  328. data/lib/async/matrix/application_service/config.rb +214 -123
  329. data/lib/async/matrix/application_service/dispatcher.rb +115 -113
  330. data/lib/async/matrix/application_service/error_response.rb +26 -26
  331. data/lib/async/matrix/application_service/event.rb +206 -1
  332. data/lib/async/matrix/application_service/server.rb +286 -215
  333. data/lib/async/matrix/application_service/transaction.rb +52 -52
  334. data/lib/async/matrix/application_service/transaction_store.rb +62 -62
  335. data/lib/async/matrix/bridge/discord/db/connection.rb +143 -0
  336. data/lib/async/matrix/bridge/discord/db/file.rb +120 -0
  337. data/lib/async/matrix/bridge/discord/db/guild.rb +122 -0
  338. data/lib/async/matrix/bridge/discord/db/message.rb +162 -0
  339. data/lib/async/matrix/bridge/discord/db/migrations/001_create_users.rb +14 -0
  340. data/lib/async/matrix/bridge/discord/db/migrations/002_create_guilds.rb +14 -0
  341. data/lib/async/matrix/bridge/discord/db/migrations/003_create_portals.rb +23 -0
  342. data/lib/async/matrix/bridge/discord/db/migrations/004_create_puppets.rb +19 -0
  343. data/lib/async/matrix/bridge/discord/db/migrations/005_create_messages.rb +20 -0
  344. data/lib/async/matrix/bridge/discord/db/migrations/006_create_reactions.rb +19 -0
  345. data/lib/async/matrix/bridge/discord/db/migrations/007_create_files.rb +18 -0
  346. data/lib/async/matrix/bridge/discord/db/portal.rb +152 -0
  347. data/lib/async/matrix/bridge/discord/db/puppet.rb +130 -0
  348. data/lib/async/matrix/bridge/discord/db/reaction.rb +167 -0
  349. data/lib/async/matrix/bridge/discord/db/user.rb +114 -0
  350. data/lib/async/matrix/bridge/discord/db.rb +140 -0
  351. data/lib/async/matrix/client.rb +919 -179
  352. data/lib/async/matrix/connection.rb +8 -8
  353. data/lib/async/matrix/double_puppet_client.rb +84 -0
  354. data/lib/async/matrix/endpoint.rb +45 -45
  355. data/lib/async/matrix/error.rb +49 -43
  356. data/lib/async/matrix/media_client.rb +173 -0
  357. data/lib/async/matrix/notifier.rb +92 -92
  358. data/lib/async/matrix/schema/registry.rb +355 -0
  359. data/lib/async/matrix/schema/validation_error.rb +226 -0
  360. data/lib/async/matrix/schema.rb +174 -0
  361. data/lib/async/matrix/server.rb +8 -7
  362. data/lib/async/matrix/stream.rb +7 -7
  363. data/lib/async/matrix/version.rb +1 -1
  364. data/lib/async/matrix.rb +4 -2
  365. metadata +419 -1
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:users) do
6
+ String :mxid, primary_key: true
7
+ String :discord_id, unique: true
8
+ String :discord_token
9
+ String :management_room
10
+ String :space_room
11
+ String :dm_space_room
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:guilds) do
6
+ String :discord_id, primary_key: true
7
+ String :mxid, unique: true
8
+ String :name
9
+ String :avatar_hash
10
+ String :avatar_url
11
+ Integer :bridging_mode, default: 0, null: false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:portals) do
6
+ String :discord_id, null: false
7
+ String :receiver, null: false, default: ""
8
+ String :mxid, unique: true
9
+ String :discord_guild_id
10
+ String :name
11
+ String :topic
12
+ String :avatar_hash
13
+ String :avatar_url
14
+ TrueClass :encrypted, default: false, null: false
15
+ Integer :channel_type, default: 0, null: false
16
+ String :relay_webhook_id
17
+ String :relay_webhook_secret
18
+
19
+ primary_key [:discord_id, :receiver]
20
+ foreign_key [:discord_guild_id], :guilds, key: :discord_id
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:puppets) do
6
+ String :discord_id, primary_key: true
7
+ String :name
8
+ String :avatar_hash
9
+ String :avatar_url
10
+ String :username
11
+ String :global_name
12
+ TrueClass :is_bot, default: false, null: false
13
+ TrueClass :is_webhook, default: false, null: false
14
+ TrueClass :contact_info_set, default: false, null: false
15
+ String :custom_mxid
16
+ String :access_token
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:messages) do
6
+ primary_key :id
7
+ String :discord_id, null: false
8
+ String :discord_attachment_id, null: false, default: ""
9
+ String :discord_channel_id, null: false
10
+ String :discord_channel_receiver, null: false, default: ""
11
+ String :discord_sender, null: false
12
+ String :mxid, null: false
13
+ Bignum :timestamp, null: false
14
+ String :discord_thread_id
15
+
16
+ unique [:discord_id, :discord_attachment_id, :discord_channel_id, :discord_channel_receiver]
17
+ index :mxid
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:reactions) do
6
+ primary_key :id
7
+ String :discord_message_id, null: false
8
+ String :discord_sender, null: false
9
+ String :discord_emoji_name, null: false
10
+ String :mxid, null: false
11
+ String :discord_channel_id, null: false
12
+ String :discord_channel_receiver, null: false, default: ""
13
+ String :discord_thread_id
14
+
15
+ unique [:discord_message_id, :discord_sender, :discord_emoji_name]
16
+ index :mxid
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:files) do
6
+ String :url, null: false
7
+ TrueClass :encrypted, null: false, default: false
8
+ String :mxc, null: false
9
+ String :mime_type
10
+ Bignum :size
11
+ Integer :width
12
+ Integer :height
13
+ String :decryption_info
14
+
15
+ primary_key [:url, :encrypted]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ require "bundler/setup"
7
+ require "async/matrix"
8
+
9
+ module Async
10
+ module Matrix
11
+ module Bridge
12
+ module Discord
13
+ module DB
14
+ # Maps a Discord channel to a Matrix room.
15
+ #
16
+ # Uses a composite primary key of (discord_id, receiver). For guild
17
+ # channels, receiver is an empty string. For DMs, receiver is the
18
+ # Discord user ID of the bridge user who owns the portal.
19
+ #
20
+ # portal = Portal.create(discord_id: "chan123", receiver: "", mxid: "!room:x")
21
+ # portal.guild # => Guild or nil
22
+ # portal.messages # => [Message, ...]
23
+ #
24
+ class Portal < Sequel::Model
25
+ unrestrict_primary_key
26
+
27
+ many_to_one :guild,
28
+ class: "Async::Matrix::Bridge::Discord::DB::Guild",
29
+ key: :discord_guild_id,
30
+ primary_key: :discord_id
31
+
32
+ one_to_many :messages,
33
+ class: "Async::Matrix::Bridge::Discord::DB::Message",
34
+ key: [:discord_channel_id, :discord_channel_receiver],
35
+ primary_key: [:discord_id, :receiver]
36
+
37
+ def validate
38
+ super
39
+ errors.add(:discord_id, "cannot be empty") if discord_id.nil? || discord_id.empty?
40
+ end
41
+
42
+ # Is this a DM portal? (has a receiver)
43
+ def dm?
44
+ receiver && !receiver.empty?
45
+ end
46
+
47
+ # Is this a guild channel portal?
48
+ def guild_channel?
49
+ !dm?
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ test do
59
+ describe "Async::Matrix::Bridge::Discord::DB::Portal" do
60
+ def setup_db
61
+ db = Sequel.sqlite
62
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
63
+ db
64
+ end
65
+
66
+ def set_datasets(db)
67
+ Async::Matrix::Bridge::Discord::DB::Portal.dataset = db[:portals]
68
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
69
+ Async::Matrix::Bridge::Discord::DB::Message.dataset = db[:messages]
70
+ end
71
+
72
+ it "creates a guild channel portal" do
73
+ db = setup_db
74
+ set_datasets(db)
75
+
76
+ portal = Async::Matrix::Bridge::Discord::DB::Portal.create(
77
+ discord_id: "chan1", receiver: "", mxid: "!room:x", name: "general"
78
+ )
79
+ portal.discord_id.should == "chan1"
80
+ portal.receiver.should == ""
81
+ portal.guild_channel?.should == true
82
+ portal.dm?.should == false
83
+ db.disconnect
84
+ end
85
+
86
+ it "creates a DM portal with receiver" do
87
+ db = setup_db
88
+ set_datasets(db)
89
+
90
+ portal = Async::Matrix::Bridge::Discord::DB::Portal.create(
91
+ discord_id: "dm1", receiver: "user123", mxid: "!dm:x"
92
+ )
93
+ portal.dm?.should == true
94
+ portal.guild_channel?.should == false
95
+ db.disconnect
96
+ end
97
+
98
+ it "finds by composite primary key" do
99
+ db = setup_db
100
+ set_datasets(db)
101
+
102
+ Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "c1", receiver: "")
103
+ Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "c1", receiver: "u1")
104
+
105
+ found = Async::Matrix::Bridge::Discord::DB::Portal["c1", ""]
106
+ found.should.not.be.nil
107
+ found.receiver.should == ""
108
+
109
+ found2 = Async::Matrix::Bridge::Discord::DB::Portal["c1", "u1"]
110
+ found2.receiver.should == "u1"
111
+ db.disconnect
112
+ end
113
+
114
+ it "associates with a guild" do
115
+ db = setup_db
116
+ set_datasets(db)
117
+
118
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "g_assoc", name: "Guild")
119
+ portal = Async::Matrix::Bridge::Discord::DB::Portal.create(
120
+ discord_id: "c_assoc", receiver: "", discord_guild_id: "g_assoc"
121
+ )
122
+ # Query through the db directly to avoid races with parallel tests
123
+ # that rebind the class-level dataset on Sequel models.
124
+ portal.discord_guild_id.should == "g_assoc"
125
+ linked_guild = db[:guilds].where(discord_id: portal.discord_guild_id).first
126
+ linked_guild[:discord_id].should == "g_assoc"
127
+ linked_portals = db[:portals].where(discord_guild_id: guild.discord_id).all
128
+ linked_portals.length.should == 1
129
+ db.disconnect
130
+ end
131
+
132
+ it "enforces unique mxid" do
133
+ db = setup_db
134
+ set_datasets(db)
135
+
136
+ Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "a", receiver: "", mxid: "!r:x")
137
+ lambda {
138
+ Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "b", receiver: "", mxid: "!r:x")
139
+ }.should.raise(Sequel::UniqueConstraintViolation)
140
+ db.disconnect
141
+ end
142
+
143
+ it "defaults encrypted to false" do
144
+ db = setup_db
145
+ set_datasets(db)
146
+
147
+ portal = Async::Matrix::Bridge::Discord::DB::Portal.create(discord_id: "e1", receiver: "")
148
+ portal.encrypted.should == false
149
+ db.disconnect
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ require "bundler/setup"
7
+ require "async/matrix"
8
+
9
+ module Async
10
+ module Matrix
11
+ module Bridge
12
+ module Discord
13
+ module DB
14
+ # Maps a Discord user to a Matrix ghost user.
15
+ #
16
+ # The ghost's Matrix ID is derived from the username template in the
17
+ # bridge config (e.g., @discord_123456:example.com). This model
18
+ # tracks the Discord user's profile info for syncing to Matrix.
19
+ #
20
+ # puppet = Puppet.create(discord_id: "123", username: "alice", name: "Alice")
21
+ # puppet.is_bot? # => false
22
+ # puppet.double_puppet? # => true (if custom_mxid is set)
23
+ #
24
+ class Puppet < Sequel::Model
25
+ unrestrict_primary_key
26
+
27
+ def validate
28
+ super
29
+ errors.add(:discord_id, "cannot be empty") if discord_id.nil? || discord_id.empty?
30
+ end
31
+
32
+ # Is this puppet configured for double puppeting?
33
+ def double_puppet?
34
+ !custom_mxid.nil? && !custom_mxid.empty?
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ test do
44
+ describe "Async::Matrix::Bridge::Discord::DB::Puppet" do
45
+ def setup_db
46
+ db = Sequel.sqlite
47
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
48
+ db
49
+ end
50
+
51
+ it "creates and retrieves a puppet" do
52
+ db = setup_db
53
+ Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
54
+
55
+ puppet = Async::Matrix::Bridge::Discord::DB::Puppet.create(
56
+ discord_id: "123",
57
+ name: "Alice",
58
+ username: "alice",
59
+ global_name: "Alice Wonderland"
60
+ )
61
+ puppet.discord_id.should == "123"
62
+ puppet.name.should == "Alice"
63
+ puppet.username.should == "alice"
64
+ puppet.global_name.should == "Alice Wonderland"
65
+ db.disconnect
66
+ end
67
+
68
+ it "defaults boolean flags to false" do
69
+ db = setup_db
70
+ Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
71
+
72
+ puppet = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "200")
73
+ puppet.is_bot.should == false
74
+ puppet.is_webhook.should == false
75
+ puppet.contact_info_set.should == false
76
+ db.disconnect
77
+ end
78
+
79
+ it "tracks bot and webhook puppets" do
80
+ db = setup_db
81
+ Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
82
+
83
+ bot = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "300", is_bot: true)
84
+ bot.is_bot.should == true
85
+
86
+ webhook = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "301", is_webhook: true)
87
+ webhook.is_webhook.should == true
88
+ db.disconnect
89
+ end
90
+
91
+ it "detects double puppet configuration" do
92
+ db = setup_db
93
+ Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
94
+
95
+ regular = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "400")
96
+ regular.double_puppet?.should == false
97
+
98
+ double = Async::Matrix::Bridge::Discord::DB::Puppet.create(
99
+ discord_id: "401",
100
+ custom_mxid: "@alice:example.com",
101
+ access_token: "syt_token"
102
+ )
103
+ double.double_puppet?.should == true
104
+ double.access_token.should == "syt_token"
105
+ db.disconnect
106
+ end
107
+
108
+ it "validates discord_id is present" do
109
+ db = setup_db
110
+ Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
111
+
112
+ puppet = Async::Matrix::Bridge::Discord::DB::Puppet.new(discord_id: "")
113
+ puppet.valid?.should == false
114
+ puppet.errors[:discord_id].should.not.be.empty
115
+ db.disconnect
116
+ end
117
+
118
+ it "updates avatar info" do
119
+ db = setup_db
120
+ Async::Matrix::Bridge::Discord::DB::Puppet.dataset = db[:puppets]
121
+
122
+ puppet = Async::Matrix::Bridge::Discord::DB::Puppet.create(discord_id: "500")
123
+ puppet.update(avatar_hash: "abc123", avatar_url: "mxc://example.com/abc")
124
+ puppet.refresh
125
+ puppet.avatar_hash.should == "abc123"
126
+ puppet.avatar_url.should == "mxc://example.com/abc"
127
+ db.disconnect
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ require "bundler/setup"
7
+ require "async/matrix"
8
+
9
+ module Async
10
+ module Matrix
11
+ module Bridge
12
+ module Discord
13
+ module DB
14
+ # Maps a Discord reaction to a Matrix reaction event.
15
+ #
16
+ # Uniquely identified by (discord_message_id, discord_sender,
17
+ # discord_emoji_name) — one reaction per emoji per user per message.
18
+ #
19
+ # reaction = Reaction.create(
20
+ # discord_message_id: "msg1", discord_sender: "user1",
21
+ # discord_emoji_name: "\u{1f44d}", mxid: "$react1",
22
+ # discord_channel_id: "ch1"
23
+ # )
24
+ #
25
+ class Reaction < Sequel::Model
26
+ def validate
27
+ super
28
+ errors.add(:discord_message_id, "cannot be empty") if discord_message_id.nil? || discord_message_id.empty?
29
+ errors.add(:discord_sender, "cannot be empty") if discord_sender.nil? || discord_sender.empty?
30
+ errors.add(:discord_emoji_name, "cannot be empty") if discord_emoji_name.nil? || discord_emoji_name.empty?
31
+ errors.add(:mxid, "cannot be empty") if mxid.nil? || mxid.empty?
32
+ end
33
+
34
+ # Find all reactions on a Discord message.
35
+ def self.by_discord_message(message_id, channel_id, receiver = "")
36
+ where(
37
+ discord_message_id: message_id,
38
+ discord_channel_id: channel_id,
39
+ discord_channel_receiver: receiver
40
+ ).all
41
+ end
42
+
43
+ # Find a specific reaction by its Matrix event ID.
44
+ def self.by_mxid(mxid)
45
+ first(mxid: mxid)
46
+ end
47
+
48
+ # Find a specific reaction by Discord compound key.
49
+ def self.by_discord_key(message_id:, sender:, emoji_name:)
50
+ first(
51
+ discord_message_id: message_id,
52
+ discord_sender: sender,
53
+ discord_emoji_name: emoji_name
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ test do
64
+ describe "Async::Matrix::Bridge::Discord::DB::Reaction" do
65
+ def setup_db
66
+ db = Sequel.sqlite
67
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
68
+ db
69
+ end
70
+
71
+ it "creates and retrieves a reaction" do
72
+ db = setup_db
73
+ Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
74
+
75
+ reaction = Async::Matrix::Bridge::Discord::DB::Reaction.create(
76
+ discord_message_id: "msg1",
77
+ discord_sender: "user1",
78
+ discord_emoji_name: "\u{1f44d}",
79
+ mxid: "$react1",
80
+ discord_channel_id: "ch1"
81
+ )
82
+ reaction.discord_emoji_name.should == "\u{1f44d}"
83
+ reaction.mxid.should == "$react1"
84
+ db.disconnect
85
+ end
86
+
87
+ it "finds reactions by discord message" do
88
+ db = setup_db
89
+ Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
90
+
91
+ Async::Matrix::Bridge::Discord::DB::Reaction.create(
92
+ discord_message_id: "msg2", discord_sender: "u1",
93
+ discord_emoji_name: "\u{1f44d}", mxid: "$r1", discord_channel_id: "ch1"
94
+ )
95
+ Async::Matrix::Bridge::Discord::DB::Reaction.create(
96
+ discord_message_id: "msg2", discord_sender: "u2",
97
+ discord_emoji_name: "\u{2764}", mxid: "$r2", discord_channel_id: "ch1"
98
+ )
99
+
100
+ reactions = Async::Matrix::Bridge::Discord::DB::Reaction.by_discord_message("msg2", "ch1")
101
+ reactions.length.should == 2
102
+ db.disconnect
103
+ end
104
+
105
+ it "finds by Discord compound key" do
106
+ db = setup_db
107
+ Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
108
+
109
+ Async::Matrix::Bridge::Discord::DB::Reaction.create(
110
+ discord_message_id: "msg3", discord_sender: "u1",
111
+ discord_emoji_name: "custom_emoji", mxid: "$r3", discord_channel_id: "ch1"
112
+ )
113
+
114
+ found = Async::Matrix::Bridge::Discord::DB::Reaction.by_discord_key(
115
+ message_id: "msg3", sender: "u1", emoji_name: "custom_emoji"
116
+ )
117
+ found.should.not.be.nil
118
+ found.mxid.should == "$r3"
119
+ db.disconnect
120
+ end
121
+
122
+ it "finds by Matrix event ID" do
123
+ db = setup_db
124
+ Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
125
+
126
+ Async::Matrix::Bridge::Discord::DB::Reaction.create(
127
+ discord_message_id: "msg4", discord_sender: "u1",
128
+ discord_emoji_name: "x", mxid: "$find_react", discord_channel_id: "ch1"
129
+ )
130
+
131
+ found = Async::Matrix::Bridge::Discord::DB::Reaction.by_mxid("$find_react")
132
+ found.discord_message_id.should == "msg4"
133
+ db.disconnect
134
+ end
135
+
136
+ it "enforces unique reaction per user per emoji per message" do
137
+ db = setup_db
138
+ Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
139
+
140
+ Async::Matrix::Bridge::Discord::DB::Reaction.create(
141
+ discord_message_id: "msg5", discord_sender: "u1",
142
+ discord_emoji_name: "x", mxid: "$r5", discord_channel_id: "ch1"
143
+ )
144
+ lambda {
145
+ Async::Matrix::Bridge::Discord::DB::Reaction.create(
146
+ discord_message_id: "msg5", discord_sender: "u1",
147
+ discord_emoji_name: "x", mxid: "$r6", discord_channel_id: "ch1"
148
+ )
149
+ }.should.raise(Sequel::UniqueConstraintViolation)
150
+ db.disconnect
151
+ end
152
+
153
+ it "validates required fields" do
154
+ db = setup_db
155
+ Async::Matrix::Bridge::Discord::DB::Reaction.dataset = db[:reactions]
156
+
157
+ r = Async::Matrix::Bridge::Discord::DB::Reaction.new(
158
+ discord_message_id: "", discord_sender: "",
159
+ discord_emoji_name: "", mxid: ""
160
+ )
161
+ r.valid?.should == false
162
+ r.errors[:discord_message_id].should.not.be.empty
163
+ r.errors[:mxid].should.not.be.empty
164
+ db.disconnect
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ require "bundler/setup"
7
+ require "async/matrix"
8
+
9
+ module Async
10
+ module Matrix
11
+ module Bridge
12
+ module Discord
13
+ module DB
14
+ # Maps a Matrix user to their Discord identity and session state.
15
+ #
16
+ # user = User.create(mxid: "@alice:example.com", discord_id: "123456789")
17
+ # user.discord_id # => "123456789"
18
+ # user.space_room # => "!space:example.com"
19
+ # user.portals # => [Portal, ...]
20
+ #
21
+ class User < Sequel::Model
22
+ unrestrict_primary_key
23
+
24
+ one_to_many :portals,
25
+ class: "Async::Matrix::Bridge::Discord::DB::Portal",
26
+ key: :receiver,
27
+ primary_key: :discord_id
28
+
29
+ def validate
30
+ super
31
+ errors.add(:mxid, "cannot be empty") if mxid.nil? || mxid.empty?
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ test do
41
+ describe "Async::Matrix::Bridge::Discord::DB::User" do
42
+ def setup_db
43
+ db = Sequel.sqlite
44
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
45
+ db
46
+ end
47
+
48
+ it "creates and retrieves a user by mxid" do
49
+ db = setup_db
50
+ Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
51
+
52
+ user = Async::Matrix::Bridge::Discord::DB::User.create(
53
+ mxid: "@alice:example.com",
54
+ discord_id: "123456789"
55
+ )
56
+ user.mxid.should == "@alice:example.com"
57
+ user.discord_id.should == "123456789"
58
+
59
+ found = Async::Matrix::Bridge::Discord::DB::User["@alice:example.com"]
60
+ found.discord_id.should == "123456789"
61
+ db.disconnect
62
+ end
63
+
64
+ it "stores optional session fields" do
65
+ db = setup_db
66
+ Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
67
+
68
+ user = Async::Matrix::Bridge::Discord::DB::User.create(
69
+ mxid: "@bob:example.com",
70
+ discord_token: "secret_token",
71
+ management_room: "!mgmt:example.com",
72
+ space_room: "!space:example.com",
73
+ dm_space_room: "!dm:example.com"
74
+ )
75
+ user.discord_token.should == "secret_token"
76
+ user.management_room.should == "!mgmt:example.com"
77
+ user.space_room.should == "!space:example.com"
78
+ user.dm_space_room.should == "!dm:example.com"
79
+ db.disconnect
80
+ end
81
+
82
+ it "enforces unique discord_id" do
83
+ db = setup_db
84
+ Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
85
+
86
+ Async::Matrix::Bridge::Discord::DB::User.create(mxid: "@a:x", discord_id: "111")
87
+ lambda {
88
+ Async::Matrix::Bridge::Discord::DB::User.create(mxid: "@b:x", discord_id: "111")
89
+ }.should.raise(Sequel::UniqueConstraintViolation)
90
+ db.disconnect
91
+ end
92
+
93
+ it "validates mxid is present" do
94
+ db = setup_db
95
+ Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
96
+
97
+ user = Async::Matrix::Bridge::Discord::DB::User.new(mxid: "")
98
+ user.valid?.should == false
99
+ user.errors[:mxid].should.not.be.empty
100
+ db.disconnect
101
+ end
102
+
103
+ it "updates discord token" do
104
+ db = setup_db
105
+ Async::Matrix::Bridge::Discord::DB::User.dataset = db[:users]
106
+
107
+ user = Async::Matrix::Bridge::Discord::DB::User.create(mxid: "@c:x")
108
+ user.update(discord_token: "new_token")
109
+ user.refresh
110
+ user.discord_token.should == "new_token"
111
+ db.disconnect
112
+ end
113
+ end
114
+ end