async-matrix 2.0.1-aarch64-mingw-ucrt

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 (371) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +200 -0
  3. data/README.md +133 -0
  4. data/data/discord-api-spec/openapi.json +40404 -0
  5. data/data/matrix-spec/api/client-server/account-data.yaml +389 -0
  6. data/data/matrix-spec/api/client-server/account_deactivation.yaml +149 -0
  7. data/data/matrix-spec/api/client-server/admin.yaml +512 -0
  8. data/data/matrix-spec/api/client-server/administrative_contact.yaml +620 -0
  9. data/data/matrix-spec/api/client-server/appservice_ping.yaml +183 -0
  10. data/data/matrix-spec/api/client-server/appservice_room_directory.yaml +104 -0
  11. data/data/matrix-spec/api/client-server/authed-content-repo.yaml +590 -0
  12. data/data/matrix-spec/api/client-server/banning.yaml +177 -0
  13. data/data/matrix-spec/api/client-server/capabilities.yaml +235 -0
  14. data/data/matrix-spec/api/client-server/content-repo.yaml +887 -0
  15. data/data/matrix-spec/api/client-server/create_room.yaml +306 -0
  16. data/data/matrix-spec/api/client-server/cross_signing.yaml +300 -0
  17. data/data/matrix-spec/api/client-server/definitions/auth_data.yaml +34 -0
  18. data/data/matrix-spec/api/client-server/definitions/auth_response.yaml +62 -0
  19. data/data/matrix-spec/api/client-server/definitions/client_device.yaml +44 -0
  20. data/data/matrix-spec/api/client-server/definitions/client_event.yaml +49 -0
  21. data/data/matrix-spec/api/client-server/definitions/client_event_without_room_id.yaml +147 -0
  22. data/data/matrix-spec/api/client-server/definitions/cross_signing_key.yaml +57 -0
  23. data/data/matrix-spec/api/client-server/definitions/device_keys.yaml +70 -0
  24. data/data/matrix-spec/api/client-server/definitions/errors/error.yaml +26 -0
  25. data/data/matrix-spec/api/client-server/definitions/errors/rate_limited.yaml +34 -0
  26. data/data/matrix-spec/api/client-server/definitions/event_batch.yaml +22 -0
  27. data/data/matrix-spec/api/client-server/definitions/event_filter.yaml +55 -0
  28. data/data/matrix-spec/api/client-server/definitions/invite_3pid.yaml +45 -0
  29. data/data/matrix-spec/api/client-server/definitions/key_backup_auth_data.yaml +36 -0
  30. data/data/matrix-spec/api/client-server/definitions/key_backup_data.yaml +50 -0
  31. data/data/matrix-spec/api/client-server/definitions/key_backup_session_data.yaml +57 -0
  32. data/data/matrix-spec/api/client-server/definitions/m.login.terms_params.yaml +83 -0
  33. data/data/matrix-spec/api/client-server/definitions/m.mentions.yaml +33 -0
  34. data/data/matrix-spec/api/client-server/definitions/m.oauth_params.yaml +30 -0
  35. data/data/matrix-spec/api/client-server/definitions/m.relates_to.yaml +45 -0
  36. data/data/matrix-spec/api/client-server/definitions/megolm_export_session_data.yaml +39 -0
  37. data/data/matrix-spec/api/client-server/definitions/olm_payload.yaml +88 -0
  38. data/data/matrix-spec/api/client-server/definitions/one_time_keys.yaml +27 -0
  39. data/data/matrix-spec/api/client-server/definitions/openid_token.yaml +37 -0
  40. data/data/matrix-spec/api/client-server/definitions/protocol.yaml +44 -0
  41. data/data/matrix-spec/api/client-server/definitions/public_rooms_chunk.yaml +74 -0
  42. data/data/matrix-spec/api/client-server/definitions/public_rooms_response.yaml +59 -0
  43. data/data/matrix-spec/api/client-server/definitions/push_condition.yaml +53 -0
  44. data/data/matrix-spec/api/client-server/definitions/push_rule.yaml +53 -0
  45. data/data/matrix-spec/api/client-server/definitions/push_ruleset.yaml +206 -0
  46. data/data/matrix-spec/api/client-server/definitions/recent_emoji.yaml +29 -0
  47. data/data/matrix-spec/api/client-server/definitions/request_email_validation.yaml +36 -0
  48. data/data/matrix-spec/api/client-server/definitions/request_msisdn_validation.yaml +36 -0
  49. data/data/matrix-spec/api/client-server/definitions/request_token_response.yaml +46 -0
  50. data/data/matrix-spec/api/client-server/definitions/room_event_filter.yaml +60 -0
  51. data/data/matrix-spec/api/client-server/definitions/room_key_backup.yaml +37 -0
  52. data/data/matrix-spec/api/client-server/definitions/room_summary.yaml +44 -0
  53. data/data/matrix-spec/api/client-server/definitions/security.yaml +47 -0
  54. data/data/matrix-spec/api/client-server/definitions/sso_login_flow.yaml +98 -0
  55. data/data/matrix-spec/api/client-server/definitions/state_event_batch.yaml +21 -0
  56. data/data/matrix-spec/api/client-server/definitions/sync_filter.yaml +86 -0
  57. data/data/matrix-spec/api/client-server/definitions/tag.yaml +24 -0
  58. data/data/matrix-spec/api/client-server/definitions/third_party_signed.yaml +45 -0
  59. data/data/matrix-spec/api/client-server/definitions/timeline_batch.yaml +36 -0
  60. data/data/matrix-spec/api/client-server/definitions/user_identifier.yaml +27 -0
  61. data/data/matrix-spec/api/client-server/definitions/wellknown/full.yaml +38 -0
  62. data/data/matrix-spec/api/client-server/definitions/wellknown/homeserver.yaml +25 -0
  63. data/data/matrix-spec/api/client-server/definitions/wellknown/identity_server.yaml +25 -0
  64. data/data/matrix-spec/api/client-server/device_management.yaml +314 -0
  65. data/data/matrix-spec/api/client-server/directory.yaml +318 -0
  66. data/data/matrix-spec/api/client-server/event_context.yaml +161 -0
  67. data/data/matrix-spec/api/client-server/filter.yaml +224 -0
  68. data/data/matrix-spec/api/client-server/inviting.yaml +149 -0
  69. data/data/matrix-spec/api/client-server/joining.yaml +238 -0
  70. data/data/matrix-spec/api/client-server/key_backup.yaml +993 -0
  71. data/data/matrix-spec/api/client-server/keys.yaml +482 -0
  72. data/data/matrix-spec/api/client-server/kicking.yaml +110 -0
  73. data/data/matrix-spec/api/client-server/knocking.yaml +152 -0
  74. data/data/matrix-spec/api/client-server/leaving.yaml +163 -0
  75. data/data/matrix-spec/api/client-server/list_joined_rooms.yaml +68 -0
  76. data/data/matrix-spec/api/client-server/list_public_rooms.yaml +280 -0
  77. data/data/matrix-spec/api/client-server/login.yaml +321 -0
  78. data/data/matrix-spec/api/client-server/login_token.yaml +138 -0
  79. data/data/matrix-spec/api/client-server/logout.yaml +86 -0
  80. data/data/matrix-spec/api/client-server/message_pagination.yaml +194 -0
  81. data/data/matrix-spec/api/client-server/notifications.yaml +152 -0
  82. data/data/matrix-spec/api/client-server/oauth_server_metadata.yaml +238 -0
  83. data/data/matrix-spec/api/client-server/old_sync.yaml +379 -0
  84. data/data/matrix-spec/api/client-server/openid.yaml +98 -0
  85. data/data/matrix-spec/api/client-server/password_management.yaml +246 -0
  86. data/data/matrix-spec/api/client-server/peeking_events.yaml +122 -0
  87. data/data/matrix-spec/api/client-server/policy_server.yaml +82 -0
  88. data/data/matrix-spec/api/client-server/presence.yaml +169 -0
  89. data/data/matrix-spec/api/client-server/profile.yaml +366 -0
  90. data/data/matrix-spec/api/client-server/pusher.yaml +303 -0
  91. data/data/matrix-spec/api/client-server/pushrules.yaml +633 -0
  92. data/data/matrix-spec/api/client-server/read_markers.yaml +103 -0
  93. data/data/matrix-spec/api/client-server/receipts.yaml +139 -0
  94. data/data/matrix-spec/api/client-server/redaction.yaml +116 -0
  95. data/data/matrix-spec/api/client-server/refresh.yaml +119 -0
  96. data/data/matrix-spec/api/client-server/registration.yaml +488 -0
  97. data/data/matrix-spec/api/client-server/registration_tokens.yaml +93 -0
  98. data/data/matrix-spec/api/client-server/relations.yaml +382 -0
  99. data/data/matrix-spec/api/client-server/report_content.yaml +283 -0
  100. data/data/matrix-spec/api/client-server/room_event_by_timestamp.yaml +147 -0
  101. data/data/matrix-spec/api/client-server/room_initial_sync.yaml +188 -0
  102. data/data/matrix-spec/api/client-server/room_send.yaml +130 -0
  103. data/data/matrix-spec/api/client-server/room_state.yaml +159 -0
  104. data/data/matrix-spec/api/client-server/room_summary.yaml +138 -0
  105. data/data/matrix-spec/api/client-server/room_upgrades.yaml +130 -0
  106. data/data/matrix-spec/api/client-server/rooms.yaml +380 -0
  107. data/data/matrix-spec/api/client-server/search.yaml +385 -0
  108. data/data/matrix-spec/api/client-server/space_hierarchy.yaml +237 -0
  109. data/data/matrix-spec/api/client-server/sso_login_redirect.yaml +135 -0
  110. data/data/matrix-spec/api/client-server/support.yaml +142 -0
  111. data/data/matrix-spec/api/client-server/sync.yaml +692 -0
  112. data/data/matrix-spec/api/client-server/tags.yaml +183 -0
  113. data/data/matrix-spec/api/client-server/third_party_lookup.yaml +324 -0
  114. data/data/matrix-spec/api/client-server/third_party_membership.yaml +139 -0
  115. data/data/matrix-spec/api/client-server/threads_list.yaml +167 -0
  116. data/data/matrix-spec/api/client-server/to_device.yaml +104 -0
  117. data/data/matrix-spec/api/client-server/typing.yaml +103 -0
  118. data/data/matrix-spec/api/client-server/users.yaml +136 -0
  119. data/data/matrix-spec/api/client-server/versions.yaml +108 -0
  120. data/data/matrix-spec/api/client-server/voip.yaml +93 -0
  121. data/data/matrix-spec/api/client-server/wellknown.yaml +60 -0
  122. data/data/matrix-spec/api/client-server/whoami.yaml +121 -0
  123. data/data/matrix-spec/event-schemas/examples/core/event.json +6 -0
  124. data/data/matrix-spec/event-schemas/examples/core/room_edu.json +3 -0
  125. data/data/matrix-spec/event-schemas/examples/core/room_event.json +11 -0
  126. data/data/matrix-spec/event-schemas/examples/core/state_event.json +4 -0
  127. data/data/matrix-spec/event-schemas/examples/invite_room_state.json +18 -0
  128. data/data/matrix-spec/event-schemas/examples/knock_room_state.json +18 -0
  129. data/data/matrix-spec/event-schemas/examples/m.accepted_terms.yaml +10 -0
  130. data/data/matrix-spec/event-schemas/examples/m.call.answer.yaml +21 -0
  131. data/data/matrix-spec/event-schemas/examples/m.call.candidates.yaml +16 -0
  132. data/data/matrix-spec/event-schemas/examples/m.call.hangup.yaml +10 -0
  133. data/data/matrix-spec/event-schemas/examples/m.call.invite.yaml +22 -0
  134. data/data/matrix-spec/event-schemas/examples/m.call.negotiate.yaml +22 -0
  135. data/data/matrix-spec/event-schemas/examples/m.call.reject.yaml +9 -0
  136. data/data/matrix-spec/event-schemas/examples/m.call.sdp_stream_metadata_changed.yaml +16 -0
  137. data/data/matrix-spec/event-schemas/examples/m.call.select_answer.yaml +10 -0
  138. data/data/matrix-spec/event-schemas/examples/m.direct.yaml +10 -0
  139. data/data/matrix-spec/event-schemas/examples/m.dummy.yaml +4 -0
  140. data/data/matrix-spec/event-schemas/examples/m.forwarded_room_key.yaml +14 -0
  141. data/data/matrix-spec/event-schemas/examples/m.fully_read.yaml +7 -0
  142. data/data/matrix-spec/event-schemas/examples/m.identity_server.yaml +7 -0
  143. data/data/matrix-spec/event-schemas/examples/m.ignored_user_list.yaml +9 -0
  144. data/data/matrix-spec/event-schemas/examples/m.invite_permission_config.yaml +7 -0
  145. data/data/matrix-spec/event-schemas/examples/m.key.verification.accept.yaml +12 -0
  146. data/data/matrix-spec/event-schemas/examples/m.key.verification.cancel.yaml +8 -0
  147. data/data/matrix-spec/event-schemas/examples/m.key.verification.done.yaml +6 -0
  148. data/data/matrix-spec/event-schemas/examples/m.key.verification.key.yaml +7 -0
  149. data/data/matrix-spec/event-schemas/examples/m.key.verification.mac.yaml +10 -0
  150. data/data/matrix-spec/event-schemas/examples/m.key.verification.ready.yaml +10 -0
  151. data/data/matrix-spec/event-schemas/examples/m.key.verification.request.yaml +11 -0
  152. data/data/matrix-spec/event-schemas/examples/m.key.verification.start$m.sas.v1.yaml +12 -0
  153. data/data/matrix-spec/event-schemas/examples/m.key.verification.start.yaml +8 -0
  154. data/data/matrix-spec/event-schemas/examples/m.key_backup.yaml +7 -0
  155. data/data/matrix-spec/event-schemas/examples/m.marked_unread.yaml +7 -0
  156. data/data/matrix-spec/event-schemas/examples/m.policy.rule.room.yaml +10 -0
  157. data/data/matrix-spec/event-schemas/examples/m.policy.rule.server.yaml +10 -0
  158. data/data/matrix-spec/event-schemas/examples/m.policy.rule.user.yaml +10 -0
  159. data/data/matrix-spec/event-schemas/examples/m.presence.yaml +12 -0
  160. data/data/matrix-spec/event-schemas/examples/m.push_rules.yaml +177 -0
  161. data/data/matrix-spec/event-schemas/examples/m.reaction.yaml +11 -0
  162. data/data/matrix-spec/event-schemas/examples/m.receipt.yaml +18 -0
  163. data/data/matrix-spec/event-schemas/examples/m.recent_emoji.yaml +16 -0
  164. data/data/matrix-spec/event-schemas/examples/m.room.avatar.yaml +14 -0
  165. data/data/matrix-spec/event-schemas/examples/m.room.canonical_alias.yaml +12 -0
  166. data/data/matrix-spec/event-schemas/examples/m.room.create.yaml +13 -0
  167. data/data/matrix-spec/event-schemas/examples/m.room.encrypted$megolm.yaml +11 -0
  168. data/data/matrix-spec/event-schemas/examples/m.room.encrypted$olm.yaml +14 -0
  169. data/data/matrix-spec/event-schemas/examples/m.room.encryption.yaml +10 -0
  170. data/data/matrix-spec/event-schemas/examples/m.room.guest_access.yaml +8 -0
  171. data/data/matrix-spec/event-schemas/examples/m.room.history_visibility.yaml +8 -0
  172. data/data/matrix-spec/event-schemas/examples/m.room.join_rules$restricted.yaml +18 -0
  173. data/data/matrix-spec/event-schemas/examples/m.room.join_rules.yaml +8 -0
  174. data/data/matrix-spec/event-schemas/examples/m.room.member$invite_room_state.yaml +15 -0
  175. data/data/matrix-spec/event-schemas/examples/m.room.member$join_authorised_via_users_server.yaml +12 -0
  176. data/data/matrix-spec/event-schemas/examples/m.room.member$knock_room_state.yaml +15 -0
  177. data/data/matrix-spec/event-schemas/examples/m.room.member$third_party_invite.yaml +20 -0
  178. data/data/matrix-spec/event-schemas/examples/m.room.member.yaml +12 -0
  179. data/data/matrix-spec/event-schemas/examples/m.room.message$m.audio.yaml +14 -0
  180. data/data/matrix-spec/event-schemas/examples/m.room.message$m.emote.yaml +10 -0
  181. data/data/matrix-spec/event-schemas/examples/m.room.message$m.file.yaml +14 -0
  182. data/data/matrix-spec/event-schemas/examples/m.room.message$m.image.yaml +16 -0
  183. data/data/matrix-spec/event-schemas/examples/m.room.message$m.key.verification.request.yaml +19 -0
  184. data/data/matrix-spec/event-schemas/examples/m.room.message$m.location.yaml +18 -0
  185. data/data/matrix-spec/event-schemas/examples/m.room.message$m.notice.yaml +10 -0
  186. data/data/matrix-spec/event-schemas/examples/m.room.message$m.server_notice.yaml +11 -0
  187. data/data/matrix-spec/event-schemas/examples/m.room.message$m.text.yaml +10 -0
  188. data/data/matrix-spec/event-schemas/examples/m.room.message$m.video.yaml +23 -0
  189. data/data/matrix-spec/event-schemas/examples/m.room.name.yaml +8 -0
  190. data/data/matrix-spec/event-schemas/examples/m.room.pinned_events.yaml +8 -0
  191. data/data/matrix-spec/event-schemas/examples/m.room.policy.yaml +11 -0
  192. data/data/matrix-spec/event-schemas/examples/m.room.power_levels.yaml +24 -0
  193. data/data/matrix-spec/event-schemas/examples/m.room.redaction.yaml +8 -0
  194. data/data/matrix-spec/event-schemas/examples/m.room.server_acl.yaml +10 -0
  195. data/data/matrix-spec/event-schemas/examples/m.room.third_party_invite.yaml +14 -0
  196. data/data/matrix-spec/event-schemas/examples/m.room.tombstone.yaml +9 -0
  197. data/data/matrix-spec/event-schemas/examples/m.room.topic.yaml +16 -0
  198. data/data/matrix-spec/event-schemas/examples/m.room_key.withheld.yaml +12 -0
  199. data/data/matrix-spec/event-schemas/examples/m.room_key.yaml +10 -0
  200. data/data/matrix-spec/event-schemas/examples/m.room_key_request$cancel_request.yaml +8 -0
  201. data/data/matrix-spec/event-schemas/examples/m.room_key_request$request.yaml +14 -0
  202. data/data/matrix-spec/event-schemas/examples/m.secret.request.yaml +9 -0
  203. data/data/matrix-spec/event-schemas/examples/m.secret.send.yaml +7 -0
  204. data/data/matrix-spec/event-schemas/examples/m.space.child.yaml +10 -0
  205. data/data/matrix-spec/event-schemas/examples/m.space.parent.yaml +9 -0
  206. data/data/matrix-spec/event-schemas/examples/m.sticker.yaml +22 -0
  207. data/data/matrix-spec/event-schemas/examples/m.tag.yaml +9 -0
  208. data/data/matrix-spec/event-schemas/examples/m.typing.yaml +7 -0
  209. data/data/matrix-spec/event-schemas/moderation_policy_rule.yaml +32 -0
  210. data/data/matrix-spec/event-schemas/schema/components/m_text_content_block.yaml +28 -0
  211. data/data/matrix-spec/event-schemas/schema/components/sdp_stream_metadata.yaml +43 -0
  212. data/data/matrix-spec/event-schemas/schema/components/signed_third_party_invite.yaml +48 -0
  213. data/data/matrix-spec/event-schemas/schema/core-event-schema/call_event.yaml +27 -0
  214. data/data/matrix-spec/event-schemas/schema/core-event-schema/event.yaml +16 -0
  215. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/avatar_info.yaml +30 -0
  216. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml +52 -0
  217. data/data/matrix-spec/event-schemas/schema/core-event-schema/msgtype_infos/thumbnail_info.yaml +22 -0
  218. data/data/matrix-spec/event-schemas/schema/core-event-schema/room_event.yaml +17 -0
  219. data/data/matrix-spec/event-schemas/schema/core-event-schema/state_event.yaml +8 -0
  220. data/data/matrix-spec/event-schemas/schema/core-event-schema/stripped_state.yaml +48 -0
  221. data/data/matrix-spec/event-schemas/schema/core-event-schema/sync_room_event.yaml +49 -0
  222. data/data/matrix-spec/event-schemas/schema/core-event-schema/sync_state_event.yaml +36 -0
  223. data/data/matrix-spec/event-schemas/schema/core-event-schema/unsigned_prop.yaml +23 -0
  224. data/data/matrix-spec/event-schemas/schema/m.accepted_terms.yaml +25 -0
  225. data/data/matrix-spec/event-schemas/schema/m.call.answer.yaml +44 -0
  226. data/data/matrix-spec/event-schemas/schema/m.call.candidates.yaml +52 -0
  227. data/data/matrix-spec/event-schemas/schema/m.call.hangup.yaml +57 -0
  228. data/data/matrix-spec/event-schemas/schema/m.call.invite.yaml +53 -0
  229. data/data/matrix-spec/event-schemas/schema/m.call.negotiate.yaml +78 -0
  230. data/data/matrix-spec/event-schemas/schema/m.call.reject.yaml +29 -0
  231. data/data/matrix-spec/event-schemas/schema/m.call.sdp_stream_metadata_changed.yaml +24 -0
  232. data/data/matrix-spec/event-schemas/schema/m.call.select_answer.yaml +29 -0
  233. data/data/matrix-spec/event-schemas/schema/m.direct.yaml +29 -0
  234. data/data/matrix-spec/event-schemas/schema/m.dummy.yaml +25 -0
  235. data/data/matrix-spec/event-schemas/schema/m.forwarded_room_key.yaml +70 -0
  236. data/data/matrix-spec/event-schemas/schema/m.fully_read.yaml +26 -0
  237. data/data/matrix-spec/event-schemas/schema/m.identity_server.yaml +28 -0
  238. data/data/matrix-spec/event-schemas/schema/m.ignored_user_list.yaml +30 -0
  239. data/data/matrix-spec/event-schemas/schema/m.invite_permission_config.yaml +21 -0
  240. data/data/matrix-spec/event-schemas/schema/m.key.verification.accept.yaml +63 -0
  241. data/data/matrix-spec/event-schemas/schema/m.key.verification.cancel.yaml +72 -0
  242. data/data/matrix-spec/event-schemas/schema/m.key.verification.done.yaml +24 -0
  243. data/data/matrix-spec/event-schemas/schema/m.key.verification.key.yaml +31 -0
  244. data/data/matrix-spec/event-schemas/schema/m.key.verification.m.relates_to.yaml +23 -0
  245. data/data/matrix-spec/event-schemas/schema/m.key.verification.mac.yaml +43 -0
  246. data/data/matrix-spec/event-schemas/schema/m.key.verification.ready.yaml +41 -0
  247. data/data/matrix-spec/event-schemas/schema/m.key.verification.request.yaml +47 -0
  248. data/data/matrix-spec/event-schemas/schema/m.key.verification.start$m.reciprocate.v1.yaml +45 -0
  249. data/data/matrix-spec/event-schemas/schema/m.key.verification.start$m.sas.v1.yaml +76 -0
  250. data/data/matrix-spec/event-schemas/schema/m.key.verification.start.yaml +46 -0
  251. data/data/matrix-spec/event-schemas/schema/m.key_backup.yaml +24 -0
  252. data/data/matrix-spec/event-schemas/schema/m.marked_unread.yaml +26 -0
  253. data/data/matrix-spec/event-schemas/schema/m.policy.rule.room.yaml +17 -0
  254. data/data/matrix-spec/event-schemas/schema/m.policy.rule.server.yaml +17 -0
  255. data/data/matrix-spec/event-schemas/schema/m.policy.rule.user.yaml +17 -0
  256. data/data/matrix-spec/event-schemas/schema/m.presence.yaml +50 -0
  257. data/data/matrix-spec/event-schemas/schema/m.push_rules.yaml +23 -0
  258. data/data/matrix-spec/event-schemas/schema/m.reaction.yaml +45 -0
  259. data/data/matrix-spec/event-schemas/schema/m.receipt.yaml +65 -0
  260. data/data/matrix-spec/event-schemas/schema/m.recent_emoji.yaml +29 -0
  261. data/data/matrix-spec/event-schemas/schema/m.room.avatar.yaml +29 -0
  262. data/data/matrix-spec/event-schemas/schema/m.room.canonical_alias.yaml +40 -0
  263. data/data/matrix-spec/event-schemas/schema/m.room.create.yaml +86 -0
  264. data/data/matrix-spec/event-schemas/schema/m.room.encrypted.yaml +91 -0
  265. data/data/matrix-spec/event-schemas/schema/m.room.encryption.yaml +38 -0
  266. data/data/matrix-spec/event-schemas/schema/m.room.guest_access.yaml +28 -0
  267. data/data/matrix-spec/event-schemas/schema/m.room.history_visibility.yaml +30 -0
  268. data/data/matrix-spec/event-schemas/schema/m.room.join_rules.yaml +78 -0
  269. data/data/matrix-spec/event-schemas/schema/m.room.member.yaml +174 -0
  270. data/data/matrix-spec/event-schemas/schema/m.room.message$m.audio.yaml +73 -0
  271. data/data/matrix-spec/event-schemas/schema/m.room.message$m.emote.yaml +36 -0
  272. data/data/matrix-spec/event-schemas/schema/m.room.message$m.file.yaml +85 -0
  273. data/data/matrix-spec/event-schemas/schema/m.room.message$m.image.yaml +63 -0
  274. data/data/matrix-spec/event-schemas/schema/m.room.message$m.key.verification.request.yaml +70 -0
  275. data/data/matrix-spec/event-schemas/schema/m.room.message$m.location.yaml +50 -0
  276. data/data/matrix-spec/event-schemas/schema/m.room.message$m.notice.yaml +36 -0
  277. data/data/matrix-spec/event-schemas/schema/m.room.message$m.server_notice.yaml +41 -0
  278. data/data/matrix-spec/event-schemas/schema/m.room.message$m.text.yaml +36 -0
  279. data/data/matrix-spec/event-schemas/schema/m.room.message$m.video.yaml +95 -0
  280. data/data/matrix-spec/event-schemas/schema/m.room.message.yaml +25 -0
  281. data/data/matrix-spec/event-schemas/schema/m.room.name.yaml +35 -0
  282. data/data/matrix-spec/event-schemas/schema/m.room.pinned_events.yaml +27 -0
  283. data/data/matrix-spec/event-schemas/schema/m.room.policy.yaml +41 -0
  284. data/data/matrix-spec/event-schemas/schema/m.room.power_levels.yaml +139 -0
  285. data/data/matrix-spec/event-schemas/schema/m.room.redaction.yaml +29 -0
  286. data/data/matrix-spec/event-schemas/schema/m.room.server_acl.yaml +88 -0
  287. data/data/matrix-spec/event-schemas/schema/m.room.third_party_invite.yaml +79 -0
  288. data/data/matrix-spec/event-schemas/schema/m.room.tombstone.yaml +29 -0
  289. data/data/matrix-spec/event-schemas/schema/m.room.topic.yaml +53 -0
  290. data/data/matrix-spec/event-schemas/schema/m.room_key.withheld.yaml +88 -0
  291. data/data/matrix-spec/event-schemas/schema/m.room_key.yaml +38 -0
  292. data/data/matrix-spec/event-schemas/schema/m.room_key_request.yaml +73 -0
  293. data/data/matrix-spec/event-schemas/schema/m.secret.request.yaml +42 -0
  294. data/data/matrix-spec/event-schemas/schema/m.secret.send.yaml +35 -0
  295. data/data/matrix-spec/event-schemas/schema/m.space.child.yaml +50 -0
  296. data/data/matrix-spec/event-schemas/schema/m.space.parent.yaml +36 -0
  297. data/data/matrix-spec/event-schemas/schema/m.sticker.yaml +36 -0
  298. data/data/matrix-spec/event-schemas/schema/m.tag.yaml +28 -0
  299. data/data/matrix-spec/event-schemas/schema/m.typing.yaml +29 -0
  300. data/lib/async/discord/api/path_tree.rb +127 -0
  301. data/lib/async/discord/api.rb +151 -0
  302. data/lib/async/discord/client.rb +283 -0
  303. data/lib/async/discord/error.rb +84 -0
  304. data/lib/async/discord/gateway.rb +359 -0
  305. data/lib/async/discord.rb +15 -0
  306. data/lib/async/matrix/3.4/async_matrix_e2ee.so +0 -0
  307. data/lib/async/matrix/4.0/async_matrix_e2ee.so +0 -0
  308. data/lib/async/matrix/api/chain.rb +578 -0
  309. data/lib/async/matrix/api/concat.rb +101 -0
  310. data/lib/async/matrix/api/path_tree.rb +205 -0
  311. data/lib/async/matrix/api.rb +200 -0
  312. data/lib/async/matrix/application_service/bot.rb +232 -0
  313. data/lib/async/matrix/application_service/config/schema/analytics.json +21 -0
  314. data/lib/async/matrix/application_service/config/schema/appservice.json +82 -0
  315. data/lib/async/matrix/application_service/config/schema/backfill.json +91 -0
  316. data/lib/async/matrix/application_service/config/schema/bridge.json +209 -0
  317. data/lib/async/matrix/application_service/config/schema/config.json +61 -0
  318. data/lib/async/matrix/application_service/config/schema/database.json +38 -0
  319. data/lib/async/matrix/application_service/config/schema/direct_media.json +35 -0
  320. data/lib/async/matrix/application_service/config/schema/double_puppet.json +24 -0
  321. data/lib/async/matrix/application_service/config/schema/encryption.json +164 -0
  322. data/lib/async/matrix/application_service/config/schema/homeserver.json +58 -0
  323. data/lib/async/matrix/application_service/config/schema/logging.json +50 -0
  324. data/lib/async/matrix/application_service/config/schema/management_room_texts.json +25 -0
  325. data/lib/async/matrix/application_service/config/schema/matrix.json +45 -0
  326. data/lib/async/matrix/application_service/config/schema/permissions.json +54 -0
  327. data/lib/async/matrix/application_service/config/schema/provisioning.json +23 -0
  328. data/lib/async/matrix/application_service/config/schema/public_media.json +39 -0
  329. data/lib/async/matrix/application_service/config/schema/relay.json +43 -0
  330. data/lib/async/matrix/application_service/config/vivify.rb +109 -0
  331. data/lib/async/matrix/application_service/config.rb +225 -0
  332. data/lib/async/matrix/application_service/dispatcher.rb +185 -0
  333. data/lib/async/matrix/application_service/error_response.rb +37 -0
  334. data/lib/async/matrix/application_service/event.rb +285 -0
  335. data/lib/async/matrix/application_service/server.rb +430 -0
  336. data/lib/async/matrix/application_service/transaction.rb +66 -0
  337. data/lib/async/matrix/application_service/transaction_handler.rb +185 -0
  338. data/lib/async/matrix/application_service/transaction_store.rb +80 -0
  339. data/lib/async/matrix/bridge/discord/db/connection.rb +141 -0
  340. data/lib/async/matrix/bridge/discord/db/file.rb +118 -0
  341. data/lib/async/matrix/bridge/discord/db/guild.rb +120 -0
  342. data/lib/async/matrix/bridge/discord/db/message.rb +160 -0
  343. data/lib/async/matrix/bridge/discord/db/migrations/001_create_users.rb +14 -0
  344. data/lib/async/matrix/bridge/discord/db/migrations/002_create_guilds.rb +14 -0
  345. data/lib/async/matrix/bridge/discord/db/migrations/003_create_portals.rb +23 -0
  346. data/lib/async/matrix/bridge/discord/db/migrations/004_create_puppets.rb +19 -0
  347. data/lib/async/matrix/bridge/discord/db/migrations/005_create_messages.rb +20 -0
  348. data/lib/async/matrix/bridge/discord/db/migrations/006_create_reactions.rb +19 -0
  349. data/lib/async/matrix/bridge/discord/db/migrations/007_create_files.rb +18 -0
  350. data/lib/async/matrix/bridge/discord/db/portal.rb +150 -0
  351. data/lib/async/matrix/bridge/discord/db/puppet.rb +128 -0
  352. data/lib/async/matrix/bridge/discord/db/reaction.rb +165 -0
  353. data/lib/async/matrix/bridge/discord/db/schema.rb +18 -0
  354. data/lib/async/matrix/bridge/discord/db/user.rb +112 -0
  355. data/lib/async/matrix/bridge/discord/db.rb +138 -0
  356. data/lib/async/matrix/client.rb +934 -0
  357. data/lib/async/matrix/connection.rb +19 -0
  358. data/lib/async/matrix/double_puppet_client.rb +82 -0
  359. data/lib/async/matrix/e2ee.rb +92 -0
  360. data/lib/async/matrix/endpoint.rb +57 -0
  361. data/lib/async/matrix/error.rb +67 -0
  362. data/lib/async/matrix/media_client.rb +170 -0
  363. data/lib/async/matrix/notifier.rb +104 -0
  364. data/lib/async/matrix/schema/registry.rb +354 -0
  365. data/lib/async/matrix/schema/validation_error.rb +225 -0
  366. data/lib/async/matrix/schema.rb +170 -0
  367. data/lib/async/matrix/server.rb +18 -0
  368. data/lib/async/matrix/stream.rb +18 -0
  369. data/lib/async/matrix/version.rb +10 -0
  370. data/lib/async/matrix.rb +22 -0
  371. metadata +587 -0
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the Apache License, Version 2.0.
4
+ # Copyright, 2026, by General Intelligence Systems.
5
+
6
+ module Async
7
+ module Matrix
8
+ module ApplicationService
9
+ # In-memory idempotency store for appservice transaction IDs.
10
+ class TransactionStore
11
+ DEFAULT_CAPACITY = 1024
12
+
13
+ def initialize(capacity: DEFAULT_CAPACITY)
14
+ @seen = {}
15
+ @capacity = capacity
16
+ end
17
+
18
+ def seen?(txn_id)
19
+ @seen.key?(txn_id)
20
+ end
21
+
22
+ def mark_seen(txn_id)
23
+ prune! if @seen.size >= @capacity
24
+ @seen[txn_id] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
25
+ end
26
+
27
+ def size
28
+ @seen.size
29
+ end
30
+
31
+ private
32
+
33
+ def prune!
34
+ sorted = @seen.sort_by { |_, ts| ts }
35
+ drop = sorted.size / 2
36
+ sorted.first(drop).each { |id, _| @seen.delete(id) }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ __END__
44
+ describe "Async::Matrix::ApplicationService::TransactionStore" do
45
+ it "tracks seen transaction IDs" do
46
+ store = Async::Matrix::ApplicationService::TransactionStore.new
47
+ store.seen?("txn1").should == false
48
+ store.mark_seen("txn1")
49
+ store.seen?("txn1").should == true
50
+ end
51
+
52
+ it "reports size" do
53
+ store = Async::Matrix::ApplicationService::TransactionStore.new
54
+ store.size.should == 0
55
+ store.mark_seen("a")
56
+ store.mark_seen("b")
57
+ store.size.should == 2
58
+ end
59
+
60
+ it "prunes oldest entries when capacity is reached" do
61
+ store = Async::Matrix::ApplicationService::TransactionStore.new(capacity: 4)
62
+ store.mark_seen("a")
63
+ store.mark_seen("b")
64
+ store.mark_seen("c")
65
+ store.mark_seen("d")
66
+ # This triggers prune — drops oldest half (a, b)
67
+ store.mark_seen("e")
68
+ store.seen?("a").should == false
69
+ store.seen?("b").should == false
70
+ store.seen?("d").should == true
71
+ store.seen?("e").should == true
72
+ end
73
+
74
+ it "does not prune below capacity" do
75
+ store = Async::Matrix::ApplicationService::TransactionStore.new(capacity: 10)
76
+ 5.times { |i| store.mark_seen("txn#{i}") }
77
+ store.size.should == 5
78
+ store.seen?("txn0").should == true
79
+ end
80
+ end
@@ -0,0 +1,141 @@
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 "sequel"
7
+
8
+ module Async
9
+ module Matrix
10
+ module Bridge
11
+ module Discord
12
+ module DB
13
+ # Establishes a Sequel database connection from the bridge config's
14
+ # database section.
15
+ #
16
+ # Supports both SQLite (with foreign keys and WAL mode) and PostgreSQL,
17
+ # matching the mautrix bridgev2 database config schema.
18
+ #
19
+ # db = Connection.establish(config.database)
20
+ # db[:users].all # => [...]
21
+ #
22
+ module Connection
23
+ SQLITE_TYPE = "sqlite3-fk-wal"
24
+ POSTGRES_TYPE = "postgres"
25
+
26
+ # Establish a database connection from a config.database section.
27
+ #
28
+ # @param database_config [Hash, Vivify] config.database with :type, :uri, :max_open_conns
29
+ # @return [Sequel::Database]
30
+ def self.establish(database_config)
31
+ type = database_config.type || SQLITE_TYPE
32
+ uri = database_config.uri || "sqlite://bridge.db"
33
+
34
+ db = case type
35
+ when SQLITE_TYPE
36
+ connect_sqlite(uri, database_config)
37
+ when POSTGRES_TYPE
38
+ connect_postgres(uri, database_config)
39
+ else
40
+ raise ArgumentError, "Unknown database type: #{type.inspect}. Expected #{SQLITE_TYPE.inspect} or #{POSTGRES_TYPE.inspect}."
41
+ end
42
+
43
+ db
44
+ end
45
+
46
+ class << self
47
+ private
48
+
49
+ def connect_sqlite(uri, config)
50
+ # Normalize mautrix-style URIs: "file:bridge.db?..." -> "sqlite://bridge.db"
51
+ sequel_uri = if uri.start_with?("file:")
52
+ path = uri.sub(%r{\Afile:}, "").split("?").first
53
+ "sqlite://#{path}"
54
+ elsif uri.start_with?("sqlite://")
55
+ uri
56
+ else
57
+ "sqlite://#{uri}"
58
+ end
59
+
60
+ db = Sequel.connect(sequel_uri, max_connections: max_conns(config))
61
+ db.run("PRAGMA foreign_keys = ON")
62
+ db.run("PRAGMA journal_mode = WAL")
63
+ db
64
+ end
65
+
66
+ def connect_postgres(uri, config)
67
+ Sequel.connect(uri, max_connections: max_conns(config))
68
+ end
69
+
70
+ def max_conns(config)
71
+ val = config.respond_to?(:max_open_conns) ? config.max_open_conns : nil
72
+ val && val > 0 ? val : 5
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ __END__
83
+ describe "Async::Matrix::Bridge::Discord::DB::Connection" do
84
+ it "connects to an in-memory SQLite database" do
85
+ config = Object.new
86
+ config.define_singleton_method(:type) { "sqlite3-fk-wal" }
87
+ config.define_singleton_method(:uri) { "sqlite:/" }
88
+ config.define_singleton_method(:max_open_conns) { 1 }
89
+
90
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
91
+ db.should.be.kind_of Sequel::Database
92
+ db.database_type.should == :sqlite
93
+ db.disconnect
94
+ end
95
+
96
+ it "enables foreign keys and WAL on SQLite" do
97
+ config = Object.new
98
+ config.define_singleton_method(:type) { "sqlite3-fk-wal" }
99
+ config.define_singleton_method(:uri) { "sqlite:/" }
100
+ config.define_singleton_method(:max_open_conns) { 1 }
101
+
102
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
103
+ fk = db["PRAGMA foreign_keys"].first
104
+ fk[:foreign_keys].should == 1
105
+ db.disconnect
106
+ end
107
+
108
+ it "normalizes mautrix file: URIs to sequel sqlite:// URIs" do
109
+ config = Object.new
110
+ config.define_singleton_method(:type) { "sqlite3-fk-wal" }
111
+ config.define_singleton_method(:uri) { "file:test.db?_txlock=immediate" }
112
+ config.define_singleton_method(:max_open_conns) { 1 }
113
+
114
+ # Should not raise — the URI is normalized
115
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
116
+ db.should.be.kind_of Sequel::Database
117
+ db.disconnect
118
+ end
119
+
120
+ it "defaults to sqlite with 5 connections when config is empty" do
121
+ config = Object.new
122
+ config.define_singleton_method(:type) { nil }
123
+ config.define_singleton_method(:uri) { nil }
124
+ config.define_singleton_method(:max_open_conns) { nil }
125
+
126
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
127
+ db.should.be.kind_of Sequel::Database
128
+ db.disconnect
129
+ end
130
+
131
+ it "raises on unknown database type" do
132
+ config = Object.new
133
+ config.define_singleton_method(:type) { "mysql" }
134
+ config.define_singleton_method(:uri) { "mysql://localhost/db" }
135
+ config.define_singleton_method(:max_open_conns) { 1 }
136
+
137
+ lambda {
138
+ Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
139
+ }.should.raise(ArgumentError)
140
+ end
141
+ end
@@ -0,0 +1,118 @@
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_relative "schema"
7
+
8
+ module Async
9
+ module Matrix
10
+ module Bridge
11
+ module Discord
12
+ module DB
13
+ # Caches uploaded media to avoid re-uploading the same Discord
14
+ # attachment to the Matrix homeserver.
15
+ #
16
+ # Uses a composite primary key of (url, encrypted) because the same
17
+ # source URL may be uploaded both encrypted and unencrypted to
18
+ # different portal rooms.
19
+ #
20
+ # cached = CachedFile.create(
21
+ # url: "https://cdn.discordapp.com/...",
22
+ # encrypted: false,
23
+ # mxc: "mxc://example.com/abc"
24
+ # )
25
+ #
26
+ class CachedFile < Sequel::Model
27
+ unrestrict_primary_key
28
+
29
+ def validate
30
+ super
31
+ errors.add(:url, "cannot be empty") if url.nil? || url.empty?
32
+ errors.add(:mxc, "cannot be empty") if mxc.nil? || mxc.empty?
33
+ end
34
+
35
+ # Look up a cached file by source URL and encryption status.
36
+ def self.by_url(url, encrypted: false)
37
+ first(url: url, encrypted: encrypted)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ __END__
47
+ describe "Async::Matrix::Bridge::Discord::DB::CachedFile" do
48
+ def setup_db
49
+ db = Sequel.sqlite
50
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
51
+ db
52
+ end
53
+
54
+ it "creates and retrieves a cached file" do
55
+ db = setup_db
56
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
57
+
58
+ cached = Async::Matrix::Bridge::Discord::DB::CachedFile.create(
59
+ url: "https://cdn.discordapp.com/attachments/123/456/image.png",
60
+ mxc: "mxc://example.com/abc",
61
+ mime_type: "image/png",
62
+ size: 102400,
63
+ width: 800,
64
+ height: 600
65
+ )
66
+ cached.url.should.include "discordapp.com"
67
+ cached.mxc.should == "mxc://example.com/abc"
68
+ cached.mime_type.should == "image/png"
69
+ cached.width.should == 800
70
+ cached.height.should == 600
71
+ db.disconnect
72
+ end
73
+
74
+ it "looks up by URL and encryption status" do
75
+ db = setup_db
76
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
77
+
78
+ Async::Matrix::Bridge::Discord::DB::CachedFile.create(
79
+ url: "https://example.com/file.png", encrypted: false,
80
+ mxc: "mxc://x/plain"
81
+ )
82
+ Async::Matrix::Bridge::Discord::DB::CachedFile.create(
83
+ url: "https://example.com/file.png", encrypted: true,
84
+ mxc: "mxc://x/encrypted",
85
+ decryption_info: '{"key":"abc"}'
86
+ )
87
+
88
+ plain = Async::Matrix::Bridge::Discord::DB::CachedFile.by_url("https://example.com/file.png")
89
+ plain.mxc.should == "mxc://x/plain"
90
+
91
+ enc = Async::Matrix::Bridge::Discord::DB::CachedFile.by_url("https://example.com/file.png", encrypted: true)
92
+ enc.mxc.should == "mxc://x/encrypted"
93
+ enc.decryption_info.should == '{"key":"abc"}'
94
+ db.disconnect
95
+ end
96
+
97
+ it "defaults encrypted to false" do
98
+ db = setup_db
99
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
100
+
101
+ cached = Async::Matrix::Bridge::Discord::DB::CachedFile.create(
102
+ url: "https://example.com/a.jpg", mxc: "mxc://x/y"
103
+ )
104
+ cached.encrypted.should == false
105
+ db.disconnect
106
+ end
107
+
108
+ it "validates required fields" do
109
+ db = setup_db
110
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
111
+
112
+ f = Async::Matrix::Bridge::Discord::DB::CachedFile.new(url: "", mxc: "")
113
+ f.valid?.should == false
114
+ f.errors[:url].should.not.be.empty
115
+ f.errors[:mxc].should.not.be.empty
116
+ db.disconnect
117
+ end
118
+ end
@@ -0,0 +1,120 @@
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_relative "schema"
7
+
8
+ module Async
9
+ module Matrix
10
+ module Bridge
11
+ module Discord
12
+ module DB
13
+ # Maps a Discord guild (server) to a Matrix Space room.
14
+ #
15
+ # Bridging modes control how aggressively the bridge creates portals:
16
+ # BRIDGE_NOTHING = 0 — never bridge
17
+ # BRIDGE_IF_PORTAL_EXISTS = 1 — only bridge existing portals
18
+ # BRIDGE_CREATE_ON_MESSAGE = 2 — create portals on first message
19
+ # BRIDGE_EVERYTHING = 3 — proactively create all portals
20
+ #
21
+ # guild = Guild.create(discord_id: "999", name: "My Server", bridging_mode: 3)
22
+ # guild.portals # => [Portal, ...]
23
+ #
24
+ class Guild < Sequel::Model
25
+ unrestrict_primary_key
26
+
27
+ BRIDGE_NOTHING = 0
28
+ BRIDGE_IF_PORTAL_EXISTS = 1
29
+ BRIDGE_CREATE_ON_MESSAGE = 2
30
+ BRIDGE_EVERYTHING = 3
31
+
32
+ one_to_many :portals,
33
+ class: "Async::Matrix::Bridge::Discord::DB::Portal",
34
+ key: :discord_guild_id,
35
+ primary_key: :discord_id
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
+ def bridge_nothing?
43
+ bridging_mode == BRIDGE_NOTHING
44
+ end
45
+
46
+ def bridge_everything?
47
+ bridging_mode == BRIDGE_EVERYTHING
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ __END__
57
+ describe "Async::Matrix::Bridge::Discord::DB::Guild" do
58
+ def setup_db
59
+ db = Sequel.sqlite
60
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
61
+ db
62
+ end
63
+
64
+ it "creates and retrieves a guild" do
65
+ db = setup_db
66
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
67
+
68
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.create(
69
+ discord_id: "999",
70
+ name: "Test Guild",
71
+ mxid: "!space:example.com"
72
+ )
73
+ guild.discord_id.should == "999"
74
+ guild.name.should == "Test Guild"
75
+ guild.mxid.should == "!space:example.com"
76
+ db.disconnect
77
+ end
78
+
79
+ it "defaults bridging_mode to 0 (nothing)" do
80
+ db = setup_db
81
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
82
+
83
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "100")
84
+ guild.bridging_mode.should == 0
85
+ guild.bridge_nothing?.should == true
86
+ guild.bridge_everything?.should == false
87
+ db.disconnect
88
+ end
89
+
90
+ it "supports all bridging modes" do
91
+ db = setup_db
92
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
93
+
94
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "200", bridging_mode: 3)
95
+ guild.bridge_everything?.should == true
96
+ guild.bridge_nothing?.should == false
97
+ db.disconnect
98
+ end
99
+
100
+ it "validates discord_id is present" do
101
+ db = setup_db
102
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
103
+
104
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.new(discord_id: "")
105
+ guild.valid?.should == false
106
+ guild.errors[:discord_id].should.not.be.empty
107
+ db.disconnect
108
+ end
109
+
110
+ it "enforces unique mxid" do
111
+ db = setup_db
112
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
113
+
114
+ Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "300", mxid: "!a:x")
115
+ lambda {
116
+ Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "301", mxid: "!a:x")
117
+ }.should.raise(Sequel::UniqueConstraintViolation)
118
+ db.disconnect
119
+ end
120
+ end
@@ -0,0 +1,160 @@
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_relative "schema"
7
+
8
+ module Async
9
+ module Matrix
10
+ module Bridge
11
+ module Discord
12
+ module DB
13
+ # Maps a Discord message to one or more Matrix events.
14
+ #
15
+ # A single Discord message may produce multiple Matrix events (one per
16
+ # attachment), distinguished by discord_attachment_id. The composite
17
+ # unique index on (discord_id, discord_attachment_id, discord_channel_id,
18
+ # discord_channel_receiver) ensures no duplicates.
19
+ #
20
+ # msg = Message.create(
21
+ # discord_id: "msg1", discord_channel_id: "ch1",
22
+ # discord_sender: "user1", mxid: "$evt1", timestamp: 1234567890
23
+ # )
24
+ # msg.portal # => Portal
25
+ #
26
+ class Message < Sequel::Model
27
+ many_to_one :portal,
28
+ class: "Async::Matrix::Bridge::Discord::DB::Portal",
29
+ key: [:discord_channel_id, :discord_channel_receiver],
30
+ primary_key: [:discord_id, :receiver]
31
+
32
+ def validate
33
+ super
34
+ errors.add(:discord_id, "cannot be empty") if discord_id.nil? || discord_id.empty?
35
+ errors.add(:mxid, "cannot be empty") if mxid.nil? || mxid.empty?
36
+ errors.add(:discord_sender, "cannot be empty") if discord_sender.nil? || discord_sender.empty?
37
+ end
38
+
39
+ # Find all parts of a Discord message (text + attachments).
40
+ def self.by_discord_id(discord_id, channel_id, receiver = "")
41
+ where(
42
+ discord_id: discord_id,
43
+ discord_channel_id: channel_id,
44
+ discord_channel_receiver: receiver
45
+ ).order(:discord_attachment_id).all
46
+ end
47
+
48
+ # Find a message by its Matrix event ID.
49
+ def self.by_mxid(mxid)
50
+ first(mxid: mxid)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ __END__
60
+ describe "Async::Matrix::Bridge::Discord::DB::Message" do
61
+ def setup_db
62
+ db = Sequel.sqlite
63
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
64
+ db
65
+ end
66
+
67
+ def set_datasets(db)
68
+ Async::Matrix::Bridge::Discord::DB::Message.dataset = db[:messages]
69
+ Async::Matrix::Bridge::Discord::DB::Portal.dataset = db[:portals]
70
+ end
71
+
72
+ it "creates and retrieves a message" do
73
+ db = setup_db
74
+ set_datasets(db)
75
+
76
+ msg = Async::Matrix::Bridge::Discord::DB::Message.create(
77
+ discord_id: "msg1",
78
+ discord_channel_id: "ch1",
79
+ discord_sender: "user1",
80
+ mxid: "$evt1",
81
+ timestamp: 1234567890
82
+ )
83
+ msg.discord_id.should == "msg1"
84
+ msg.mxid.should == "$evt1"
85
+ msg.discord_attachment_id.should == ""
86
+ db.disconnect
87
+ end
88
+
89
+ it "supports multi-part messages with attachments" do
90
+ db = setup_db
91
+ set_datasets(db)
92
+
93
+ Async::Matrix::Bridge::Discord::DB::Message.create(
94
+ discord_id: "msg2", discord_attachment_id: "",
95
+ discord_channel_id: "ch1", discord_sender: "u1",
96
+ mxid: "$text", timestamp: 100
97
+ )
98
+ Async::Matrix::Bridge::Discord::DB::Message.create(
99
+ discord_id: "msg2", discord_attachment_id: "att1",
100
+ discord_channel_id: "ch1", discord_sender: "u1",
101
+ mxid: "$img1", timestamp: 100
102
+ )
103
+ Async::Matrix::Bridge::Discord::DB::Message.create(
104
+ discord_id: "msg2", discord_attachment_id: "att2",
105
+ discord_channel_id: "ch1", discord_sender: "u1",
106
+ mxid: "$img2", timestamp: 100
107
+ )
108
+
109
+ parts = Async::Matrix::Bridge::Discord::DB::Message.by_discord_id("msg2", "ch1")
110
+ parts.length.should == 3
111
+ parts.map(&:mxid).should == ["$text", "$img1", "$img2"]
112
+ db.disconnect
113
+ end
114
+
115
+ it "finds by Matrix event ID" do
116
+ db = setup_db
117
+ set_datasets(db)
118
+
119
+ Async::Matrix::Bridge::Discord::DB::Message.create(
120
+ discord_id: "msg3", discord_channel_id: "ch1",
121
+ discord_sender: "u1", mxid: "$find_me", timestamp: 200
122
+ )
123
+
124
+ found = Async::Matrix::Bridge::Discord::DB::Message.by_mxid("$find_me")
125
+ found.should.not.be.nil
126
+ found.discord_id.should == "msg3"
127
+ db.disconnect
128
+ end
129
+
130
+ it "enforces unique composite key" do
131
+ db = setup_db
132
+ set_datasets(db)
133
+
134
+ Async::Matrix::Bridge::Discord::DB::Message.create(
135
+ discord_id: "msg4", discord_attachment_id: "",
136
+ discord_channel_id: "ch1", discord_channel_receiver: "",
137
+ discord_sender: "u1", mxid: "$a", timestamp: 300
138
+ )
139
+ lambda {
140
+ Async::Matrix::Bridge::Discord::DB::Message.create(
141
+ discord_id: "msg4", discord_attachment_id: "",
142
+ discord_channel_id: "ch1", discord_channel_receiver: "",
143
+ discord_sender: "u1", mxid: "$b", timestamp: 300
144
+ )
145
+ }.should.raise(Sequel::UniqueConstraintViolation)
146
+ db.disconnect
147
+ end
148
+
149
+ it "validates required fields" do
150
+ db = setup_db
151
+ set_datasets(db)
152
+
153
+ msg = Async::Matrix::Bridge::Discord::DB::Message.new(discord_id: "", mxid: "", discord_sender: "")
154
+ msg.valid?.should == false
155
+ msg.errors[:discord_id].should.not.be.empty
156
+ msg.errors[:mxid].should.not.be.empty
157
+ msg.errors[:discord_sender].should.not.be.empty
158
+ db.disconnect
159
+ end
160
+ end
@@ -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