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
@@ -7,78 +7,78 @@ require "bundler/setup"
7
7
  require "async/matrix"
8
8
 
9
9
  module Async
10
- module Matrix
11
- module ApplicationService
12
- # In-memory idempotency store for appservice transaction IDs.
13
- class TransactionStore
14
- DEFAULT_CAPACITY = 1024
10
+ module Matrix
11
+ module ApplicationService
12
+ # In-memory idempotency store for appservice transaction IDs.
13
+ class TransactionStore
14
+ DEFAULT_CAPACITY = 1024
15
15
 
16
- def initialize(capacity: DEFAULT_CAPACITY)
17
- @seen = {}
18
- @capacity = capacity
19
- end
16
+ def initialize(capacity: DEFAULT_CAPACITY)
17
+ @seen = {}
18
+ @capacity = capacity
19
+ end
20
20
 
21
- def seen?(txn_id)
22
- @seen.key?(txn_id)
23
- end
21
+ def seen?(txn_id)
22
+ @seen.key?(txn_id)
23
+ end
24
24
 
25
- def mark_seen(txn_id)
26
- prune! if @seen.size >= @capacity
27
- @seen[txn_id] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
28
- end
25
+ def mark_seen(txn_id)
26
+ prune! if @seen.size >= @capacity
27
+ @seen[txn_id] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
28
+ end
29
29
 
30
- def size
31
- @seen.size
32
- end
30
+ def size
31
+ @seen.size
32
+ end
33
33
 
34
- private
34
+ private
35
35
 
36
- def prune!
37
- sorted = @seen.sort_by { |_, ts| ts }
38
- drop = sorted.size / 2
39
- sorted.first(drop).each { |id, _| @seen.delete(id) }
40
- end
41
- end
42
- end
43
- end
36
+ def prune!
37
+ sorted = @seen.sort_by { |_, ts| ts }
38
+ drop = sorted.size / 2
39
+ sorted.first(drop).each { |id, _| @seen.delete(id) }
40
+ end
41
+ end
42
+ end
43
+ end
44
44
  end
45
45
 
46
46
  test do
47
- describe "Async::Matrix::ApplicationService::TransactionStore" do
48
- it "tracks seen transaction IDs" do
49
- store = Async::Matrix::ApplicationService::TransactionStore.new
50
- store.seen?("txn1").should == false
51
- store.mark_seen("txn1")
52
- store.seen?("txn1").should == true
53
- end
47
+ describe "Async::Matrix::ApplicationService::TransactionStore" do
48
+ it "tracks seen transaction IDs" do
49
+ store = Async::Matrix::ApplicationService::TransactionStore.new
50
+ store.seen?("txn1").should == false
51
+ store.mark_seen("txn1")
52
+ store.seen?("txn1").should == true
53
+ end
54
54
 
55
- it "reports size" do
56
- store = Async::Matrix::ApplicationService::TransactionStore.new
57
- store.size.should == 0
58
- store.mark_seen("a")
59
- store.mark_seen("b")
60
- store.size.should == 2
61
- end
55
+ it "reports size" do
56
+ store = Async::Matrix::ApplicationService::TransactionStore.new
57
+ store.size.should == 0
58
+ store.mark_seen("a")
59
+ store.mark_seen("b")
60
+ store.size.should == 2
61
+ end
62
62
 
63
- it "prunes oldest entries when capacity is reached" do
64
- store = Async::Matrix::ApplicationService::TransactionStore.new(capacity: 4)
65
- store.mark_seen("a")
66
- store.mark_seen("b")
67
- store.mark_seen("c")
68
- store.mark_seen("d")
69
- # This triggers prune — drops oldest half (a, b)
70
- store.mark_seen("e")
71
- store.seen?("a").should == false
72
- store.seen?("b").should == false
73
- store.seen?("d").should == true
74
- store.seen?("e").should == true
75
- end
63
+ it "prunes oldest entries when capacity is reached" do
64
+ store = Async::Matrix::ApplicationService::TransactionStore.new(capacity: 4)
65
+ store.mark_seen("a")
66
+ store.mark_seen("b")
67
+ store.mark_seen("c")
68
+ store.mark_seen("d")
69
+ # This triggers prune — drops oldest half (a, b)
70
+ store.mark_seen("e")
71
+ store.seen?("a").should == false
72
+ store.seen?("b").should == false
73
+ store.seen?("d").should == true
74
+ store.seen?("e").should == true
75
+ end
76
76
 
77
- it "does not prune below capacity" do
78
- store = Async::Matrix::ApplicationService::TransactionStore.new(capacity: 10)
79
- 5.times { |i| store.mark_seen("txn#{i}") }
80
- store.size.should == 5
81
- store.seen?("txn0").should == true
82
- end
83
- end
77
+ it "does not prune below capacity" do
78
+ store = Async::Matrix::ApplicationService::TransactionStore.new(capacity: 10)
79
+ 5.times { |i| store.mark_seen("txn#{i}") }
80
+ store.size.should == 5
81
+ store.seen?("txn0").should == true
82
+ end
83
+ end
84
84
  end
@@ -0,0 +1,143 @@
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 "sequel"
8
+
9
+ module Async
10
+ module Matrix
11
+ module Bridge
12
+ module Discord
13
+ module DB
14
+ # Establishes a Sequel database connection from the bridge config's
15
+ # database section.
16
+ #
17
+ # Supports both SQLite (with foreign keys and WAL mode) and PostgreSQL,
18
+ # matching the mautrix bridgev2 database config schema.
19
+ #
20
+ # db = Connection.establish(config.database)
21
+ # db[:users].all # => [...]
22
+ #
23
+ module Connection
24
+ SQLITE_TYPE = "sqlite3-fk-wal"
25
+ POSTGRES_TYPE = "postgres"
26
+
27
+ # Establish a database connection from a config.database section.
28
+ #
29
+ # @param database_config [Hash, Vivify] config.database with :type, :uri, :max_open_conns
30
+ # @return [Sequel::Database]
31
+ def self.establish(database_config)
32
+ type = database_config.type || SQLITE_TYPE
33
+ uri = database_config.uri || "sqlite://bridge.db"
34
+
35
+ db = case type
36
+ when SQLITE_TYPE
37
+ connect_sqlite(uri, database_config)
38
+ when POSTGRES_TYPE
39
+ connect_postgres(uri, database_config)
40
+ else
41
+ raise ArgumentError, "Unknown database type: #{type.inspect}. Expected #{SQLITE_TYPE.inspect} or #{POSTGRES_TYPE.inspect}."
42
+ end
43
+
44
+ db
45
+ end
46
+
47
+ class << self
48
+ private
49
+
50
+ def connect_sqlite(uri, config)
51
+ # Normalize mautrix-style URIs: "file:bridge.db?..." -> "sqlite://bridge.db"
52
+ sequel_uri = if uri.start_with?("file:")
53
+ path = uri.sub(%r{\Afile:}, "").split("?").first
54
+ "sqlite://#{path}"
55
+ elsif uri.start_with?("sqlite://")
56
+ uri
57
+ else
58
+ "sqlite://#{uri}"
59
+ end
60
+
61
+ db = Sequel.connect(sequel_uri, max_connections: max_conns(config))
62
+ db.run("PRAGMA foreign_keys = ON")
63
+ db.run("PRAGMA journal_mode = WAL")
64
+ db
65
+ end
66
+
67
+ def connect_postgres(uri, config)
68
+ Sequel.connect(uri, max_connections: max_conns(config))
69
+ end
70
+
71
+ def max_conns(config)
72
+ val = config.respond_to?(:max_open_conns) ? config.max_open_conns : nil
73
+ val && val > 0 ? val : 5
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ test do
84
+ describe "Async::Matrix::Bridge::Discord::DB::Connection" do
85
+ it "connects to an in-memory SQLite database" do
86
+ config = Object.new
87
+ config.define_singleton_method(:type) { "sqlite3-fk-wal" }
88
+ config.define_singleton_method(:uri) { "sqlite:/" }
89
+ config.define_singleton_method(:max_open_conns) { 1 }
90
+
91
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
92
+ db.should.be.kind_of Sequel::Database
93
+ db.database_type.should == :sqlite
94
+ db.disconnect
95
+ end
96
+
97
+ it "enables foreign keys and WAL on SQLite" do
98
+ config = Object.new
99
+ config.define_singleton_method(:type) { "sqlite3-fk-wal" }
100
+ config.define_singleton_method(:uri) { "sqlite:/" }
101
+ config.define_singleton_method(:max_open_conns) { 1 }
102
+
103
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
104
+ fk = db["PRAGMA foreign_keys"].first
105
+ fk[:foreign_keys].should == 1
106
+ db.disconnect
107
+ end
108
+
109
+ it "normalizes mautrix file: URIs to sequel sqlite:// URIs" do
110
+ config = Object.new
111
+ config.define_singleton_method(:type) { "sqlite3-fk-wal" }
112
+ config.define_singleton_method(:uri) { "file:test.db?_txlock=immediate" }
113
+ config.define_singleton_method(:max_open_conns) { 1 }
114
+
115
+ # Should not raise — the URI is normalized
116
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
117
+ db.should.be.kind_of Sequel::Database
118
+ db.disconnect
119
+ end
120
+
121
+ it "defaults to sqlite with 5 connections when config is empty" do
122
+ config = Object.new
123
+ config.define_singleton_method(:type) { nil }
124
+ config.define_singleton_method(:uri) { nil }
125
+ config.define_singleton_method(:max_open_conns) { nil }
126
+
127
+ db = Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
128
+ db.should.be.kind_of Sequel::Database
129
+ db.disconnect
130
+ end
131
+
132
+ it "raises on unknown database type" do
133
+ config = Object.new
134
+ config.define_singleton_method(:type) { "mysql" }
135
+ config.define_singleton_method(:uri) { "mysql://localhost/db" }
136
+ config.define_singleton_method(:max_open_conns) { 1 }
137
+
138
+ lambda {
139
+ Async::Matrix::Bridge::Discord::DB::Connection.establish(config)
140
+ }.should.raise(ArgumentError)
141
+ end
142
+ end
143
+ 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 "bundler/setup"
7
+ require "async/matrix"
8
+
9
+ module Async
10
+ module Matrix
11
+ module Bridge
12
+ module Discord
13
+ module DB
14
+ # Caches uploaded media to avoid re-uploading the same Discord
15
+ # attachment to the Matrix homeserver.
16
+ #
17
+ # Uses a composite primary key of (url, encrypted) because the same
18
+ # source URL may be uploaded both encrypted and unencrypted to
19
+ # different portal rooms.
20
+ #
21
+ # cached = CachedFile.create(
22
+ # url: "https://cdn.discordapp.com/...",
23
+ # encrypted: false,
24
+ # mxc: "mxc://example.com/abc"
25
+ # )
26
+ #
27
+ class CachedFile < Sequel::Model
28
+ unrestrict_primary_key
29
+
30
+ def validate
31
+ super
32
+ errors.add(:url, "cannot be empty") if url.nil? || url.empty?
33
+ errors.add(:mxc, "cannot be empty") if mxc.nil? || mxc.empty?
34
+ end
35
+
36
+ # Look up a cached file by source URL and encryption status.
37
+ def self.by_url(url, encrypted: false)
38
+ first(url: url, encrypted: encrypted)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ test do
48
+ describe "Async::Matrix::Bridge::Discord::DB::CachedFile" do
49
+ def setup_db
50
+ db = Sequel.sqlite
51
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
52
+ db
53
+ end
54
+
55
+ it "creates and retrieves a cached file" do
56
+ db = setup_db
57
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
58
+
59
+ cached = Async::Matrix::Bridge::Discord::DB::CachedFile.create(
60
+ url: "https://cdn.discordapp.com/attachments/123/456/image.png",
61
+ mxc: "mxc://example.com/abc",
62
+ mime_type: "image/png",
63
+ size: 102400,
64
+ width: 800,
65
+ height: 600
66
+ )
67
+ cached.url.should.include "discordapp.com"
68
+ cached.mxc.should == "mxc://example.com/abc"
69
+ cached.mime_type.should == "image/png"
70
+ cached.width.should == 800
71
+ cached.height.should == 600
72
+ db.disconnect
73
+ end
74
+
75
+ it "looks up by URL and encryption status" do
76
+ db = setup_db
77
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
78
+
79
+ Async::Matrix::Bridge::Discord::DB::CachedFile.create(
80
+ url: "https://example.com/file.png", encrypted: false,
81
+ mxc: "mxc://x/plain"
82
+ )
83
+ Async::Matrix::Bridge::Discord::DB::CachedFile.create(
84
+ url: "https://example.com/file.png", encrypted: true,
85
+ mxc: "mxc://x/encrypted",
86
+ decryption_info: '{"key":"abc"}'
87
+ )
88
+
89
+ plain = Async::Matrix::Bridge::Discord::DB::CachedFile.by_url("https://example.com/file.png")
90
+ plain.mxc.should == "mxc://x/plain"
91
+
92
+ enc = Async::Matrix::Bridge::Discord::DB::CachedFile.by_url("https://example.com/file.png", encrypted: true)
93
+ enc.mxc.should == "mxc://x/encrypted"
94
+ enc.decryption_info.should == '{"key":"abc"}'
95
+ db.disconnect
96
+ end
97
+
98
+ it "defaults encrypted to false" do
99
+ db = setup_db
100
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
101
+
102
+ cached = Async::Matrix::Bridge::Discord::DB::CachedFile.create(
103
+ url: "https://example.com/a.jpg", mxc: "mxc://x/y"
104
+ )
105
+ cached.encrypted.should == false
106
+ db.disconnect
107
+ end
108
+
109
+ it "validates required fields" do
110
+ db = setup_db
111
+ Async::Matrix::Bridge::Discord::DB::CachedFile.dataset = db[:files]
112
+
113
+ f = Async::Matrix::Bridge::Discord::DB::CachedFile.new(url: "", mxc: "")
114
+ f.valid?.should == false
115
+ f.errors[:url].should.not.be.empty
116
+ f.errors[:mxc].should.not.be.empty
117
+ db.disconnect
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,122 @@
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 guild (server) to a Matrix Space room.
15
+ #
16
+ # Bridging modes control how aggressively the bridge creates portals:
17
+ # BRIDGE_NOTHING = 0 — never bridge
18
+ # BRIDGE_IF_PORTAL_EXISTS = 1 — only bridge existing portals
19
+ # BRIDGE_CREATE_ON_MESSAGE = 2 — create portals on first message
20
+ # BRIDGE_EVERYTHING = 3 — proactively create all portals
21
+ #
22
+ # guild = Guild.create(discord_id: "999", name: "My Server", bridging_mode: 3)
23
+ # guild.portals # => [Portal, ...]
24
+ #
25
+ class Guild < Sequel::Model
26
+ unrestrict_primary_key
27
+
28
+ BRIDGE_NOTHING = 0
29
+ BRIDGE_IF_PORTAL_EXISTS = 1
30
+ BRIDGE_CREATE_ON_MESSAGE = 2
31
+ BRIDGE_EVERYTHING = 3
32
+
33
+ one_to_many :portals,
34
+ class: "Async::Matrix::Bridge::Discord::DB::Portal",
35
+ key: :discord_guild_id,
36
+ primary_key: :discord_id
37
+
38
+ def validate
39
+ super
40
+ errors.add(:discord_id, "cannot be empty") if discord_id.nil? || discord_id.empty?
41
+ end
42
+
43
+ def bridge_nothing?
44
+ bridging_mode == BRIDGE_NOTHING
45
+ end
46
+
47
+ def bridge_everything?
48
+ bridging_mode == BRIDGE_EVERYTHING
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ test do
58
+ describe "Async::Matrix::Bridge::Discord::DB::Guild" do
59
+ def setup_db
60
+ db = Sequel.sqlite
61
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
62
+ db
63
+ end
64
+
65
+ it "creates and retrieves a guild" do
66
+ db = setup_db
67
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
68
+
69
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.create(
70
+ discord_id: "999",
71
+ name: "Test Guild",
72
+ mxid: "!space:example.com"
73
+ )
74
+ guild.discord_id.should == "999"
75
+ guild.name.should == "Test Guild"
76
+ guild.mxid.should == "!space:example.com"
77
+ db.disconnect
78
+ end
79
+
80
+ it "defaults bridging_mode to 0 (nothing)" do
81
+ db = setup_db
82
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
83
+
84
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "100")
85
+ guild.bridging_mode.should == 0
86
+ guild.bridge_nothing?.should == true
87
+ guild.bridge_everything?.should == false
88
+ db.disconnect
89
+ end
90
+
91
+ it "supports all bridging modes" do
92
+ db = setup_db
93
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
94
+
95
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "200", bridging_mode: 3)
96
+ guild.bridge_everything?.should == true
97
+ guild.bridge_nothing?.should == false
98
+ db.disconnect
99
+ end
100
+
101
+ it "validates discord_id is present" do
102
+ db = setup_db
103
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
104
+
105
+ guild = Async::Matrix::Bridge::Discord::DB::Guild.new(discord_id: "")
106
+ guild.valid?.should == false
107
+ guild.errors[:discord_id].should.not.be.empty
108
+ db.disconnect
109
+ end
110
+
111
+ it "enforces unique mxid" do
112
+ db = setup_db
113
+ Async::Matrix::Bridge::Discord::DB::Guild.dataset = db[:guilds]
114
+
115
+ Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "300", mxid: "!a:x")
116
+ lambda {
117
+ Async::Matrix::Bridge::Discord::DB::Guild.create(discord_id: "301", mxid: "!a:x")
118
+ }.should.raise(Sequel::UniqueConstraintViolation)
119
+ db.disconnect
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,162 @@
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 message to one or more Matrix events.
15
+ #
16
+ # A single Discord message may produce multiple Matrix events (one per
17
+ # attachment), distinguished by discord_attachment_id. The composite
18
+ # unique index on (discord_id, discord_attachment_id, discord_channel_id,
19
+ # discord_channel_receiver) ensures no duplicates.
20
+ #
21
+ # msg = Message.create(
22
+ # discord_id: "msg1", discord_channel_id: "ch1",
23
+ # discord_sender: "user1", mxid: "$evt1", timestamp: 1234567890
24
+ # )
25
+ # msg.portal # => Portal
26
+ #
27
+ class Message < Sequel::Model
28
+ many_to_one :portal,
29
+ class: "Async::Matrix::Bridge::Discord::DB::Portal",
30
+ key: [:discord_channel_id, :discord_channel_receiver],
31
+ primary_key: [:discord_id, :receiver]
32
+
33
+ def validate
34
+ super
35
+ errors.add(:discord_id, "cannot be empty") if discord_id.nil? || discord_id.empty?
36
+ errors.add(:mxid, "cannot be empty") if mxid.nil? || mxid.empty?
37
+ errors.add(:discord_sender, "cannot be empty") if discord_sender.nil? || discord_sender.empty?
38
+ end
39
+
40
+ # Find all parts of a Discord message (text + attachments).
41
+ def self.by_discord_id(discord_id, channel_id, receiver = "")
42
+ where(
43
+ discord_id: discord_id,
44
+ discord_channel_id: channel_id,
45
+ discord_channel_receiver: receiver
46
+ ).order(:discord_attachment_id).all
47
+ end
48
+
49
+ # Find a message by its Matrix event ID.
50
+ def self.by_mxid(mxid)
51
+ first(mxid: mxid)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ test do
61
+ describe "Async::Matrix::Bridge::Discord::DB::Message" do
62
+ def setup_db
63
+ db = Sequel.sqlite
64
+ Sequel::Migrator.run(db, File.join(__dir__, "migrations"))
65
+ db
66
+ end
67
+
68
+ def set_datasets(db)
69
+ Async::Matrix::Bridge::Discord::DB::Message.dataset = db[:messages]
70
+ Async::Matrix::Bridge::Discord::DB::Portal.dataset = db[:portals]
71
+ end
72
+
73
+ it "creates and retrieves a message" do
74
+ db = setup_db
75
+ set_datasets(db)
76
+
77
+ msg = Async::Matrix::Bridge::Discord::DB::Message.create(
78
+ discord_id: "msg1",
79
+ discord_channel_id: "ch1",
80
+ discord_sender: "user1",
81
+ mxid: "$evt1",
82
+ timestamp: 1234567890
83
+ )
84
+ msg.discord_id.should == "msg1"
85
+ msg.mxid.should == "$evt1"
86
+ msg.discord_attachment_id.should == ""
87
+ db.disconnect
88
+ end
89
+
90
+ it "supports multi-part messages with attachments" do
91
+ db = setup_db
92
+ set_datasets(db)
93
+
94
+ Async::Matrix::Bridge::Discord::DB::Message.create(
95
+ discord_id: "msg2", discord_attachment_id: "",
96
+ discord_channel_id: "ch1", discord_sender: "u1",
97
+ mxid: "$text", timestamp: 100
98
+ )
99
+ Async::Matrix::Bridge::Discord::DB::Message.create(
100
+ discord_id: "msg2", discord_attachment_id: "att1",
101
+ discord_channel_id: "ch1", discord_sender: "u1",
102
+ mxid: "$img1", timestamp: 100
103
+ )
104
+ Async::Matrix::Bridge::Discord::DB::Message.create(
105
+ discord_id: "msg2", discord_attachment_id: "att2",
106
+ discord_channel_id: "ch1", discord_sender: "u1",
107
+ mxid: "$img2", timestamp: 100
108
+ )
109
+
110
+ parts = Async::Matrix::Bridge::Discord::DB::Message.by_discord_id("msg2", "ch1")
111
+ parts.length.should == 3
112
+ parts.map(&:mxid).should == ["$text", "$img1", "$img2"]
113
+ db.disconnect
114
+ end
115
+
116
+ it "finds by Matrix event ID" do
117
+ db = setup_db
118
+ set_datasets(db)
119
+
120
+ Async::Matrix::Bridge::Discord::DB::Message.create(
121
+ discord_id: "msg3", discord_channel_id: "ch1",
122
+ discord_sender: "u1", mxid: "$find_me", timestamp: 200
123
+ )
124
+
125
+ found = Async::Matrix::Bridge::Discord::DB::Message.by_mxid("$find_me")
126
+ found.should.not.be.nil
127
+ found.discord_id.should == "msg3"
128
+ db.disconnect
129
+ end
130
+
131
+ it "enforces unique composite key" do
132
+ db = setup_db
133
+ set_datasets(db)
134
+
135
+ Async::Matrix::Bridge::Discord::DB::Message.create(
136
+ discord_id: "msg4", discord_attachment_id: "",
137
+ discord_channel_id: "ch1", discord_channel_receiver: "",
138
+ discord_sender: "u1", mxid: "$a", timestamp: 300
139
+ )
140
+ lambda {
141
+ Async::Matrix::Bridge::Discord::DB::Message.create(
142
+ discord_id: "msg4", discord_attachment_id: "",
143
+ discord_channel_id: "ch1", discord_channel_receiver: "",
144
+ discord_sender: "u1", mxid: "$b", timestamp: 300
145
+ )
146
+ }.should.raise(Sequel::UniqueConstraintViolation)
147
+ db.disconnect
148
+ end
149
+
150
+ it "validates required fields" do
151
+ db = setup_db
152
+ set_datasets(db)
153
+
154
+ msg = Async::Matrix::Bridge::Discord::DB::Message.new(discord_id: "", mxid: "", discord_sender: "")
155
+ msg.valid?.should == false
156
+ msg.errors[:discord_id].should.not.be.empty
157
+ msg.errors[:mxid].should.not.be.empty
158
+ msg.errors[:discord_sender].should.not.be.empty
159
+ db.disconnect
160
+ end
161
+ end
162
+ end