async-matrix 2.0.1-aarch64-linux-musl

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 (372) 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.3/async_matrix_e2ee.so +0 -0
  307. data/lib/async/matrix/3.4/async_matrix_e2ee.so +0 -0
  308. data/lib/async/matrix/4.0/async_matrix_e2ee.so +0 -0
  309. data/lib/async/matrix/api/chain.rb +578 -0
  310. data/lib/async/matrix/api/concat.rb +101 -0
  311. data/lib/async/matrix/api/path_tree.rb +205 -0
  312. data/lib/async/matrix/api.rb +200 -0
  313. data/lib/async/matrix/application_service/bot.rb +232 -0
  314. data/lib/async/matrix/application_service/config/schema/analytics.json +21 -0
  315. data/lib/async/matrix/application_service/config/schema/appservice.json +82 -0
  316. data/lib/async/matrix/application_service/config/schema/backfill.json +91 -0
  317. data/lib/async/matrix/application_service/config/schema/bridge.json +209 -0
  318. data/lib/async/matrix/application_service/config/schema/config.json +61 -0
  319. data/lib/async/matrix/application_service/config/schema/database.json +38 -0
  320. data/lib/async/matrix/application_service/config/schema/direct_media.json +35 -0
  321. data/lib/async/matrix/application_service/config/schema/double_puppet.json +24 -0
  322. data/lib/async/matrix/application_service/config/schema/encryption.json +164 -0
  323. data/lib/async/matrix/application_service/config/schema/homeserver.json +58 -0
  324. data/lib/async/matrix/application_service/config/schema/logging.json +50 -0
  325. data/lib/async/matrix/application_service/config/schema/management_room_texts.json +25 -0
  326. data/lib/async/matrix/application_service/config/schema/matrix.json +45 -0
  327. data/lib/async/matrix/application_service/config/schema/permissions.json +54 -0
  328. data/lib/async/matrix/application_service/config/schema/provisioning.json +23 -0
  329. data/lib/async/matrix/application_service/config/schema/public_media.json +39 -0
  330. data/lib/async/matrix/application_service/config/schema/relay.json +43 -0
  331. data/lib/async/matrix/application_service/config/vivify.rb +109 -0
  332. data/lib/async/matrix/application_service/config.rb +225 -0
  333. data/lib/async/matrix/application_service/dispatcher.rb +185 -0
  334. data/lib/async/matrix/application_service/error_response.rb +37 -0
  335. data/lib/async/matrix/application_service/event.rb +285 -0
  336. data/lib/async/matrix/application_service/server.rb +430 -0
  337. data/lib/async/matrix/application_service/transaction.rb +66 -0
  338. data/lib/async/matrix/application_service/transaction_handler.rb +185 -0
  339. data/lib/async/matrix/application_service/transaction_store.rb +80 -0
  340. data/lib/async/matrix/bridge/discord/db/connection.rb +141 -0
  341. data/lib/async/matrix/bridge/discord/db/file.rb +118 -0
  342. data/lib/async/matrix/bridge/discord/db/guild.rb +120 -0
  343. data/lib/async/matrix/bridge/discord/db/message.rb +160 -0
  344. data/lib/async/matrix/bridge/discord/db/migrations/001_create_users.rb +14 -0
  345. data/lib/async/matrix/bridge/discord/db/migrations/002_create_guilds.rb +14 -0
  346. data/lib/async/matrix/bridge/discord/db/migrations/003_create_portals.rb +23 -0
  347. data/lib/async/matrix/bridge/discord/db/migrations/004_create_puppets.rb +19 -0
  348. data/lib/async/matrix/bridge/discord/db/migrations/005_create_messages.rb +20 -0
  349. data/lib/async/matrix/bridge/discord/db/migrations/006_create_reactions.rb +19 -0
  350. data/lib/async/matrix/bridge/discord/db/migrations/007_create_files.rb +18 -0
  351. data/lib/async/matrix/bridge/discord/db/portal.rb +150 -0
  352. data/lib/async/matrix/bridge/discord/db/puppet.rb +128 -0
  353. data/lib/async/matrix/bridge/discord/db/reaction.rb +165 -0
  354. data/lib/async/matrix/bridge/discord/db/schema.rb +18 -0
  355. data/lib/async/matrix/bridge/discord/db/user.rb +112 -0
  356. data/lib/async/matrix/bridge/discord/db.rb +138 -0
  357. data/lib/async/matrix/client.rb +934 -0
  358. data/lib/async/matrix/connection.rb +19 -0
  359. data/lib/async/matrix/double_puppet_client.rb +82 -0
  360. data/lib/async/matrix/e2ee.rb +92 -0
  361. data/lib/async/matrix/endpoint.rb +57 -0
  362. data/lib/async/matrix/error.rb +67 -0
  363. data/lib/async/matrix/media_client.rb +170 -0
  364. data/lib/async/matrix/notifier.rb +104 -0
  365. data/lib/async/matrix/schema/registry.rb +354 -0
  366. data/lib/async/matrix/schema/validation_error.rb +225 -0
  367. data/lib/async/matrix/schema.rb +170 -0
  368. data/lib/async/matrix/server.rb +18 -0
  369. data/lib/async/matrix/stream.rb +18 -0
  370. data/lib/async/matrix/version.rb +10 -0
  371. data/lib/async/matrix.rb +22 -0
  372. metadata +588 -0
@@ -0,0 +1,578 @@
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 "string_builder"
7
+ require "erb"
8
+
9
+ module Async
10
+ module Matrix
11
+ module Api
12
+ # Wraps a StringBuilder to build Matrix API paths via method chaining,
13
+ # terminated by .get(), .post(), .put(), or .delete() which validate
14
+ # the path against the PathTree and fire the HTTP request.
15
+ #
16
+ # Inherits from BasicObject so that method names like `send`, `display`,
17
+ # `format`, `test`, etc. are not defined and fall through to
18
+ # method_missing, where they get recorded as URL path segments.
19
+ #
20
+ # Usage:
21
+ # chain = Chain.new(client: client, path_tree: tree, prefix: %w[_matrix client v3])
22
+ # chain.account.whoami.get
23
+ # chain.rooms("!abc:ex.com").send("m.room.message", "txn1").put(body: "hi")
24
+ # chain.rooms("!abc:ex.com").messages.get(dir: "b", limit: 10)
25
+ #
26
+ class Chain < ::BasicObject
27
+ # Paths that carry raw bytes instead of JSON. Matched by
28
+ # _binary_route? where * is a single-segment wildcard.
29
+ # The HTTP method determines the operation:
30
+ # GET → download (returns raw bytes)
31
+ # POST/PUT → upload (sends raw bytes)
32
+ BINARY_ROUTES = [
33
+ "/_matrix/media/v3/upload",
34
+ "/_matrix/media/v3/upload/*/*",
35
+ "/_matrix/media/v3/download/*/*",
36
+ "/_matrix/media/v3/download/*/*/*",
37
+ "/_matrix/media/v3/thumbnail/*/*",
38
+ "/_matrix/client/v1/media/download/*/*",
39
+ "/_matrix/client/v1/media/download/*/*/*",
40
+ "/_matrix/client/v1/media/thumbnail/*/*",
41
+ ].freeze
42
+
43
+ def initialize(client:, path_tree:, prefix: nil)
44
+ @client = client
45
+ @path_tree = path_tree
46
+ @prefix = prefix || ["_matrix", "client", "v3"]
47
+ @buffer = []
48
+ end
49
+
50
+ # ── Terminal HTTP methods ──────────────────────────────────
51
+
52
+ def get(**kwargs)
53
+ max_retries = kwargs.delete(:max_retries)
54
+ execute("GET", query: kwargs, max_retries: max_retries)
55
+ end
56
+
57
+ def post(body = nil, **kwargs)
58
+ content_type = kwargs.delete(:content_type)
59
+ max_retries = kwargs.delete(:max_retries)
60
+ query = _extract_query_params(kwargs)
61
+ execute("POST", body: body || kwargs, content_type: content_type, max_retries: max_retries, query: query)
62
+ end
63
+
64
+ def put(body = nil, **kwargs)
65
+ content_type = kwargs.delete(:content_type)
66
+ max_retries = kwargs.delete(:max_retries)
67
+ query = _extract_query_params(kwargs)
68
+ execute("PUT", body: body || kwargs, content_type: content_type, max_retries: max_retries, query: query)
69
+ end
70
+
71
+ def delete(**kwargs)
72
+ max_retries = kwargs.delete(:max_retries)
73
+ execute("DELETE", query: kwargs, max_retries: max_retries)
74
+ end
75
+
76
+ def patch(body = nil, **kwargs)
77
+ content_type = kwargs.delete(:content_type)
78
+ max_retries = kwargs.delete(:max_retries)
79
+ query = _extract_query_params(kwargs)
80
+ execute("PATCH", body: body || kwargs, content_type: content_type, max_retries: max_retries, query: query)
81
+ end
82
+
83
+ # ── Chain inspection ───────────────────────────────────────
84
+
85
+ def to_s
86
+ "/" + _build_full_segments.map { |s| _encode(s) }.join("/")
87
+ end
88
+
89
+ def inspect
90
+ segments = _build_full_segments
91
+ "#<Async::Matrix::Api::Chain path=/#{segments.join("/")}>"
92
+ end
93
+
94
+ def path_segments
95
+ _build_full_segments
96
+ end
97
+
98
+ # ── Everything else becomes a path segment ─────────────────
99
+
100
+ def method_missing(name, *args, **kwargs)
101
+ if kwargs.empty?
102
+ @buffer << [name.to_s, args]
103
+ else
104
+ @buffer << [name.to_s, [*args, kwargs]]
105
+ end
106
+ self
107
+ end
108
+
109
+ private
110
+
111
+ def execute(method, body: nil, query: nil, content_type: nil, max_retries: nil)
112
+ segments = _rewrite_version(_build_full_segments)
113
+ result = @path_tree.match(segments, method)
114
+
115
+ if !result[:valid]
116
+ path = "/" + segments.join("/")
117
+ available = result[:methods]
118
+ if available.empty?
119
+ ::Kernel.raise ::Async::Matrix::InvalidEndpointError.new(
120
+ "INVALID_ENDPOINT",
121
+ "No Matrix API endpoint matches: #{method} #{path}"
122
+ )
123
+ else
124
+ ::Kernel.raise ::Async::Matrix::InvalidEndpointError.new(
125
+ "METHOD_NOT_ALLOWED",
126
+ "#{method} not allowed for #{path}. Valid methods: #{available.map(&:upcase).join(", ")}"
127
+ )
128
+ end
129
+ end
130
+
131
+ path = "/" + segments.map { |s| _encode(s) }.join("/")
132
+
133
+ if query && !query.empty?
134
+ qs = query.map { |k, v| "#{_encode(k.to_s)}=#{_encode(v.to_s)}" }.join("&")
135
+ path = "#{path}?#{qs}"
136
+ end
137
+
138
+ if _binary_route?(segments)
139
+ case method
140
+ when "GET"
141
+ @client.media_client.download(path)
142
+ when "POST"
143
+ @client.media_client.upload("POST", path, body, content_type || "application/octet-stream")
144
+ when "PUT"
145
+ @client.media_client.upload("PUT", path, body, content_type || "application/octet-stream")
146
+ end
147
+ else
148
+ retry_opts = max_retries ? {max_retries: max_retries} : {}
149
+ case method
150
+ when "GET"
151
+ @client.get(path, **retry_opts)
152
+ when "POST"
153
+ @client.post(path, body || {}, **retry_opts)
154
+ when "PUT"
155
+ @client.put(path, body || {}, **retry_opts)
156
+ when "DELETE"
157
+ @client.request("DELETE", path, nil, **retry_opts)
158
+ when "PATCH"
159
+ @client.request("PATCH", path, body || {}, **retry_opts)
160
+ end
161
+ end
162
+ end
163
+
164
+ # Rewrites version segments for endpoints that only exist at
165
+ # a different version than the gateway prefix provides.
166
+ # _matrix/media/v3/create → _matrix/media/v1/create
167
+ # _matrix/client/v3/media/... → _matrix/client/v1/media/...
168
+ def _rewrite_version(segments)
169
+ # POST /_matrix/media/v1/create (only exists at v1)
170
+ if segments.length == 4 &&
171
+ segments[0] == "_matrix" && segments[1] == "media" &&
172
+ segments[2] == "v3" && segments[3] == "create"
173
+ segments = segments.dup
174
+ segments[2] = "v1"
175
+ return segments
176
+ end
177
+
178
+ # Authenticated media endpoints live at /_matrix/client/v1/media/...
179
+ if segments.length >= 5 &&
180
+ segments[0] == "_matrix" && segments[1] == "client" &&
181
+ segments[2] == "v3" && segments[3] == "media"
182
+ segments = segments.dup
183
+ segments[2] = "v1"
184
+ return segments
185
+ end
186
+
187
+ segments
188
+ end
189
+
190
+ def _binary_route?(segments)
191
+ BINARY_ROUTES.any? do |pattern|
192
+ parts = pattern.split("/")
193
+ next false unless parts.length == segments.length + 1 # leading slash adds empty element
194
+ parts.shift # remove empty string from leading /
195
+ parts.length == segments.length &&
196
+ parts.zip(segments).all? { |pat, seg| pat == "*" || pat == seg }
197
+ end
198
+ end
199
+
200
+ def _extract_query_params(kwargs)
201
+ query = {}
202
+ kwargs.each_key do |k|
203
+ key_s = k.to_s
204
+ if key_s.start_with?("?")
205
+ query[key_s[1..]] = kwargs.delete(k)
206
+ end
207
+ end
208
+ query.empty? ? nil : query
209
+ end
210
+
211
+ def _build_full_segments
212
+ chain_segments = []
213
+ @buffer.each do |name, args|
214
+ chain_segments << name
215
+ next if args.nil? || args.empty?
216
+ args.each do |arg|
217
+ next if arg.is_a?(::Hash)
218
+ chain_segments << arg.to_s
219
+ end
220
+ end
221
+ @prefix + chain_segments
222
+ end
223
+
224
+ def _encode(value)
225
+ ::ERB::Util.url_encode(value.to_s)
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ __END__
233
+ describe "Async::Matrix::Api::Chain" do
234
+ def make_tree
235
+ tree = Async::Matrix::Api::PathTree.new
236
+ tree.insert(%w[_matrix client v3 account whoami], "get", "getAccountWhoami")
237
+ tree.insert(%w[_matrix client v3 createRoom], "post", "createRoom")
238
+ tree.insert(%w[_matrix client v3 rooms {roomId} ban], "post", "ban")
239
+ tree.insert(%w[_matrix client v3 rooms {roomId} state {eventType} {stateKey}], "get")
240
+ tree.insert(%w[_matrix client v3 rooms {roomId} state {eventType} {stateKey}], "put")
241
+ tree.insert(%w[_matrix client v3 rooms {roomId} messages], "get", "getRoomMessages")
242
+ tree.insert(%w[_matrix client v3 rooms {roomId} send {eventType} {txnId}], "put")
243
+ tree.insert(%w[_matrix client v3 profile {userId} displayname], "put", "setDisplayName")
244
+ tree
245
+ end
246
+
247
+ it "builds correct path segments for simple chain" do
248
+ chain = Async::Matrix::Api::Chain.new(
249
+ client: nil, path_tree: make_tree
250
+ )
251
+ chain.account.whoami
252
+ chain.path_segments.should == %w[_matrix client v3 account whoami]
253
+ end
254
+
255
+ it "builds correct path segments with args" do
256
+ chain = Async::Matrix::Api::Chain.new(
257
+ client: nil, path_tree: make_tree
258
+ )
259
+ chain.rooms("!abc:ex.com").ban
260
+ chain.path_segments.should == %w[_matrix client v3 rooms !abc:ex.com ban]
261
+ end
262
+
263
+ it "builds correct to_s with URL encoding" do
264
+ chain = Async::Matrix::Api::Chain.new(
265
+ client: nil, path_tree: make_tree
266
+ )
267
+ chain.rooms("!abc:ex.com").ban
268
+ chain.to_s.should == "/_matrix/client/v3/rooms/%21abc%3Aex.com/ban"
269
+ end
270
+
271
+ it "builds path for /rooms/{roomId}/send/{eventType}/{txnId}" do
272
+ chain = Async::Matrix::Api::Chain.new(
273
+ client: nil, path_tree: make_tree
274
+ )
275
+ chain.rooms("!abc:ex.com").send("m.room.message", "txn-123")
276
+ chain.path_segments.should == %w[_matrix client v3 rooms !abc:ex.com send m.room.message txn-123]
277
+ end
278
+
279
+ it "raises InvalidEndpointError for unknown path" do
280
+ chain = Async::Matrix::Api::Chain.new(
281
+ client: nil, path_tree: make_tree
282
+ )
283
+ chain.account.foobar
284
+ lambda { chain.get }.should.raise Async::Matrix::InvalidEndpointError
285
+ end
286
+
287
+ it "raises InvalidEndpointError for wrong HTTP method" do
288
+ chain = Async::Matrix::Api::Chain.new(
289
+ client: nil, path_tree: make_tree
290
+ )
291
+ chain.account.whoami
292
+ lambda { chain.post }.should.raise Async::Matrix::InvalidEndpointError
293
+ end
294
+
295
+ # ── Binary route detection ───────────────────────────────
296
+
297
+ def make_media_tree
298
+ tree = Async::Matrix::Api::PathTree.new
299
+ tree.insert(%w[_matrix media v3 upload], "post", "uploadContent")
300
+ tree.insert(%w[_matrix media v3 upload {serverName} {mediaId}], "put", "uploadContentToMXC")
301
+ tree.insert(%w[_matrix media v3 download {serverName} {mediaId}], "get", "getContent")
302
+ tree.insert(%w[_matrix media v3 download {serverName} {mediaId} {fileName}], "get", "getContentOverrideName")
303
+ tree.insert(%w[_matrix media v3 thumbnail {serverName} {mediaId}], "get", "getContentThumbnail")
304
+ tree.insert(%w[_matrix client v1 media download {serverName} {mediaId}], "get", "getContentAuthed")
305
+ tree.insert(%w[_matrix client v1 media download {serverName} {mediaId} {fileName}], "get", "getContentOverrideNameAuthed")
306
+ tree.insert(%w[_matrix client v1 media thumbnail {serverName} {mediaId}], "get", "getContentThumbnailAuthed")
307
+ tree
308
+ end
309
+
310
+ # Stub response object for download tests.
311
+ StubResponse = Struct.new(:body_bytes, :headers, :status) do
312
+ def initialize(body_bytes = "\xFF\xD8\xFF".b)
313
+ super(body_bytes, {"content-type" => "image/jpeg"}, 200)
314
+ end
315
+
316
+ def read
317
+ body_bytes
318
+ end
319
+ end
320
+
321
+ # Stub client with a stub media_client for testing dispatch.
322
+ StubMediaClient = Struct.new(:calls) do
323
+ def initialize
324
+ super([])
325
+ end
326
+
327
+ def upload(method, path, body, content_type)
328
+ calls << [:upload, method, path, body, content_type]
329
+ {"content_uri" => "mxc://example.com/abc123"}
330
+ end
331
+
332
+ def download(path)
333
+ calls << [:download, path]
334
+ StubResponse.new
335
+ end
336
+ end
337
+
338
+ StubClientWithMedia = Struct.new(:calls, :media_client) do
339
+ def initialize
340
+ super([], StubMediaClient.new)
341
+ end
342
+
343
+ def get(path, max_retries: nil)
344
+ calls << [:get, path, {max_retries: max_retries}]
345
+ {"stub" => true}
346
+ end
347
+
348
+ def post(path, body = {}, max_retries: nil)
349
+ calls << [:post, path, body, {max_retries: max_retries}]
350
+ {"stub" => true}
351
+ end
352
+
353
+ def put(path, body = {}, max_retries: nil)
354
+ calls << [:put, path, body, {max_retries: max_retries}]
355
+ {"stub" => true}
356
+ end
357
+
358
+ def request(method, path, body = nil, max_retries: nil)
359
+ calls << [method.downcase.to_sym, path, body, {max_retries: max_retries}]
360
+ {"stub" => true}
361
+ end
362
+ end
363
+
364
+ it "detects upload route and dispatches to media_client" do
365
+ client = StubClientWithMedia.new
366
+ chain = Async::Matrix::Api::Chain.new(
367
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
368
+ )
369
+ chain.upload.post("image-bytes", content_type: "image/png")
370
+ client.media_client.calls.last[0].should == :upload
371
+ client.media_client.calls.last[1].should == "POST"
372
+ client.media_client.calls.last[3].should == "image-bytes"
373
+ client.media_client.calls.last[4].should == "image/png"
374
+ client.calls.should.be.empty
375
+ end
376
+
377
+ it "detects upload PUT route and dispatches to media_client" do
378
+ client = StubClientWithMedia.new
379
+ chain = Async::Matrix::Api::Chain.new(
380
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
381
+ )
382
+ chain.upload("example.com", "abc123").put("image-bytes", content_type: "image/jpeg")
383
+ client.media_client.calls.last[0].should == :upload
384
+ client.media_client.calls.last[1].should == "PUT"
385
+ client.media_client.calls.last[4].should == "image/jpeg"
386
+ client.calls.should.be.empty
387
+ end
388
+
389
+ it "detects download route and dispatches to media_client" do
390
+ client = StubClientWithMedia.new
391
+ chain = Async::Matrix::Api::Chain.new(
392
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
393
+ )
394
+ response = chain.download("example.com", "abc123").get
395
+ client.media_client.calls.last[0].should == :download
396
+ response.read.should == "\xFF\xD8\xFF".b
397
+ response.headers["content-type"].should == "image/jpeg"
398
+ client.calls.should.be.empty
399
+ end
400
+
401
+ it "detects download with filename route" do
402
+ client = StubClientWithMedia.new
403
+ chain = Async::Matrix::Api::Chain.new(
404
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
405
+ )
406
+ chain.download("example.com", "abc123", "photo.jpg").get
407
+ client.media_client.calls.last[0].should == :download
408
+ end
409
+
410
+ it "detects thumbnail route and passes query params" do
411
+ client = StubClientWithMedia.new
412
+ chain = Async::Matrix::Api::Chain.new(
413
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
414
+ )
415
+ chain.thumbnail("example.com", "abc123").get(width: 64, height: 64)
416
+ call = client.media_client.calls.last
417
+ call[0].should == :download
418
+ call[1].should.include "width=64"
419
+ call[1].should.include "height=64"
420
+ end
421
+
422
+ it "detects authed download route (client/v1 prefix)" do
423
+ client = StubClientWithMedia.new
424
+ chain = Async::Matrix::Api::Chain.new(
425
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix client v1]
426
+ )
427
+ chain.media.download("example.com", "abc123").get
428
+ client.media_client.calls.last[0].should == :download
429
+ end
430
+
431
+ it "does NOT route non-binary paths to media_client" do
432
+ client = StubClientWithMedia.new
433
+ tree = make_tree
434
+ chain = Async::Matrix::Api::Chain.new(
435
+ client: client, path_tree: tree
436
+ )
437
+ chain.account.whoami.get
438
+ client.calls.last[0].should == :get
439
+ client.media_client.calls.should.be.empty
440
+ end
441
+
442
+ it "defaults content_type to application/octet-stream for uploads" do
443
+ client = StubClientWithMedia.new
444
+ chain = Async::Matrix::Api::Chain.new(
445
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
446
+ )
447
+ chain.upload.post("raw-bytes")
448
+ client.media_client.calls.last[4].should == "application/octet-stream"
449
+ end
450
+
451
+ # ── ?-prefixed query params on uploads ────────────────────
452
+
453
+ it "passes ?-prefixed kwargs as query params on upload POST" do
454
+ client = StubClientWithMedia.new
455
+ chain = Async::Matrix::Api::Chain.new(
456
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
457
+ )
458
+ chain.upload.post("image-bytes", content_type: "image/png", "?filename": "photo.png")
459
+ call = client.media_client.calls.last
460
+ call[0].should == :upload
461
+ call[2].should.include "filename=photo.png"
462
+ call[4].should == "image/png"
463
+ end
464
+
465
+ it "passes ?-prefixed kwargs as query params on upload PUT" do
466
+ client = StubClientWithMedia.new
467
+ chain = Async::Matrix::Api::Chain.new(
468
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix media v3]
469
+ )
470
+ chain.upload("example.com", "abc123").put("bytes", content_type: "image/jpeg", "?filename": "pic.jpg")
471
+ call = client.media_client.calls.last
472
+ call[0].should == :upload
473
+ call[2].should.include "filename=pic.jpg"
474
+ end
475
+
476
+ # ── Version rewrites ──────────────────────────────────────
477
+
478
+ def make_full_media_tree
479
+ tree = make_media_tree
480
+ tree.insert(%w[_matrix media v1 create], "post", "createContent")
481
+ tree.insert(%w[_matrix media v3 config], "get", "getConfig")
482
+ tree.insert(%w[_matrix media v3 preview_url], "get", "getUrlPreview")
483
+ tree
484
+ end
485
+
486
+ it "rewrites media/v3/create to media/v1/create" do
487
+ client = StubClientWithMedia.new
488
+ chain = Async::Matrix::Api::Chain.new(
489
+ client: client, path_tree: make_full_media_tree, prefix: %w[_matrix media v3]
490
+ )
491
+ chain.create.post
492
+ call = client.calls.last
493
+ call[0].should == :post
494
+ call[1].should == "/_matrix/media/v1/create"
495
+ end
496
+
497
+ it "rewrites client/v3/media/download to client/v1/media/download" do
498
+ client = StubClientWithMedia.new
499
+ chain = Async::Matrix::Api::Chain.new(
500
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix client v3]
501
+ )
502
+ chain.media.download("example.com", "abc123").get
503
+ call = client.media_client.calls.last
504
+ call[0].should == :download
505
+ call[1].should.include "/_matrix/client/v1/media/download/"
506
+ end
507
+
508
+ it "rewrites client/v3/media/thumbnail to client/v1/media/thumbnail" do
509
+ client = StubClientWithMedia.new
510
+ chain = Async::Matrix::Api::Chain.new(
511
+ client: client, path_tree: make_media_tree, prefix: %w[_matrix client v3]
512
+ )
513
+ chain.media.thumbnail("example.com", "abc123").get(width: 32, height: 32)
514
+ call = client.media_client.calls.last
515
+ call[0].should == :download
516
+ call[1].should.include "/_matrix/client/v1/media/thumbnail/"
517
+ end
518
+
519
+ it "does NOT rewrite non-media client/v3 paths" do
520
+ client = StubClientWithMedia.new
521
+ tree = make_tree
522
+ chain = Async::Matrix::Api::Chain.new(
523
+ client: client, path_tree: tree, prefix: %w[_matrix client v3]
524
+ )
525
+ chain.account.whoami.get
526
+ call = client.calls.last
527
+ call[1].should == "/_matrix/client/v3/account/whoami"
528
+ end
529
+
530
+ it "does NOT rewrite media/v3 paths that are not create" do
531
+ client = StubClientWithMedia.new
532
+ chain = Async::Matrix::Api::Chain.new(
533
+ client: client, path_tree: make_full_media_tree, prefix: %w[_matrix media v3]
534
+ )
535
+ chain.config.get
536
+ call = client.calls.last
537
+ call[0].should == :get
538
+ call[1].should == "/_matrix/media/v3/config"
539
+ end
540
+
541
+ # ── max_retries forwarding ─────────────────────────────────
542
+
543
+ it "forwards max_retries on get" do
544
+ client = StubClientWithMedia.new
545
+ chain = Async::Matrix::Api::Chain.new(
546
+ client: client, path_tree: make_tree
547
+ )
548
+ chain.account.whoami.get(max_retries: 0)
549
+ client.calls.last.last[:max_retries].should == 0
550
+ end
551
+
552
+ it "forwards max_retries on post" do
553
+ client = StubClientWithMedia.new
554
+ chain = Async::Matrix::Api::Chain.new(
555
+ client: client, path_tree: make_tree
556
+ )
557
+ chain.createRoom.post({name: "Test"}, max_retries: 1)
558
+ client.calls.last.last[:max_retries].should == 1
559
+ end
560
+
561
+ it "forwards max_retries on put" do
562
+ client = StubClientWithMedia.new
563
+ chain = Async::Matrix::Api::Chain.new(
564
+ client: client, path_tree: make_tree
565
+ )
566
+ chain.profile("@user:ex.com").displayname.put({displayname: "Bob"}, max_retries: 2)
567
+ client.calls.last.last[:max_retries].should == 2
568
+ end
569
+
570
+ it "forwards nil max_retries by default" do
571
+ client = StubClientWithMedia.new
572
+ chain = Async::Matrix::Api::Chain.new(
573
+ client: client, path_tree: make_tree
574
+ )
575
+ chain.account.whoami.get
576
+ client.calls.last.last[:max_retries].should.be.nil
577
+ end
578
+ end
@@ -0,0 +1,101 @@
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 Api
9
+ # StringBuilder concat handler that renders a method chain into URL path segments.
10
+ #
11
+ # Convention:
12
+ # - Bare methods become path segments: .account.whoami -> ["account", "whoami"]
13
+ # - Methods with args inject the arg as a path segment after the method name:
14
+ # .rooms("!abc:ex.com") -> ["rooms", "!abc:ex.com"]
15
+ # - .call("literal") injects a raw path segment:
16
+ # .("m.room.message") -> ["m.room.message"]
17
+ #
18
+ # The handler returns an array of raw (unencoded) segments.
19
+ # URL-encoding is applied later when constructing the final URL.
20
+ #
21
+ class Concat
22
+ def self.call(buffer)
23
+ new(buffer).segments
24
+ end
25
+
26
+ def initialize(buffer)
27
+ @buffer = buffer.respond_to?(:to_a) ? buffer.to_a : buffer
28
+ end
29
+
30
+ def segments
31
+ result = []
32
+
33
+ @buffer.each do |entry|
34
+ case entry
35
+ when Symbol
36
+ # :slash, :dash -- ignore in URL context
37
+ next
38
+ when Array
39
+ name, args = entry
40
+ next unless name.is_a?(String)
41
+
42
+ if args.nil? || args.empty?
43
+ # Bare method -> path segment
44
+ result << name
45
+ else
46
+ # Method with args -> method name as segment, then each arg as segment
47
+ result << name
48
+ args.each do |arg|
49
+ next if arg.is_a?(Hash) # kwargs are not path segments
50
+ result << arg.to_s
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ result
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ __END__
64
+ require "string_builder"
65
+
66
+ describe "Async::Matrix::Api::Concat" do
67
+ it "converts bare methods to path segments" do
68
+ sb = StringBuilder.new
69
+ sb.account.whoami
70
+ segments = Async::Matrix::Api::Concat.call(sb)
71
+ segments.should == %w[account whoami]
72
+ end
73
+
74
+ it "converts methods with args to segment + arg" do
75
+ sb = StringBuilder.new
76
+ sb.rooms("!abc:ex.com").ban
77
+ segments = Async::Matrix::Api::Concat.call(sb)
78
+ segments.should == %w[rooms !abc:ex.com ban]
79
+ end
80
+
81
+ it "handles methods with multiple args" do
82
+ sb = StringBuilder.new
83
+ sb.rooms("!abc:ex.com").state("m.room.name", "some_key")
84
+ segments = Async::Matrix::Api::Concat.call(sb)
85
+ segments.should == %w[rooms !abc:ex.com state m.room.name some_key]
86
+ end
87
+
88
+ it "handles deep paths" do
89
+ sb = StringBuilder.new
90
+ sb.rooms("!abc:ex.com").state("m.room.name", "")
91
+ segments = Async::Matrix::Api::Concat.call(sb)
92
+ segments.should == ["rooms", "!abc:ex.com", "state", "m.room.name", ""]
93
+ end
94
+
95
+ it "ignores kwargs in segments" do
96
+ sb = StringBuilder.new
97
+ sb.account.whoami
98
+ segments = Async::Matrix::Api::Concat.call(sb)
99
+ segments.should == %w[account whoami]
100
+ end
101
+ end