slidge 0.3.0b1__tar.gz → 0.3.0b3__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 (186) hide show
  1. {slidge-0.3.0b1 → slidge-0.3.0b3}/PKG-INFO +1 -1
  2. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/examples/ejabberd.yaml +5 -0
  3. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/privilege.rst +6 -6
  4. {slidge-0.3.0b1 → slidge-0.3.0b3}/pyproject.toml +0 -1
  5. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/contact/roster.py +2 -0
  6. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/message/message.py +3 -1
  7. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/presence.py +5 -0
  8. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/attachment.py +4 -0
  9. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/avatar.py +7 -19
  10. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/base.py +1 -1
  11. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/disco.py +1 -1
  12. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/session.py +2 -2
  13. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/store.py +1 -1
  14. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/group/bookmarks.py +21 -3
  15. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/group/room.py +73 -36
  16. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/main.py +6 -4
  17. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/conf.py +4 -1
  18. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/types.py +2 -0
  19. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge.egg-info/PKG-INFO +1 -1
  20. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_attachment.py +22 -9
  21. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_config.py +12 -0
  22. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_muc.py +1 -3
  23. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_session.py +3 -2
  24. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_session_2.py +207 -3
  25. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_shakespeare.py +1 -2
  26. {slidge-0.3.0b1 → slidge-0.3.0b3}/.gitignore +0 -0
  27. {slidge-0.3.0b1 → slidge-0.3.0b3}/.pre-commit-config.yaml +0 -0
  28. {slidge-0.3.0b1 → slidge-0.3.0b3}/.woodpecker/container-ci.yaml +0 -0
  29. {slidge-0.3.0b1 → slidge-0.3.0b3}/.woodpecker/docs.yaml +0 -0
  30. {slidge-0.3.0b1 → slidge-0.3.0b3}/.woodpecker/package.yaml +0 -0
  31. {slidge-0.3.0b1 → slidge-0.3.0b3}/.woodpecker/test.yaml +0 -0
  32. {slidge-0.3.0b1 → slidge-0.3.0b3}/Dockerfile +0 -0
  33. {slidge-0.3.0b1 → slidge-0.3.0b3}/LICENSE +0 -0
  34. {slidge-0.3.0b1 → slidge-0.3.0b3}/README.md +0 -0
  35. {slidge-0.3.0b1 → slidge-0.3.0b3}/commitlint.config.js +0 -0
  36. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/assets/5x5.png +0 -0
  37. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/assets/slidge-color-small.png +0 -0
  38. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/assets/slidge-color.png +0 -0
  39. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/assets/slidge-mono-black.png +0 -0
  40. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/assets/slidge-mono-white.png +0 -0
  41. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/assets/slidge.svg +0 -0
  42. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/confs/movim.env +0 -0
  43. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/confs/nginx.conf +0 -0
  44. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/confs/slidge-dev.ini +0 -0
  45. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/confs/slidge-example.ini +0 -0
  46. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/hot-reload.sh +0 -0
  47. {slidge-0.3.0b1 → slidge-0.3.0b3}/dev/prettify_tests.py +0 -0
  48. {slidge-0.3.0b1 → slidge-0.3.0b3}/doap.xml +0 -0
  49. {slidge-0.3.0b1 → slidge-0.3.0b3}/docker-compose.yml +0 -0
  50. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/Makefile +0 -0
  51. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/attachments.rst +0 -0
  52. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/component.rst +0 -0
  53. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/config/index.rst +0 -0
  54. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/daemon.rst +0 -0
  55. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/examples/index.rst +0 -0
  56. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/examples/prosody.cfg.lua +0 -0
  57. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/index.rst +0 -0
  58. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/install.rst +0 -0
  59. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/admin/note.rst +0 -0
  60. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/codeberg.svg +0 -0
  61. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/conf.py +0 -0
  62. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/dev/contributing.rst +0 -0
  63. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/dev/design.rst +0 -0
  64. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/dev/howto.rst +0 -0
  65. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/dev/index.rst +0 -0
  66. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/dev/tutorial.rst +0 -0
  67. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/glossary.rst +0 -0
  68. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/index.rst +0 -0
  69. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/commands.rst +0 -0
  70. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/contacts.rst +0 -0
  71. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/foxyproxy.png +0 -0
  72. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/gajim.png +0 -0
  73. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/index.rst +0 -0
  74. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/low_profile.rst +0 -0
  75. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/movim1.png +0 -0
  76. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/movim2.png +0 -0
  77. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/note.rst +0 -0
  78. {slidge-0.3.0b1 → slidge-0.3.0b3}/docs/source/user/register.rst +0 -0
  79. {slidge-0.3.0b1 → slidge-0.3.0b3}/setup.cfg +0 -0
  80. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/__init__.py +0 -0
  81. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/__main__.py +0 -0
  82. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/__init__.py +0 -0
  83. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/adhoc.py +0 -0
  84. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/admin.py +0 -0
  85. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/base.py +0 -0
  86. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/categories.py +0 -0
  87. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/chat_command.py +0 -0
  88. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/register.py +0 -0
  89. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/command/user.py +0 -0
  90. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/contact/__init__.py +0 -0
  91. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/contact/contact.py +0 -0
  92. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/__init__.py +0 -0
  93. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/config.py +0 -0
  94. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/__init__.py +0 -0
  95. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/caps.py +0 -0
  96. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/disco.py +0 -0
  97. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/message/__init__.py +0 -0
  98. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/message/chat_state.py +0 -0
  99. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/message/marker.py +0 -0
  100. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/muc/__init__.py +0 -0
  101. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/muc/admin.py +0 -0
  102. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/muc/mam.py +0 -0
  103. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/muc/misc.py +0 -0
  104. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/muc/owner.py +0 -0
  105. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/muc/ping.py +0 -0
  106. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/registration.py +0 -0
  107. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/search.py +0 -0
  108. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/session_dispatcher.py +0 -0
  109. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/util.py +0 -0
  110. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/dispatcher/vcard.py +0 -0
  111. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/gateway.py +0 -0
  112. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/__init__.py +0 -0
  113. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/db.py +0 -0
  114. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/message.py +0 -0
  115. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/message_maker.py +0 -0
  116. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/message_text.py +0 -0
  117. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/presence.py +0 -0
  118. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/mixins/recipient.py +0 -0
  119. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/core/pubsub.py +0 -0
  120. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/__init__.py +0 -0
  121. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/alembic/__init__.py +0 -0
  122. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/alembic/env.py +0 -0
  123. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/alembic/script.py.mako +0 -0
  124. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +0 -0
  125. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/avatar.py +0 -0
  126. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/meta.py +0 -0
  127. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/db/models.py +0 -0
  128. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/group/__init__.py +0 -0
  129. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/group/archive.py +0 -0
  130. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/group/participant.py +0 -0
  131. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/migration.py +0 -0
  132. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/py.typed +0 -0
  133. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/__init__.py +0 -0
  134. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/delivery_receipt.py +0 -0
  135. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/link_preview/__init__.py +0 -0
  136. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/link_preview/link_preview.py +0 -0
  137. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/link_preview/stanza.py +0 -0
  138. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/roster.py +0 -0
  139. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0077/__init__.py +0 -0
  140. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0077/register.py +0 -0
  141. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0077/stanza.py +0 -0
  142. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0100/__init__.py +0 -0
  143. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0100/gateway.py +0 -0
  144. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0100/stanza.py +0 -0
  145. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0153/__init__.py +0 -0
  146. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0153/vcard_avatar.py +0 -0
  147. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0292/__init__.py +0 -0
  148. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/slixfix/xep_0292/vcard4.py +0 -0
  149. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/__init__.py +0 -0
  150. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/archive_msg.py +0 -0
  151. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/jid_escaping.py +0 -0
  152. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/lock.py +0 -0
  153. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/test.py +0 -0
  154. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge/util/util.py +0 -0
  155. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge.egg-info/SOURCES.txt +0 -0
  156. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge.egg-info/dependency_links.txt +0 -0
  157. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge.egg-info/entry_points.txt +0 -0
  158. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge.egg-info/requires.txt +0 -0
  159. {slidge-0.3.0b1 → slidge-0.3.0b3}/slidge.egg-info/top_level.txt +0 -0
  160. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/__init__.py +0 -0
  161. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/__main__.py +0 -0
  162. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/contact.py +0 -0
  163. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/gateway.py +0 -0
  164. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/group.py +0 -0
  165. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/legacy_client.py +0 -0
  166. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/session.py +0 -0
  167. {slidge-0.3.0b1 → slidge-0.3.0b3}/superduper/util.py +0 -0
  168. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/conftest.py +0 -0
  169. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_adhoc/test_access.py +0 -0
  170. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_adhoc/test_confirmation.py +0 -0
  171. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_adhoc/test_form.py +0 -0
  172. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_adhoc/test_reported.py +0 -0
  173. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_avatar.py +0 -0
  174. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_backfill.py +0 -0
  175. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_chat_commands.py +0 -0
  176. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_db/test_store.py +0 -0
  177. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_db/test_user.py +0 -0
  178. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_feature_restriction.py +0 -0
  179. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_mam_archivable.py +0 -0
  180. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_mds.py +0 -0
  181. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_resourceprep.py +0 -0
  182. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_set_name_before_fill.py +0 -0
  183. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_stanza_link_preview.py +0 -0
  184. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_util.py +0 -0
  185. {slidge-0.3.0b1 → slidge-0.3.0b3}/tests/test_vcard.py +0 -0
  186. {slidge-0.3.0b1 → slidge-0.3.0b3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slidge
3
- Version: 0.3.0b1
3
+ Version: 0.3.0b3
4
4
  Summary: XMPP bridging framework
5
5
  Author-email: Nicolas Cedilnik <nicoco@nicoco.fr>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -47,5 +47,10 @@ modules:
47
47
  both: slidge_rule
48
48
  message:
49
49
  outgoing: slidge_rule
50
+ iq:
51
+ "http://jabber.org/protocol/pubsub":
52
+ both: slidge_rule
53
+ "http://jabber.org/protocol/pubsub#owner":
54
+ set: slidge_rule
50
55
  mod_roster:
51
56
  versioning: true
@@ -60,12 +60,6 @@ for all changes to be taken into account
60
60
  Privileges with ejabberd
61
61
  ------------------------
62
62
 
63
- .. warning::
64
-
65
- If you want to set up privileges, you need ejabberd with version 23.10 or newer because of these two issues:
66
- https://github.com/processone/ejabberd/issues/3990 and
67
- https://github.com/processone/ejabberd/issues/3942
68
-
69
63
  .. code-block:: yaml
70
64
 
71
65
  acl:
@@ -87,3 +81,9 @@ Privileges with ejabberd
87
81
  outgoing: slidge_rule
88
82
  mod_roster:
89
83
  versioning: true
84
+
85
+ iq:
86
+ "http://jabber.org/protocol/pubsub":
87
+ both: slidge_rule
88
+ "http://jabber.org/protocol/pubsub#owner":
89
+ set: slidge_rule
@@ -99,7 +99,6 @@ exclude_lines = [
99
99
  ]
100
100
 
101
101
  [tool.pytest.ini_options]
102
- log_level = "DEBUG"
103
102
  asyncio_mode = "strict"
104
103
  filterwarnings = [
105
104
  "ignore:The object should be created within an async function:DeprecationWarning:aiohttp",
@@ -95,6 +95,8 @@ class LegacyRoster(
95
95
  contact_jid = JID(contact_jid.bare)
96
96
  async with self.lock(("username", username)):
97
97
  legacy_id = await self.jid_username_to_legacy_id(username)
98
+ if legacy_id == self.user_legacy_id:
99
+ raise ContactIsUser
98
100
  if self.get_lock(("legacy_id", legacy_id)):
99
101
  self.log.debug("Already updating %s via by_legacy_id()", contact_jid)
100
102
  return await self.by_legacy_id(legacy_id)
@@ -415,7 +415,9 @@ class MessageContentMixin(DispatcherMixin):
415
415
  with self.xmpp.store.session() as orm:
416
416
  sticker = self.xmpp.store.bob.get_sticker(orm, cid)
417
417
  if sticker is None:
418
- await self.xmpp.plugin["xep_0231"].get_bob(from_, cid)
418
+ await self.xmpp.plugin["xep_0231"].get_bob(
419
+ from_, cid, ifrom=self.xmpp.boundjid
420
+ )
419
421
  with self.xmpp.store.session() as orm:
420
422
  sticker = self.xmpp.store.bob.get_sticker(orm, cid)
421
423
  assert sticker is not None
@@ -3,6 +3,7 @@ import logging
3
3
  from slixmpp import JID, Presence
4
4
  from slixmpp.exceptions import XMPPError
5
5
 
6
+ from ...contact.roster import ContactIsUser
6
7
  from ...util.util import merge_resources
7
8
  from ..session import BaseSession
8
9
  from .util import DispatcherMixin, exceptions_to_xmpp_errors
@@ -142,6 +143,10 @@ class PresenceHandlerMixin(DispatcherMixin):
142
143
  contact = await session.contacts.by_jid(pto)
143
144
  except XMPPError:
144
145
  contact = None
146
+ except ContactIsUser:
147
+ raise XMPPError(
148
+ "bad-request", "Actions with yourself are not supported."
149
+ )
145
150
  if contact is not None:
146
151
  await self.xmpp.pubsub.on_presence_available(p, contact)
147
152
  return
@@ -303,6 +303,10 @@ class AttachmentMixin(TextMessageMixin):
303
303
  )
304
304
  if attachment.name:
305
305
  sfs["file"]["name"] = attachment.name
306
+ if attachment.disposition:
307
+ sfs["disposition"] = attachment.disposition
308
+ else:
309
+ del sfs["disposition"]
306
310
  if thumbnail is not None:
307
311
  sfs["file"].append(thumbnail)
308
312
  stored.sfs = str(sfs)
@@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, Optional
4
4
 
5
5
  from PIL import UnidentifiedImageError
6
6
  from slixmpp import JID
7
- from sqlalchemy.exc import IntegrityError
8
7
  from sqlalchemy.orm.exc import DetachedInstanceError
9
8
 
10
9
  from ...db.avatar import CachedAvatar, avatar_cache
@@ -124,25 +123,14 @@ class AvatarMixin(UpdateInfoMixin):
124
123
  avatar.path.unlink()
125
124
 
126
125
  stored_avatar = None if cached_avatar is None else cached_avatar.stored
127
- self.stored.avatar = stored_avatar
128
-
129
- try:
130
- self.commit(merge=True)
131
- except IntegrityError as e:
132
- with self.xmpp.store.session(expire_on_commit=False) as orm:
133
- if orm.object_session(self.stored):
134
- self.log.debug(
135
- "Hit integrity error, attempting to fix by refreshing participants"
136
- )
137
- orm.refresh(self.stored, ["participants"])
138
- else:
139
- self.log.debug(
140
- "Hit integrity error, attempting to fix by merging contact.stored"
141
- )
126
+ if not self._updating_info:
127
+ with self.xmpp.store.session() as orm:
128
+ with orm.no_autoflush:
142
129
  self.stored = orm.merge(self.stored)
143
- self.stored.avatar = stored_avatar
144
- orm.add(self.stored)
145
- orm.commit()
130
+ orm.refresh(self.stored)
131
+
132
+ self.stored.avatar = stored_avatar
133
+ self.commit(merge=True)
146
134
 
147
135
  self._post_avatar_update(cached_avatar)
148
136
 
@@ -19,7 +19,7 @@ class Base:
19
19
  xmpp: "BaseGateway" = NotImplemented
20
20
 
21
21
  jid: JID = NotImplemented
22
- name: str = NotImplemented
22
+ name: str | None = NotImplemented
23
23
 
24
24
  @property
25
25
  def user_jid(self):
@@ -13,7 +13,7 @@ class BaseDiscoMixin(Base):
13
13
  DISCO_NAME: str = NotImplemented
14
14
  DISCO_LANG = None
15
15
 
16
- def _get_disco_name(self):
16
+ def _get_disco_name(self) -> str | None:
17
17
  if self.DISCO_NAME is NotImplemented:
18
18
  return self.xmpp.COMPONENT_NAME
19
19
  return self.DISCO_NAME or self.xmpp.COMPONENT_NAME
@@ -133,8 +133,8 @@ class BaseSession(
133
133
  self.log.debug("Removing fut %s", fut)
134
134
  self.__tasks.remove(fut)
135
135
 
136
- def create_task(self, coro) -> asyncio.Task:
137
- task = self.xmpp.loop.create_task(coro)
136
+ def create_task(self, coro, name: str | None = None) -> asyncio.Task:
137
+ task = self.xmpp.loop.create_task(coro, name=name)
138
138
  self.__tasks.add(task)
139
139
  self.log.debug("Creating task %s", task)
140
140
  task.add_done_callback(lambda _: self.__remove_task(task))
@@ -478,7 +478,7 @@ class ParticipantStore:
478
478
 
479
479
  @staticmethod
480
480
  def get_all(
481
- session, room_pk: int, user_included: bool = True
481
+ session: Session, room_pk: int, user_included: bool = True
482
482
  ) -> Iterator[Participant]:
483
483
  query = select(Participant).where(Participant.room_id == room_pk)
484
484
  if not user_included:
@@ -1,6 +1,6 @@
1
1
  import abc
2
2
  import logging
3
- from typing import TYPE_CHECKING, Generic, Iterator, Optional, Type
3
+ from typing import TYPE_CHECKING, Generic, Iterator, Literal, Optional, Type, overload
4
4
 
5
5
  from slixmpp import JID
6
6
  from slixmpp.exceptions import XMPPError
@@ -119,13 +119,31 @@ class LegacyBookmarks(
119
119
  return self.from_store(stored)
120
120
  return None
121
121
 
122
- async def by_legacy_id(self, legacy_id: LegacyGroupIdType) -> LegacyMUCType:
122
+ @overload
123
+ async def by_legacy_id(self, legacy_id: LegacyGroupIdType) -> "LegacyMUCType": ...
124
+
125
+ @overload
126
+ async def by_legacy_id(
127
+ self, legacy_id: LegacyGroupIdType, create: Literal[False]
128
+ ) -> "LegacyMUCType | None": ...
129
+
130
+ @overload
131
+ async def by_legacy_id(
132
+ self, legacy_id: LegacyGroupIdType, create: Literal[True]
133
+ ) -> "LegacyMUCType": ...
134
+
135
+ async def by_legacy_id(
136
+ self, legacy_id: LegacyGroupIdType, create: bool = False
137
+ ) -> LegacyMUCType | None:
123
138
  async with self.lock(("legacy_id", legacy_id)):
124
139
  local = await self.legacy_id_to_jid_local_part(legacy_id)
125
140
  jid = JID(f"{local}@{self.xmpp.boundjid}")
126
141
  if self.get_lock(("bare", jid.bare)):
127
142
  self.session.log.debug("Already updating %s via by_jid()", jid)
128
- return await self.by_jid(jid)
143
+ if create:
144
+ return await self.by_jid(jid)
145
+ else:
146
+ return self.by_jid_only_if_exists(jid)
129
147
 
130
148
  with self.xmpp.store.session() as orm:
131
149
  stored = (
@@ -3,9 +3,20 @@ import logging
3
3
  import re
4
4
  import string
5
5
  import warnings
6
+ from asyncio import Lock
7
+ from contextlib import asynccontextmanager
6
8
  from copy import copy
7
9
  from datetime import datetime, timedelta, timezone
8
- from typing import TYPE_CHECKING, AsyncIterator, Generic, Optional, Type, Union
10
+ from typing import (
11
+ TYPE_CHECKING,
12
+ AsyncIterator,
13
+ Generic,
14
+ Literal,
15
+ Optional,
16
+ Type,
17
+ Union,
18
+ overload,
19
+ )
9
20
  from uuid import uuid4
10
21
 
11
22
  import sqlalchemy as sa
@@ -18,7 +29,6 @@ from slixmpp.plugins.xep_0469.stanza import NS as PINNING_NS
18
29
  from slixmpp.plugins.xep_0492.stanza import NS as NOTIFY_NS
19
30
  from slixmpp.plugins.xep_0492.stanza import WhenLiteral
20
31
  from slixmpp.xmlstream import ET
21
- from sqlalchemy.exc import InvalidRequestError
22
32
  from sqlalchemy.orm import Session as OrmSession
23
33
 
24
34
  from ..contact.contact import LegacyContact
@@ -28,7 +38,6 @@ from ..core.mixins.disco import ChatterDiscoMixin
28
38
  from ..core.mixins.recipient import ReactionRecipientMixin, ThreadRecipientMixin
29
39
  from ..db.models import Participant, Room
30
40
  from ..util.jid_escaping import unescape_node
31
- from ..util.lock import NamedLockMixin
32
41
  from ..util.types import (
33
42
  HoleBound,
34
43
  LegacyGroupIdType,
@@ -56,7 +65,6 @@ class LegacyMUC(
56
65
  LegacyGroupIdType, LegacyMessageType, LegacyParticipantType, LegacyUserIdType
57
66
  ],
58
67
  AvatarMixin,
59
- NamedLockMixin,
60
68
  ChatterDiscoMixin,
61
69
  ReactionRecipientMixin,
62
70
  ThreadRecipientMixin,
@@ -242,10 +250,22 @@ class LegacyMUC(
242
250
  )
243
251
  self.commit()
244
252
 
253
+ @asynccontextmanager
254
+ async def lock(self, id_: str) -> AsyncIterator[None]:
255
+ async with self.session.lock((self.legacy_id, id_)):
256
+ yield
257
+
258
+ def get_lock(self, id_: str) -> Lock | None:
259
+ return self.session.get_lock((self.legacy_id, id_))
260
+
245
261
  async def __fill_participants(self) -> None:
246
- if self.participants_filled:
247
- return
248
262
  async with self.lock("fill participants"):
263
+ with self.xmpp.store.session(expire_on_commit=False) as orm:
264
+ orm.add(self.stored)
265
+ with orm.no_autoflush:
266
+ orm.refresh(self.stored, ["participants_filled"])
267
+ if self.participants_filled:
268
+ return
249
269
  parts: list[Participant] = []
250
270
  resources = set[str]()
251
271
  # During fill_participants(), self.get_participant*() methods may
@@ -261,17 +281,13 @@ class LegacyMUC(
261
281
  resources.add(participant.stored.resource)
262
282
  with self.xmpp.store.session(expire_on_commit=False) as orm:
263
283
  orm.add(self.stored)
264
- # because self.participants() is async, self.stored may be out of sync at
265
- # this point.
284
+ # because self.fill_participants() is async, self.stored may be stale at
285
+ # this point, and the only thing we want to update is the participant list
286
+ # and the participant_filled attribute.
266
287
  with orm.no_autoflush:
267
288
  orm.refresh(self.stored)
268
- for part in parts:
269
- try:
270
- self.stored.participants.append(part)
271
- except InvalidRequestError:
272
- # the participant was already stored in the DB. `part` may even
273
- # be out-of-sync, so it's fine to just ditch it.
274
- pass
289
+ for part in parts:
290
+ orm.merge(part)
275
291
  self.stored.participants_filled = True
276
292
  orm.commit()
277
293
 
@@ -290,10 +306,14 @@ class LegacyMUC(
290
306
  yield self.participant_from_store(db_participant)
291
307
 
292
308
  async def __fill_history(self) -> None:
293
- if self.stored.history_filled:
294
- self.log.debug("History has already been fetched.")
295
- return
296
309
  async with self.lock("fill history"):
310
+ with self.xmpp.store.session(expire_on_commit=False) as orm:
311
+ orm.add(self.stored)
312
+ with orm.no_autoflush:
313
+ orm.refresh(self.stored, ["history_filled"])
314
+ if self.stored.history_filled:
315
+ self.log.debug("History has already been fetched.")
316
+ return
297
317
  log.debug("Fetching history for %s", self)
298
318
  try:
299
319
  before, after = self.archive.get_hole_bounds()
@@ -314,16 +334,15 @@ class LegacyMUC(
314
334
  self.stored.history_filled = True
315
335
  self.commit(merge=True)
316
336
 
317
- @property
318
- def DISCO_NAME(self) -> str: # type:ignore
319
- return self.name or "unnamed-room"
337
+ def _get_disco_name(self) -> str | None:
338
+ return self.name
320
339
 
321
340
  @property
322
- def name(self) -> str:
323
- return self.stored.name or "unnamed-room"
341
+ def name(self) -> str | None:
342
+ return self.stored.name
324
343
 
325
344
  @name.setter
326
- def name(self, n: str) -> None:
345
+ def name(self, n: str | None) -> None:
327
346
  if self.name == n:
328
347
  return
329
348
  self.stored.name = n
@@ -662,7 +681,7 @@ class LegacyMUC(
662
681
  since=since,
663
682
  )
664
683
  self.__get_subject_setter_participant().set_room_subject(
665
- self.subject if self.HAS_SUBJECT else (self.description or self.name),
684
+ self.subject if self.HAS_SUBJECT else (self.description or self.name or ""),
666
685
  user_full_jid,
667
686
  self.subject_date,
668
687
  )
@@ -753,9 +772,24 @@ class LegacyMUC(
753
772
  """
754
773
  return self._participant_cls(self, Participant(), is_system=True)
755
774
 
775
+ @overload
756
776
  async def get_participant_by_contact(
757
777
  self, c: "LegacyContact"
758
- ) -> "LegacyParticipantType":
778
+ ) -> "LegacyParticipantType": ...
779
+
780
+ @overload
781
+ async def get_participant_by_contact(
782
+ self, c: "LegacyContact", create: Literal[False]
783
+ ) -> "LegacyParticipantType | None": ...
784
+
785
+ @overload
786
+ async def get_participant_by_contact(
787
+ self, c: "LegacyContact", create: Literal[True]
788
+ ) -> "LegacyParticipantType": ...
789
+
790
+ async def get_participant_by_contact(
791
+ self, c: "LegacyContact", create: bool = True
792
+ ) -> "LegacyParticipantType | None":
759
793
  """
760
794
  Get a non-anonymous participant.
761
795
 
@@ -763,20 +797,23 @@ class LegacyMUC(
763
797
  that the Contact jid is associated to this participant
764
798
 
765
799
  :param c: The :class:`.LegacyContact` instance corresponding to this contact
800
+ :param create: Creates the participant if it does not exist.
766
801
  :return:
767
802
  """
768
803
  await self.session.contacts.ready
769
804
 
770
- if not self.get_lock("fill participants"):
771
- with self.xmpp.store.session() as orm:
772
- self.stored = orm.merge(self.stored)
773
- stored = (
774
- orm.query(Participant)
775
- .filter_by(contact=c.stored, room=self.stored)
776
- .one_or_none()
777
- )
778
- if stored is not None:
779
- return self.participant_from_store(stored=stored, contact=c)
805
+ with self.xmpp.store.session() as orm:
806
+ self.stored = orm.merge(self.stored)
807
+ stored = (
808
+ orm.query(Participant)
809
+ .filter_by(contact=c.stored, room=self.stored)
810
+ .one_or_none()
811
+ )
812
+ if stored is None:
813
+ if not create:
814
+ return None
815
+ else:
816
+ return self.participant_from_store(stored=stored, contact=c)
780
817
 
781
818
  nickname = c.name or unescape_node(c.jid.node)
782
819
 
@@ -166,10 +166,12 @@ def main(module_name: str | None = None) -> None:
166
166
  f"_{config.LEGACY_MODULE.split('.')[-1].upper()}_"
167
167
  )
168
168
  logging.debug("Env var prefix: %s", ConfigModule.ENV_VAR_PREFIX)
169
- ConfigModule(plugin_config_obj).set_conf(unknown_argv)
170
- else:
171
- if unknown_argv:
172
- raise RuntimeError("Some arguments have not been recognized", unknown_argv)
169
+ _, unknown_argv = ConfigModule(plugin_config_obj).set_conf(unknown_argv)
170
+
171
+ if unknown_argv:
172
+ logging.error(
173
+ f"These config options have not been recognized and ignored: {unknown_argv}"
174
+ )
173
175
 
174
176
  migrate()
175
177
 
@@ -159,7 +159,10 @@ class ConfigModule:
159
159
  upper = _argv_to_option_name(a)
160
160
  opt = options_long.get(upper)
161
161
  if opt and opt.type is bool:
162
- if _argv_to_option_name(aa) not in options_long:
162
+ if (
163
+ not aa.startswith("-")
164
+ and _argv_to_option_name(aa) not in options_long
165
+ ):
163
166
  log.debug("Removing %s from argv", aa)
164
167
  skip_next = True
165
168
 
@@ -72,6 +72,7 @@ MucRole = Literal["visitor", "participant", "moderator", "none"]
72
72
  ClientType = Literal[
73
73
  "bot", "console", "game", "handheld", "pc", "phone", "sms", "tablet", "web"
74
74
  ]
75
+ AttachmentDisposition = Literal["attachment", "inline"]
75
76
 
76
77
 
77
78
  @dataclass
@@ -115,6 +116,7 @@ class LegacyAttachment:
115
116
  legacy_file_id: Optional[Union[str, int]] = None
116
117
  url: Optional[str] = None
117
118
  caption: Optional[str] = None
119
+ disposition: Optional[AttachmentDisposition] = None
118
120
  """
119
121
  A caption for this specific image. For a global caption for a list of attachments,
120
122
  use the ``body`` parameter of :meth:`.AttachmentMixin.send_files`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slidge
3
- Version: 0.3.0b1
3
+ Version: 0.3.0b3
4
4
  Summary: XMPP bridging framework
5
5
  Author-email: Nicolas Cedilnik <nicoco@nicoco.fr>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -74,15 +74,18 @@ class Base(Shakespeare, AvatarFixtureMixin):
74
74
  use_values=False,
75
75
  )
76
76
 
77
- def _assert_file(self, url="http://url"):
77
+ def _assert_file(self, url="http://url", disposition: str = ''):
78
78
  when = (
79
79
  datetime.fromtimestamp(self.avatar_path.stat().st_mtime)
80
80
  .isoformat()
81
81
  .replace("+00:00", "Z")
82
82
  )
83
+ if disposition:
84
+ el = f"disposition='{disposition}'"
85
+ else:
86
+ el = ""
83
87
  self.send( # language=XML
84
- f"""
85
- <message type="chat"
88
+ f"""<message type="chat"
86
89
  from="juliet@aim.shakespeare.lit/slidge"
87
90
  to="romeo@montague.lit">
88
91
  <reference xmlns="urn:xmpp:reference:0"
@@ -108,8 +111,7 @@ class Base(Shakespeare, AvatarFixtureMixin):
108
111
  </file>
109
112
  </media-sharing>
110
113
  </reference>
111
- <file-sharing xmlns="urn:xmpp:sfs:0"
112
- disposition="inline">
114
+ <file-sharing xmlns="urn:xmpp:sfs:0" {el}>
113
115
  <sources>
114
116
  <url-data xmlns="http://jabber.org/protocol/url-data"
115
117
  target="{url}" />
@@ -153,13 +155,13 @@ class TestBodyOnly(Base):
153
155
 
154
156
 
155
157
  class TestAttachmentUpload(Base):
156
- def __test_basic(self, attachment: LegacyAttachment, upload_kwargs: dict):
158
+ def __test_basic(self, attachment: LegacyAttachment, upload_kwargs: dict, disposition: str = ''):
157
159
  """
158
160
  Basic test that file is uploaded.
159
161
  """
160
162
  self.run_coro(self.juliet.send_files([attachment]))
161
163
  self.http_upload.assert_called_with(**upload_kwargs)
162
- self._assert_file()
164
+ self._assert_file(disposition=disposition)
163
165
 
164
166
  def _test_reuse(self, attachment: LegacyAttachment, upload_kwargs: dict):
165
167
  """
@@ -185,6 +187,18 @@ class TestAttachmentUpload(Base):
185
187
  ),
186
188
  )
187
189
 
190
+ def test_path_attachment(self):
191
+ self.__test_basic(
192
+ LegacyAttachment(path=self.avatar_path, disposition="attachment"),
193
+ dict(
194
+ filename=self.avatar_path,
195
+ content_type="image/png",
196
+ ifrom=self.xmpp.boundjid,
197
+ domain=None,
198
+ ),
199
+ "attachment",
200
+ )
201
+
188
202
  def test_thumbhash(self):
189
203
  self.__test_basic(
190
204
  LegacyAttachment(path=self.avatar_path, content_type="image/png"),
@@ -441,8 +455,7 @@ class TestAttachmentNoUpload(Base):
441
455
  </file>
442
456
  </media-sharing>
443
457
  </reference>
444
- <file-sharing xmlns="urn:xmpp:sfs:0"
445
- disposition="inline">
458
+ <file-sharing xmlns="urn:xmpp:sfs:0">
446
459
  <sources>
447
460
  <url-data xmlns="http://jabber.org/protocol/url-data"
448
461
  target="https://url/uuid/uuid/5x5.png" />
@@ -272,3 +272,15 @@ def test_rest(tmp_path):
272
272
 
273
273
  assert Config1.PROUT == "caca"
274
274
  assert Config2.PROUT2 == "something"
275
+
276
+
277
+ def test_unrecognized_after_boolean():
278
+ class Config:
279
+ PROUT = True
280
+ PROUT__DOC = "?"
281
+
282
+
283
+ configurator = ConfigModule(Config)
284
+ ns, rest = configurator.set_conf(["--prout", "--joujoujou"])
285
+ assert Config.PROUT
286
+ assert rest == ["--joujoujou"]
@@ -4189,9 +4189,7 @@ class TestMUCAdmin(Base):
4189
4189
  </field>
4190
4190
  <field label='Natural-Language Room Name'
4191
4191
  type='text-single'
4192
- var='muc#roomconfig_roomname'>
4193
- <value>unnamed-room</value>
4194
- </field>
4192
+ var='muc#roomconfig_roomname'></field>
4195
4193
  <field label='Short Description of Room'
4196
4194
  type='text-single'
4197
4195
  var='muc#roomconfig_roomdesc'>
@@ -328,10 +328,11 @@ class TestSession(AvatarFixtureMixin, SlidgeTest):
328
328
  """
329
329
  self.recv(sticker_stanza)
330
330
  self.send( # language=XML
331
- """
331
+ f"""
332
332
  <iq id="2"
333
333
  type="get"
334
- to="romeo@montague.lit/movim">
334
+ to="romeo@montague.lit/movim"
335
+ from="{self.xmpp.boundjid.bare}">
335
336
  <data xmlns="urn:xmpp:bob"
336
337
  cid="sha1+4b97ce7f0f06a0e05999f3c719cd5b4f3da992a7@bob.xmpp.org" />
337
338
  </iq>