slidge 0.3.0b3__tar.gz → 0.3.0b4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. {slidge-0.3.0b3 → slidge-0.3.0b4}/.woodpecker/container-ci.yaml +1 -1
  2. {slidge-0.3.0b3 → slidge-0.3.0b4}/PKG-INFO +1 -1
  3. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/gateway.py +7 -0
  4. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/base.py +2 -0
  5. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/message_text.py +29 -0
  6. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/presence.py +3 -2
  7. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/group/archive.py +4 -1
  8. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/group/bookmarks.py +1 -1
  9. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/group/participant.py +15 -2
  10. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/group/room.py +8 -4
  11. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/migration.py +0 -11
  12. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/util.py +7 -0
  13. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge.egg-info/PKG-INFO +1 -1
  14. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge.egg-info/SOURCES.txt +2 -0
  15. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_avatar.py +2 -1
  16. slidge-0.3.0b4/tests/test_empty_subject.py +139 -0
  17. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_session_2.py +87 -1
  18. slidge-0.3.0b4/tests/test_type_conversion.py +119 -0
  19. {slidge-0.3.0b3 → slidge-0.3.0b4}/.gitignore +0 -0
  20. {slidge-0.3.0b3 → slidge-0.3.0b4}/.pre-commit-config.yaml +0 -0
  21. {slidge-0.3.0b3 → slidge-0.3.0b4}/.woodpecker/docs.yaml +0 -0
  22. {slidge-0.3.0b3 → slidge-0.3.0b4}/.woodpecker/package.yaml +0 -0
  23. {slidge-0.3.0b3 → slidge-0.3.0b4}/.woodpecker/test.yaml +0 -0
  24. {slidge-0.3.0b3 → slidge-0.3.0b4}/Dockerfile +0 -0
  25. {slidge-0.3.0b3 → slidge-0.3.0b4}/LICENSE +0 -0
  26. {slidge-0.3.0b3 → slidge-0.3.0b4}/README.md +0 -0
  27. {slidge-0.3.0b3 → slidge-0.3.0b4}/commitlint.config.js +0 -0
  28. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/assets/5x5.png +0 -0
  29. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/assets/slidge-color-small.png +0 -0
  30. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/assets/slidge-color.png +0 -0
  31. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/assets/slidge-mono-black.png +0 -0
  32. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/assets/slidge-mono-white.png +0 -0
  33. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/assets/slidge.svg +0 -0
  34. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/confs/movim.env +0 -0
  35. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/confs/nginx.conf +0 -0
  36. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/confs/slidge-dev.ini +0 -0
  37. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/confs/slidge-example.ini +0 -0
  38. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/hot-reload.sh +0 -0
  39. {slidge-0.3.0b3 → slidge-0.3.0b4}/dev/prettify_tests.py +0 -0
  40. {slidge-0.3.0b3 → slidge-0.3.0b4}/doap.xml +0 -0
  41. {slidge-0.3.0b3 → slidge-0.3.0b4}/docker-compose.yml +0 -0
  42. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/Makefile +0 -0
  43. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/attachments.rst +0 -0
  44. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/component.rst +0 -0
  45. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/config/index.rst +0 -0
  46. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/daemon.rst +0 -0
  47. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/examples/ejabberd.yaml +0 -0
  48. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/examples/index.rst +0 -0
  49. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/examples/prosody.cfg.lua +0 -0
  50. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/index.rst +0 -0
  51. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/install.rst +0 -0
  52. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/note.rst +0 -0
  53. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/admin/privilege.rst +0 -0
  54. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/codeberg.svg +0 -0
  55. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/conf.py +0 -0
  56. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/dev/contributing.rst +0 -0
  57. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/dev/design.rst +0 -0
  58. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/dev/howto.rst +0 -0
  59. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/dev/index.rst +0 -0
  60. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/dev/tutorial.rst +0 -0
  61. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/glossary.rst +0 -0
  62. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/index.rst +0 -0
  63. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/commands.rst +0 -0
  64. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/contacts.rst +0 -0
  65. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/foxyproxy.png +0 -0
  66. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/gajim.png +0 -0
  67. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/index.rst +0 -0
  68. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/low_profile.rst +0 -0
  69. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/movim1.png +0 -0
  70. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/movim2.png +0 -0
  71. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/note.rst +0 -0
  72. {slidge-0.3.0b3 → slidge-0.3.0b4}/docs/source/user/register.rst +0 -0
  73. {slidge-0.3.0b3 → slidge-0.3.0b4}/pyproject.toml +0 -0
  74. {slidge-0.3.0b3 → slidge-0.3.0b4}/setup.cfg +0 -0
  75. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/__init__.py +0 -0
  76. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/__main__.py +0 -0
  77. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/__init__.py +0 -0
  78. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/adhoc.py +0 -0
  79. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/admin.py +0 -0
  80. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/base.py +0 -0
  81. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/categories.py +0 -0
  82. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/chat_command.py +0 -0
  83. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/register.py +0 -0
  84. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/command/user.py +0 -0
  85. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/contact/__init__.py +0 -0
  86. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/contact/contact.py +0 -0
  87. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/contact/roster.py +0 -0
  88. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/__init__.py +0 -0
  89. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/config.py +0 -0
  90. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/__init__.py +0 -0
  91. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/caps.py +0 -0
  92. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/disco.py +0 -0
  93. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/message/__init__.py +0 -0
  94. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/message/chat_state.py +0 -0
  95. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/message/marker.py +0 -0
  96. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/message/message.py +0 -0
  97. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/muc/__init__.py +0 -0
  98. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/muc/admin.py +0 -0
  99. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/muc/mam.py +0 -0
  100. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/muc/misc.py +0 -0
  101. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/muc/owner.py +0 -0
  102. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/muc/ping.py +0 -0
  103. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/presence.py +0 -0
  104. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/registration.py +0 -0
  105. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/search.py +0 -0
  106. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/session_dispatcher.py +0 -0
  107. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/util.py +0 -0
  108. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/dispatcher/vcard.py +0 -0
  109. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/__init__.py +0 -0
  110. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/attachment.py +0 -0
  111. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/avatar.py +0 -0
  112. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/db.py +0 -0
  113. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/disco.py +0 -0
  114. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/message.py +0 -0
  115. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/message_maker.py +0 -0
  116. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/mixins/recipient.py +0 -0
  117. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/pubsub.py +0 -0
  118. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/core/session.py +0 -0
  119. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/__init__.py +0 -0
  120. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/alembic/__init__.py +0 -0
  121. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/alembic/env.py +0 -0
  122. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/alembic/script.py.mako +0 -0
  123. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +0 -0
  124. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/avatar.py +0 -0
  125. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/meta.py +0 -0
  126. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/models.py +0 -0
  127. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/db/store.py +0 -0
  128. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/group/__init__.py +0 -0
  129. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/main.py +0 -0
  130. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/py.typed +0 -0
  131. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/__init__.py +0 -0
  132. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/delivery_receipt.py +0 -0
  133. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/link_preview/__init__.py +0 -0
  134. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/link_preview/link_preview.py +0 -0
  135. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/link_preview/stanza.py +0 -0
  136. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/roster.py +0 -0
  137. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0077/__init__.py +0 -0
  138. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0077/register.py +0 -0
  139. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0077/stanza.py +0 -0
  140. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0100/__init__.py +0 -0
  141. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0100/gateway.py +0 -0
  142. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0100/stanza.py +0 -0
  143. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0153/__init__.py +0 -0
  144. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0153/vcard_avatar.py +0 -0
  145. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0292/__init__.py +0 -0
  146. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/slixfix/xep_0292/vcard4.py +0 -0
  147. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/__init__.py +0 -0
  148. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/archive_msg.py +0 -0
  149. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/conf.py +0 -0
  150. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/jid_escaping.py +0 -0
  151. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/lock.py +0 -0
  152. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/test.py +0 -0
  153. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge/util/types.py +0 -0
  154. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge.egg-info/dependency_links.txt +0 -0
  155. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge.egg-info/entry_points.txt +0 -0
  156. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge.egg-info/requires.txt +0 -0
  157. {slidge-0.3.0b3 → slidge-0.3.0b4}/slidge.egg-info/top_level.txt +0 -0
  158. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/__init__.py +0 -0
  159. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/__main__.py +0 -0
  160. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/contact.py +0 -0
  161. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/gateway.py +0 -0
  162. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/group.py +0 -0
  163. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/legacy_client.py +0 -0
  164. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/session.py +0 -0
  165. {slidge-0.3.0b3 → slidge-0.3.0b4}/superduper/util.py +0 -0
  166. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/conftest.py +0 -0
  167. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_adhoc/test_access.py +0 -0
  168. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_adhoc/test_confirmation.py +0 -0
  169. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_adhoc/test_form.py +0 -0
  170. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_adhoc/test_reported.py +0 -0
  171. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_attachment.py +0 -0
  172. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_backfill.py +0 -0
  173. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_chat_commands.py +0 -0
  174. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_config.py +0 -0
  175. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_db/test_store.py +0 -0
  176. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_db/test_user.py +0 -0
  177. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_feature_restriction.py +0 -0
  178. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_mam_archivable.py +0 -0
  179. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_mds.py +0 -0
  180. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_muc.py +0 -0
  181. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_resourceprep.py +0 -0
  182. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_session.py +0 -0
  183. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_set_name_before_fill.py +0 -0
  184. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_shakespeare.py +0 -0
  185. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_stanza_link_preview.py +0 -0
  186. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_util.py +0 -0
  187. {slidge-0.3.0b3 → slidge-0.3.0b4}/tests/test_vcard.py +0 -0
  188. {slidge-0.3.0b3 → slidge-0.3.0b4}/uv.lock +0 -0
@@ -19,7 +19,7 @@ labels:
19
19
 
20
20
  steps:
21
21
  build-and-push:
22
- image: woodpeckerci/plugin-docker-buildx
22
+ image: woodpeckerci/plugin-docker-buildx:6.0.2
23
23
  settings:
24
24
  repo: codeberg.org/slidge/slidge
25
25
  registry: codeberg.org
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slidge
3
- Version: 0.3.0b3
3
+ Version: 0.3.0b4
4
4
  Summary: XMPP bridging framework
5
5
  Author-email: Nicolas Cedilnik <nicoco@nicoco.fr>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -172,6 +172,13 @@ class BaseGateway(
172
172
  required=True,
173
173
  type="boolean",
174
174
  ),
175
+ FormField(
176
+ var="reaction_fallback",
177
+ label="Receive fallback messages for reactions (for legacy XMPP clients)",
178
+ value="false",
179
+ required=True,
180
+ type="boolean",
181
+ ),
175
182
  ]
176
183
 
177
184
  ROSTER_GROUP: str = "slidge"
@@ -31,6 +31,8 @@ class Base:
31
31
 
32
32
 
33
33
  class BaseSender(Base):
34
+ is_participant = False
35
+
34
36
  def _send(
35
37
  self, stanza: MessageOrPresenceTypeVar, **send_kwargs
36
38
  ) -> MessageOrPresenceTypeVar:
@@ -2,6 +2,9 @@ import logging
2
2
  from datetime import datetime
3
3
  from typing import Iterable, Optional
4
4
 
5
+ from slixmpp import Message
6
+
7
+ from ...util.archive_msg import HistoryMessage
5
8
  from ...util.types import (
6
9
  LegacyMessageType,
7
10
  LegacyThreadType,
@@ -9,6 +12,7 @@ from ...util.types import (
9
12
  MessageReference,
10
13
  ProcessingHint,
11
14
  )
15
+ from ...util.util import add_quote_prefix
12
16
  from .message_maker import MessageMaker
13
17
 
14
18
 
@@ -188,8 +192,33 @@ class TextMessageMixin(MessageMaker):
188
192
  if not xmpp_id:
189
193
  xmpp_id = self._legacy_to_xmpp(legacy_msg_id)
190
194
  self.xmpp["xep_0444"].set_reactions(msg, to_id=xmpp_id, reactions=emojis)
195
+ self.__add_reaction_fallback(msg, legacy_msg_id, emojis)
191
196
  self._send(msg, **kwargs)
192
197
 
198
+ def __add_reaction_fallback(
199
+ self,
200
+ msg: Message,
201
+ legacy_msg_id: LegacyMessageType,
202
+ emojis: Iterable[str] = (),
203
+ ) -> None:
204
+ if not self.session.user.preferences.get("reaction_fallback", False):
205
+ return
206
+ msg["fallback"]["for"] = self.xmpp.plugin["xep_0444"].namespace
207
+ msg["fallback"].enable("body")
208
+ msg["body"] = " ".join(emojis)
209
+ if not self.is_participant:
210
+ return
211
+ with self.xmpp.store.session() as orm:
212
+ archived = self.xmpp.store.mam.get_by_legacy_id(
213
+ orm, self.muc.stored.id, str(legacy_msg_id)
214
+ )
215
+ if archived is None:
216
+ return
217
+ history_msg = HistoryMessage(archived.stanza)
218
+ msg["body"] = (
219
+ add_quote_prefix(history_msg.stanza["body"]) + "\n" + msg["body"]
220
+ )
221
+
193
222
  def retract(
194
223
  self,
195
224
  legacy_msg_id: LegacyMessageType,
@@ -62,8 +62,9 @@ class PresenceMixin(BaseSender, DBMixin):
62
62
  try:
63
63
  return self.stored.contact
64
64
  except DetachedInstanceError:
65
- self.merge()
66
- return self.stored.contact
65
+ with self.xmpp.store.session() as orm:
66
+ orm.add(self.stored)
67
+ return self.stored.contact
67
68
 
68
69
  @property
69
70
  def __contact_pk(self) -> int | None:
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import uuid
3
+ import warnings
3
4
  from copy import copy
4
5
  from datetime import datetime, timezone
5
6
  from typing import TYPE_CHECKING, Collection, Optional
@@ -48,7 +49,9 @@ class MessageArchive:
48
49
  elif participant.is_system:
49
50
  new_msg["muc"]["jid"] = participant.muc.jid
50
51
  else:
51
- log.warning("No real JID for participant in this group")
52
+ warnings.warn(
53
+ f"No real JID for participant '{participant.nickname}' in '{self.room.name}'"
54
+ )
52
55
  new_msg["muc"]["jid"] = (
53
56
  f"{uuid.uuid4()}@{participant.xmpp.boundjid.bare}"
54
57
  )
@@ -158,7 +158,7 @@ class LegacyBookmarks(
158
158
  stored = Room(
159
159
  user_account_id=self.session.user_pk,
160
160
  jid=jid,
161
- legacy_id=legacy_id,
161
+ legacy_id=str(legacy_id),
162
162
  )
163
163
  return await self.__update_if_needed(stored)
164
164
 
@@ -6,8 +6,8 @@ from copy import copy
6
6
  from datetime import datetime
7
7
  from functools import cached_property
8
8
  from typing import TYPE_CHECKING, Any, Optional, Union
9
+ from xml.etree import ElementTree as ET
9
10
 
10
- import sqlalchemy as sa
11
11
  from slixmpp import JID, InvalidJID, Message, Presence
12
12
  from slixmpp.plugins.xep_0045.stanza import MUCAdminItem
13
13
  from slixmpp.types import MessageTypes, OptJid
@@ -51,6 +51,8 @@ class LegacyParticipant(
51
51
  A legacy participant of a legacy group chat.
52
52
  """
53
53
 
54
+ is_participant = True
55
+
54
56
  mtype: MessageTypes = "groupchat"
55
57
  _can_send_carbon = False
56
58
  USE_STANZA_ID = True
@@ -93,6 +95,13 @@ class LegacyParticipant(
93
95
  self.merge()
94
96
  return self.stored.is_user
95
97
 
98
+ @is_user.setter
99
+ def is_user(self, is_user: bool) -> None:
100
+ with self.xmpp.store.session(expire_on_commit=True) as orm:
101
+ orm.add(self.stored)
102
+ self.stored.is_user = is_user
103
+ orm.commit()
104
+
96
105
  @property
97
106
  def jid(self) -> JID:
98
107
  jid = JID(self.muc.jid)
@@ -510,7 +519,11 @@ class LegacyParticipant(
510
519
  if when is not None:
511
520
  msg["delay"].set_stamp(when)
512
521
  msg["delay"]["from"] = self.muc.jid
513
- msg["subject"] = subject or str(self.muc.name)
522
+ if subject:
523
+ msg["subject"] = subject
524
+ else:
525
+ # may be simplified if slixmpp lets it do it more easily some day
526
+ msg.xml.append(ET.Element(f"{{{msg.namespace}}}subject"))
514
527
  self._send(msg, full_jid)
515
528
 
516
529
 
@@ -431,8 +431,8 @@ class LegacyMUC(
431
431
  yield
432
432
 
433
433
  @property
434
- def subject(self):
435
- return self.stored.subject
434
+ def subject(self) -> str:
435
+ return self.stored.subject or ""
436
436
 
437
437
  @subject.setter
438
438
  def subject(self, s: str) -> None:
@@ -652,7 +652,7 @@ class LegacyMUC(
652
652
  orm.refresh(self.stored, ["participants"])
653
653
  if not user_participant.is_user:
654
654
  self.log.warning("is_user flag not set participant on user_participant")
655
- user_participant.is_user = True # type:ignore
655
+ user_participant.is_user = True
656
656
  user_participant.send_initial_presence(
657
657
  user_full_jid,
658
658
  presence_id=join_presence["id"],
@@ -680,8 +680,12 @@ class LegacyMUC(
680
680
  maxstanzas=maxstanzas,
681
681
  since=since,
682
682
  )
683
+ if self.HAS_SUBJECT:
684
+ subject = self.subject or ""
685
+ else:
686
+ subject = self.description or self.name or ""
683
687
  self.__get_subject_setter_participant().set_room_subject(
684
- self.subject if self.HAS_SUBJECT else (self.description or self.name or ""),
688
+ subject,
685
689
  user_full_jid,
686
690
  self.subject_date,
687
691
  )
@@ -1,5 +1,4 @@
1
1
  import logging
2
- import shutil
3
2
  import sys
4
3
  import traceback
5
4
  from pathlib import Path
@@ -7,15 +6,6 @@ from pathlib import Path
7
6
  from alembic import command
8
7
  from alembic.config import Config
9
8
 
10
- from .core import config
11
-
12
-
13
- def remove_avatar_cache_v1() -> None:
14
- old_dir = config.HOME_DIR / "slidge_avatars"
15
- if old_dir.exists():
16
- log.info("Avatar cache dir v1 found, clearing it.")
17
- shutil.rmtree(old_dir)
18
-
19
9
 
20
10
  def get_alembic_cfg() -> Config:
21
11
  alembic_cfg = Config()
@@ -28,7 +18,6 @@ def get_alembic_cfg() -> Config:
28
18
 
29
19
 
30
20
  def migrate() -> None:
31
- remove_avatar_cache_v1()
32
21
  try:
33
22
  command.upgrade(get_alembic_cfg(), "head")
34
23
  except Exception as e:
@@ -324,3 +324,10 @@ def strip_leading_emoji(text: str) -> str:
324
324
 
325
325
  async def noop_coro() -> None:
326
326
  pass
327
+
328
+
329
+ def add_quote_prefix(text: str):
330
+ """
331
+ Return multi-line text with leading quote marks (i.e. the ">" character).
332
+ """
333
+ return "\n".join(("> " + x).strip() for x in text.split("\n")).strip()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slidge
3
- Version: 0.3.0b3
3
+ Version: 0.3.0b4
4
4
  Summary: XMPP bridging framework
5
5
  Author-email: Nicolas Cedilnik <nicoco@nicoco.fr>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -164,6 +164,7 @@ tests/test_avatar.py
164
164
  tests/test_backfill.py
165
165
  tests/test_chat_commands.py
166
166
  tests/test_config.py
167
+ tests/test_empty_subject.py
167
168
  tests/test_feature_restriction.py
168
169
  tests/test_mam_archivable.py
169
170
  tests/test_mds.py
@@ -174,6 +175,7 @@ tests/test_session_2.py
174
175
  tests/test_set_name_before_fill.py
175
176
  tests/test_shakespeare.py
176
177
  tests/test_stanza_link_preview.py
178
+ tests/test_type_conversion.py
177
179
  tests/test_util.py
178
180
  tests/test_vcard.py
179
181
  tests/test_adhoc/test_access.py
@@ -255,7 +255,8 @@ class BaseMUC(BaseNoMUC):
255
255
  </presence>
256
256
  """
257
257
  )
258
- assert self.next_sent()["subject"] != ""
258
+ subject_msg = self.next_sent()
259
+ assert subject_msg.xml.find(f"{{{subject_msg.namespace}}}subject") is not None
259
260
  # assert self.next_sent()["from"] == "room@aim.shakespeare.lit"
260
261
 
261
262
  def get_muc(self, joined=True) -> MUC:
@@ -0,0 +1,139 @@
1
+ import unittest
2
+
3
+ import pytest
4
+ from slixmpp.exceptions import XMPPError
5
+
6
+ from conftest import AvatarFixtureMixin
7
+ from slixmpp import JID, Iq
8
+
9
+ from slidge import BaseGateway, BaseSession, GatewayUser, LegacyRoster, LegacyBookmarks, LegacyMUC, LegacyContact
10
+ from slidge.core.session import _sessions
11
+ from slidge.util.test import SlidgeTest
12
+ from slidge.util.types import LegacyUserIdType, LegacyMUCType, MucType
13
+
14
+
15
+ class Gateway(BaseGateway):
16
+ COMPONENT_NAME = "A test"
17
+ GROUPS = True
18
+
19
+
20
+ class Contact(LegacyContact):
21
+ async def update_info(self):
22
+ if self.legacy_id.startswith("group"):
23
+ raise XMPPError()
24
+
25
+
26
+ class Session(BaseSession):
27
+ async def login(self):
28
+ return "YUP"
29
+
30
+
31
+ class Bookmarks(LegacyBookmarks):
32
+ async def fill(self) -> None:
33
+ pass
34
+
35
+
36
+ class MUC(LegacyMUC):
37
+ async def update_info(self):
38
+ if not self.legacy_id.startswith("group"):
39
+ raise XMPPError()
40
+ self.type = MucType.GROUP
41
+
42
+ @pytest.mark.usefixtures("avatar")
43
+ class TestEmptySubject(AvatarFixtureMixin, SlidgeTest):
44
+ plugin = globals()
45
+ xmpp: Gateway
46
+
47
+ def setUp(self):
48
+ super().setUp()
49
+ with self.xmpp.store.session() as orm:
50
+ user = GatewayUser(
51
+ jid=JID("romeo@montague.lit/gajim").bare,
52
+ legacy_module_data={"username": "romeo", "city": ""},
53
+ preferences={"sync_avatar": True, "sync_presence": True},
54
+ )
55
+ orm.add(user)
56
+ orm.commit()
57
+ self.run_coro(
58
+ self.xmpp._BaseGateway__dispatcher._on_user_register(
59
+ Iq(sfrom="romeo@montague.lit/gajim")
60
+ )
61
+ )
62
+ welcome = self.next_sent()
63
+ assert welcome["body"]
64
+ stanza = self.next_sent()
65
+ assert "logging in" in stanza["status"].lower(), stanza
66
+ stanza = self.next_sent()
67
+ assert "syncing contacts" in stanza["status"].lower(), stanza
68
+ stanza = self.next_sent()
69
+ assert "syncing groups" in stanza["status"].lower(), stanza
70
+ probe = self.next_sent()
71
+ assert probe.get_type() == "probe"
72
+ stanza = self.next_sent()
73
+ assert "yup" in stanza["status"].lower(), stanza
74
+
75
+ self.send( # language=XML
76
+ """
77
+ <iq type="get"
78
+ to="romeo@montague.lit"
79
+ id="1"
80
+ from="aim.shakespeare.lit">
81
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
82
+ <items node="urn:xmpp:avatar:metadata" />
83
+ </pubsub>
84
+ </iq>
85
+ """
86
+ )
87
+
88
+ def tearDown(self):
89
+ super().tearDown()
90
+ _sessions.clear()
91
+
92
+ @property
93
+ def romeo_session(self) -> Session:
94
+ return BaseSession.get_self_or_unique_subclass().from_jid(
95
+ JID("romeo@montague.lit")
96
+ )
97
+
98
+ def test_empty_subject(self):
99
+ muc = self.run_coro(self.romeo_session.bookmarks.by_legacy_id("group"))
100
+ with unittest.mock.patch("slidge.core.mixins.message_maker.uuid4", return_value="uuid"), unittest.mock.patch("uuid.uuid4", return_value="uuid"):
101
+ self.recv( # language=XML
102
+ f"""
103
+ <presence from="romeo@montague.lit/movim"
104
+ to="{muc.jid}/nick">
105
+ <x xmlns='http://jabber.org/protocol/muc' />
106
+ </presence>
107
+ """ )
108
+ self.send( # language=XML
109
+ """
110
+ <presence from="group@aim.shakespeare.lit/romeo"
111
+ to="romeo@montague.lit/movim">
112
+ <x xmlns="http://jabber.org/protocol/muc#user">
113
+ <item affiliation="member"
114
+ role="participant"
115
+ jid="romeo@montague.lit/movim" />
116
+ <status code="210" />
117
+ <status code="110" />
118
+ <status code="100" />
119
+ </x>
120
+ <occupant-id xmlns="urn:xmpp:occupant-id:0"
121
+ id="slidge-user" />
122
+ </presence>
123
+ """,
124
+ use_values=False)
125
+ self.send( # language=XML
126
+ """
127
+ <message type="groupchat"
128
+ from="group@aim.shakespeare.lit"
129
+ to="romeo@montague.lit/movim">
130
+ <stanza-id xmlns="urn:xmpp:sid:0"
131
+ id="uuid"
132
+ by="group@aim.shakespeare.lit" />
133
+ <occupant-id xmlns="urn:xmpp:occupant-id:0"
134
+ id="room" />
135
+ <subject />
136
+ </message>
137
+ """,
138
+ use_values=False,
139
+ )
@@ -43,7 +43,7 @@ class Contact(LegacyContact):
43
43
 
44
44
  class MUC(LegacyMUC):
45
45
  async def update_info(self):
46
- if self.legacy_id in ("room-noinfo", "room-duplicate-participant", "room-avatar-slow", "room-contact-conflict"):
46
+ if self.legacy_id in ("room-noinfo", "room-duplicate-participant", "room-avatar-slow", "room-contact-conflict", "room-reaction-fallback"):
47
47
  return
48
48
  if self.legacy_id == "room-avatar-in-slow-task":
49
49
  self.session.create_task(self._slow_set_avatar(), "slow-avatar")
@@ -580,3 +580,89 @@ class TestSession2(AvatarFixtureMixin, SlidgeTest):
580
580
  </iq>
581
581
  """,
582
582
  )
583
+
584
+ def test_reaction_fallback(self):
585
+ self.romeo_session.user.preferences["reaction_fallback"] = True
586
+ contact = self.run_coro(self.romeo_session.contacts.by_legacy_id("reacter"))
587
+ contact.react("msg-id", "♥")
588
+ self.send( # language=XML
589
+ """
590
+ <message xmlns="jabber:component:accept"
591
+ type="chat"
592
+ from="reacter@aim.shakespeare.lit/slidge"
593
+ to="romeo@montague.lit">
594
+ <store xmlns="urn:xmpp:hints" />
595
+ <reactions xmlns="urn:xmpp:reactions:0"
596
+ id="msg-id">
597
+ <reaction>♥</reaction>
598
+ </reactions>
599
+ <fallback xmlns="urn:xmpp:fallback:0"
600
+ for="urn:xmpp:reactions:0">
601
+ <body />
602
+ </fallback>
603
+ <body>♥</body>
604
+ </message>
605
+ """,
606
+ use_values=False
607
+ )
608
+
609
+ def test_reaction_fallback_muc(self):
610
+ self.romeo_session.user.preferences["reaction_fallback"] = True
611
+ muc: LegacyMUC = self.run_coro(
612
+ self.romeo_session.bookmarks.by_legacy_id("room-reaction-fallback")
613
+ )
614
+ # yuuuuuuck, if we only call it once, then it does not have an archive PK
615
+ muc: LegacyMUC = self.run_coro(
616
+ self.romeo_session.bookmarks.by_legacy_id("room-reaction-fallback")
617
+ )
618
+ part = self.run_coro(muc.get_participant_by_legacy_id("participant-x"))
619
+ muc.add_user_resource("gajim")
620
+ part.send_text("some text\non lines", legacy_msg_id="msg-id")
621
+ presence = self.next_sent()
622
+ assert presence["from"] == "room-reaction-fallback@aim.shakespeare.lit/participant-x"
623
+ self.send( # language=XML
624
+ """
625
+ <message xmlns="jabber:component:accept"
626
+ type="groupchat"
627
+ id="msg-id"
628
+ from="room-reaction-fallback@aim.shakespeare.lit/participant-x"
629
+ to="romeo@montague.lit/gajim">
630
+ <body>some text\non lines</body>
631
+ <active xmlns="http://jabber.org/protocol/chatstates" />
632
+ <markable xmlns="urn:xmpp:chat-markers:0" />
633
+ <stanza-id xmlns="urn:xmpp:sid:0"
634
+ id="msg-id"
635
+ by="room-reaction-fallback@aim.shakespeare.lit" />
636
+ <occupant-id xmlns="urn:xmpp:occupant-id:0"
637
+ id="participant-x@aim.shakespeare.lit/slidge" />
638
+ </message>
639
+ """,
640
+ use_values=False,
641
+ )
642
+ with unittest.mock.patch("slidge.core.mixins.message_maker.uuid4", return_value="uuid"):
643
+ part.react("msg-id", "♥")
644
+ self.send( # language=XML
645
+ """
646
+ <message xmlns="jabber:component:accept"
647
+ type="groupchat"
648
+ from="room-reaction-fallback@aim.shakespeare.lit/participant-x"
649
+ to="romeo@montague.lit/gajim">
650
+ <store xmlns="urn:xmpp:hints" />
651
+ <stanza-id xmlns="urn:xmpp:sid:0"
652
+ id="uuid"
653
+ by="room-reaction-fallback@aim.shakespeare.lit" />
654
+ <reactions xmlns="urn:xmpp:reactions:0"
655
+ id="msg-id">
656
+ <reaction>♥</reaction>
657
+ </reactions>
658
+ <fallback xmlns="urn:xmpp:fallback:0"
659
+ for="urn:xmpp:reactions:0">
660
+ <body />
661
+ </fallback>
662
+ <body>&gt; some text\n&gt; on lines\n♥</body>
663
+ <occupant-id xmlns="urn:xmpp:occupant-id:0"
664
+ id="participant-x@aim.shakespeare.lit/slidge" />
665
+ </message>
666
+ """,
667
+ use_values=False,
668
+ )
@@ -0,0 +1,119 @@
1
+ import pytest
2
+
3
+ from conftest import AvatarFixtureMixin
4
+ from slixmpp import JID, Iq
5
+
6
+ from slidge import BaseGateway, BaseSession, GatewayUser, LegacyRoster, LegacyBookmarks
7
+ from slidge.core.session import _sessions
8
+ from slidge.util.test import SlidgeTest
9
+ from slidge.util.types import LegacyUserIdType
10
+
11
+
12
+ class SomeType:
13
+ def __init__(self, a: int, b: int):
14
+ self.a = a
15
+ self.b = b
16
+
17
+ @classmethod
18
+ def from_str(cls, s: str):
19
+ a, b = (int(x) for x in s.split("-"))
20
+ return SomeType(a, b)
21
+
22
+ def __str__(self):
23
+ return f"{self.a}-{self.b}"
24
+
25
+ class Gateway(BaseGateway):
26
+ COMPONENT_NAME = "A test"
27
+ LEGACY_CONTACT_ID_TYPE = SomeType.from_str
28
+ LEGACY_ROOM_ID_TYPE = SomeType.from_str
29
+ GROUPS = True
30
+
31
+
32
+ class Session(BaseSession):
33
+ async def login(self):
34
+ return "YUP"
35
+
36
+
37
+ class Roster(LegacyRoster):
38
+ async def legacy_id_to_jid_username(self, legacy_id: LegacyUserIdType) -> str:
39
+ return f"{legacy_id.a}-{legacy_id.b}"
40
+
41
+ async def jid_username_to_legacy_id(self, jid_username: str) -> LegacyUserIdType:
42
+ return SomeType.from_str(jid_username)
43
+
44
+
45
+ class Bookmarks(LegacyBookmarks):
46
+ async def legacy_id_to_jid_username(self, legacy_id: LegacyUserIdType) -> str:
47
+ return f"{legacy_id.a}-{legacy_id.b}"
48
+
49
+ async def jid_username_to_legacy_id(self, jid_username: str) -> LegacyUserIdType:
50
+ return SomeType.from_str(jid_username)
51
+
52
+ async def fill(self):
53
+ pass
54
+
55
+ @pytest.mark.usefixtures("avatar")
56
+ class TestLegacyTypeConversion(AvatarFixtureMixin, SlidgeTest):
57
+ plugin = globals()
58
+ xmpp: Gateway
59
+
60
+ def setUp(self):
61
+ super().setUp()
62
+ with self.xmpp.store.session() as orm:
63
+ user = GatewayUser(
64
+ jid=JID("romeo@montague.lit/gajim").bare,
65
+ legacy_module_data={"username": "romeo", "city": ""},
66
+ preferences={"sync_avatar": True, "sync_presence": True},
67
+ )
68
+ orm.add(user)
69
+ orm.commit()
70
+ self.run_coro(
71
+ self.xmpp._BaseGateway__dispatcher._on_user_register(
72
+ Iq(sfrom="romeo@montague.lit/gajim")
73
+ )
74
+ )
75
+ welcome = self.next_sent()
76
+ assert welcome["body"]
77
+ stanza = self.next_sent()
78
+ assert "logging in" in stanza["status"].lower(), stanza
79
+ stanza = self.next_sent()
80
+ assert "syncing contacts" in stanza["status"].lower(), stanza
81
+ stanza = self.next_sent()
82
+ assert "syncing groups" in stanza["status"].lower(), stanza
83
+ probe = self.next_sent()
84
+ assert probe.get_type() == "probe"
85
+ stanza = self.next_sent()
86
+ assert "yup" in stanza["status"].lower(), stanza
87
+
88
+ self.send( # language=XML
89
+ """
90
+ <iq type="get"
91
+ to="romeo@montague.lit"
92
+ id="1"
93
+ from="aim.shakespeare.lit">
94
+ <pubsub xmlns="http://jabber.org/protocol/pubsub">
95
+ <items node="urn:xmpp:avatar:metadata" />
96
+ </pubsub>
97
+ </iq>
98
+ """
99
+ )
100
+
101
+ def tearDown(self):
102
+ super().tearDown()
103
+ _sessions.clear()
104
+
105
+ @property
106
+ def romeo_session(self) -> Session:
107
+ return BaseSession.get_self_or_unique_subclass().from_jid(
108
+ JID("romeo@montague.lit")
109
+ )
110
+
111
+ def test_contact(self):
112
+ contact = self.run_coro(self.romeo_session.contacts.by_legacy_id(SomeType(1, 2)))
113
+ assert contact.legacy_id.a == 1
114
+ assert contact.legacy_id.b == 2
115
+
116
+ def test_muc(self):
117
+ muc = self.run_coro(self.romeo_session.bookmarks.by_legacy_id(SomeType(1, 2)))
118
+ assert muc.legacy_id.a == 1
119
+ assert muc.legacy_id.b == 2
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes