slidge 0.2.0b0__tar.gz → 0.2.0b1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. {slidge-0.2.0b0 → slidge-0.2.0b1}/PKG-INFO +1 -1
  2. {slidge-0.2.0b0 → slidge-0.2.0b1}/pyproject.toml +2 -1
  3. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/__version__.py +1 -1
  4. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/command/adhoc.py +31 -15
  5. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/command/admin.py +11 -4
  6. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/command/base.py +5 -2
  7. slidge-0.2.0b1/slidge/command/categories.py +13 -0
  8. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/command/chat_command.py +14 -1
  9. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/command/user.py +17 -9
  10. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/config.py +6 -0
  11. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +2 -0
  12. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +1 -1
  13. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +6 -0
  14. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +7 -6
  15. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +4 -0
  16. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/models.py +1 -1
  17. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/util/util.py +18 -0
  18. slidge-0.2.0b0/slidge/command/categories.py +0 -3
  19. {slidge-0.2.0b0 → slidge-0.2.0b1}/LICENSE +0 -0
  20. {slidge-0.2.0b0 → slidge-0.2.0b1}/README.md +0 -0
  21. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/__init__.py +0 -0
  22. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/__main__.py +0 -0
  23. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/command/__init__.py +0 -0
  24. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/command/register.py +0 -0
  25. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/contact/__init__.py +0 -0
  26. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/contact/contact.py +0 -0
  27. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/contact/roster.py +0 -0
  28. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/__init__.py +0 -0
  29. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/__init__.py +0 -0
  30. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/caps.py +0 -0
  31. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/disco.py +0 -0
  32. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/message/__init__.py +0 -0
  33. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/message/chat_state.py +0 -0
  34. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/message/marker.py +0 -0
  35. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/message/message.py +0 -0
  36. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/muc/__init__.py +0 -0
  37. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/muc/admin.py +0 -0
  38. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/muc/mam.py +0 -0
  39. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/muc/misc.py +0 -0
  40. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/muc/owner.py +0 -0
  41. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/muc/ping.py +0 -0
  42. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/presence.py +0 -0
  43. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/registration.py +0 -0
  44. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/search.py +0 -0
  45. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/session_dispatcher.py +0 -0
  46. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/util.py +0 -0
  47. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/dispatcher/vcard.py +0 -0
  48. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/gateway.py +0 -0
  49. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/__init__.py +0 -0
  50. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/attachment.py +0 -0
  51. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/avatar.py +0 -0
  52. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/base.py +0 -0
  53. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/db.py +0 -0
  54. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/disco.py +0 -0
  55. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/lock.py +0 -0
  56. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/message.py +0 -0
  57. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/message_maker.py +0 -0
  58. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/message_text.py +0 -0
  59. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/presence.py +0 -0
  60. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/mixins/recipient.py +0 -0
  61. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/pubsub.py +0 -0
  62. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/core/session.py +0 -0
  63. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/__init__.py +0 -0
  64. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/__init__.py +0 -0
  65. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/env.py +0 -0
  66. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/old_user_store.py +0 -0
  67. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/script.py.mako +0 -0
  68. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +0 -0
  69. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +0 -0
  70. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +0 -0
  71. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +0 -0
  72. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/45c24cc73c91_add_bob.py +0 -0
  73. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +0 -0
  74. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +0 -0
  75. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +0 -0
  76. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +0 -0
  77. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +0 -0
  78. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +0 -0
  79. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +0 -0
  80. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/avatar.py +0 -0
  81. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/meta.py +0 -0
  82. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/db/store.py +2 -2
  83. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/group/__init__.py +0 -0
  84. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/group/archive.py +0 -0
  85. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/group/bookmarks.py +0 -0
  86. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/group/participant.py +0 -0
  87. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/group/room.py +0 -0
  88. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/main.py +0 -0
  89. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/migration.py +0 -0
  90. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/py.typed +0 -0
  91. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/__init__.py +0 -0
  92. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/delivery_receipt.py +0 -0
  93. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/link_preview/__init__.py +0 -0
  94. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/link_preview/link_preview.py +0 -0
  95. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/link_preview/stanza.py +0 -0
  96. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/roster.py +0 -0
  97. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0077/__init__.py +0 -0
  98. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0077/register.py +0 -0
  99. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0077/stanza.py +0 -0
  100. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0100/__init__.py +0 -0
  101. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0100/gateway.py +0 -0
  102. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0100/stanza.py +0 -0
  103. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0153/__init__.py +0 -0
  104. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0153/stanza.py +0 -0
  105. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0153/vcard_avatar.py +0 -0
  106. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0264/__init__.py +0 -0
  107. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0264/stanza.py +0 -0
  108. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0264/thumbnail.py +0 -0
  109. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0292/__init__.py +0 -0
  110. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0292/vcard4.py +0 -0
  111. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0313/__init__.py +0 -0
  112. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0313/mam.py +0 -0
  113. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0313/stanza.py +0 -0
  114. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0317/__init__.py +0 -0
  115. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0317/hats.py +0 -0
  116. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0317/stanza.py +0 -0
  117. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0356_old/__init__.py +0 -0
  118. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0356_old/privilege.py +0 -0
  119. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0356_old/stanza.py +0 -0
  120. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0424/__init__.py +0 -0
  121. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0424/retraction.py +0 -0
  122. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0424/stanza.py +0 -0
  123. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0490/__init__.py +0 -0
  124. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0490/mds.py +0 -0
  125. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/slixfix/xep_0490/stanza.py +0 -0
  126. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/util/__init__.py +0 -0
  127. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/util/archive_msg.py +0 -0
  128. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/util/conf.py +0 -0
  129. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/util/db.py +0 -0
  130. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/util/test.py +0 -0
  131. {slidge-0.2.0b0 → slidge-0.2.0b1}/slidge/util/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: slidge
3
- Version: 0.2.0b0
3
+ Version: 0.2.0b1
4
4
  Summary: XMPP bridging framework
5
5
  Home-page: https://sr.ht/~nicoco/slidge/
6
6
  License: AGPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "slidge"
3
- version = "0.2.0beta0"
3
+ version = "0.2.0beta1"
4
4
  description = "XMPP bridging framework"
5
5
  authors = ["Nicolas Cedilnik <nicoco@nicoco.fr>"]
6
6
  readme = "README.md"
@@ -51,6 +51,7 @@ xmldiff = "^2.5"
51
51
  types-pillow = "^9.5.0.0"
52
52
  pre-commit = "^3.3.0"
53
53
  coverage = "^7.2.7"
54
+ emoji = "*"
54
55
 
55
56
  [tool.poetry.group.dev.dependencies.slidge-dev-helpers]
56
57
  git = "https://git.sr.ht/~nicoco/slidge-dev-helpers"
@@ -2,4 +2,4 @@ from slidge.util.util import get_version # noqa: F401
2
2
 
3
3
  # this is modified before publish, but if someone cloned from the repo,
4
4
  # it can help
5
- __version__ = "0.2.0beta0"
5
+ __version__ = "0.2.0beta1"
@@ -9,8 +9,11 @@ from slixmpp.exceptions import XMPPError
9
9
  from slixmpp.plugins.xep_0004 import Form as SlixForm # type: ignore[attr-defined]
10
10
  from slixmpp.plugins.xep_0030.stanza.items import DiscoItems
11
11
 
12
+ from ..core import config
13
+ from ..util.util import strip_leading_emoji
12
14
  from . import Command, CommandResponseType, Confirmation, Form, TableResult
13
15
  from .base import FormField
16
+ from .categories import CommandCategory
14
17
 
15
18
  if TYPE_CHECKING:
16
19
  from ..core.gateway import BaseGateway
@@ -46,19 +49,19 @@ class AdhocProvider:
46
49
  return await self.__handle_result(session, result, adhoc_session)
47
50
 
48
51
  async def __handle_category_list(
49
- self, category: str, iq: Iq, adhoc_session: AdhocSessionType
52
+ self, category: CommandCategory, iq: Iq, adhoc_session: AdhocSessionType
50
53
  ) -> AdhocSessionType:
51
54
  try:
52
55
  session = self.xmpp.get_session_from_stanza(iq)
53
56
  except XMPPError:
54
57
  session = None
55
- commands = []
56
- for command in self._categories[category]:
58
+ commands: dict[str, Command] = {}
59
+ for command in self._categories[category.node]:
57
60
  try:
58
61
  command.raise_if_not_authorized(iq.get_from())
59
62
  except XMPPError:
60
63
  continue
61
- commands.append(command)
64
+ commands[command.NODE] = command
62
65
  if len(commands) == 0:
63
66
  raise XMPPError(
64
67
  "not-authorized", "There is no command you can run in this category"
@@ -66,7 +69,7 @@ class AdhocProvider:
66
69
  return await self.__handle_result(
67
70
  session,
68
71
  Form(
69
- category,
72
+ category.name,
70
73
  "",
71
74
  [
72
75
  FormField(
@@ -74,8 +77,11 @@ class AdhocProvider:
74
77
  label="Command",
75
78
  type="list-single",
76
79
  options=[
77
- {"label": command.NAME, "value": str(i)}
78
- for i, command in enumerate(commands)
80
+ {
81
+ "label": strip_leading_emoji_if_needed(command.NAME),
82
+ "value": command.NODE,
83
+ }
84
+ for command in commands.values()
79
85
  ],
80
86
  )
81
87
  ],
@@ -86,12 +92,12 @@ class AdhocProvider:
86
92
 
87
93
  async def __handle_category_choice(
88
94
  self,
89
- commands: list[Command],
95
+ commands: dict[str, Command],
90
96
  form_values: dict[str, str],
91
97
  session: "BaseSession[Any, Any]",
92
98
  jid: JID,
93
99
  ):
94
- command = commands[int(form_values["command"])]
100
+ command = commands[form_values["command"]]
95
101
  result = await self.__wrap_handler(command.run, session, jid)
96
102
  return result
97
103
 
@@ -207,19 +213,23 @@ class AdhocProvider:
207
213
  self.xmpp.plugin["xep_0050"].add_command( # type: ignore[no-untyped-call]
208
214
  jid=jid,
209
215
  node=command.NODE,
210
- name=command.NAME,
216
+ name=strip_leading_emoji_if_needed(command.NAME),
211
217
  handler=partial(self.__wrap_initial_handler, command),
212
218
  )
213
219
  else:
214
- if category not in self._categories:
215
- self._categories[category] = list[Command]()
220
+ if isinstance(category, str):
221
+ category = CommandCategory(category, category)
222
+ node = category.node
223
+ name = category.name
224
+ if node not in self._categories:
225
+ self._categories[node] = list[Command]()
216
226
  self.xmpp.plugin["xep_0050"].add_command( # type: ignore[no-untyped-call]
217
227
  jid=jid,
218
- node=category,
219
- name=category,
228
+ node=node,
229
+ name=strip_leading_emoji_if_needed(name),
220
230
  handler=partial(self.__handle_category_list, category),
221
231
  )
222
- self._categories[category].append(command)
232
+ self._categories[node].append(command)
223
233
 
224
234
  async def get_items(self, jid: JID, node: str, iq: Iq) -> DiscoItems:
225
235
  """
@@ -262,4 +272,10 @@ class AdhocProvider:
262
272
  return filtered_items
263
273
 
264
274
 
275
+ def strip_leading_emoji_if_needed(text: str) -> str:
276
+ if config.STRIP_LEADING_EMOJI_ADHOC:
277
+ return strip_leading_emoji(text)
278
+ return text
279
+
280
+
265
281
  log = logging.getLogger(__name__)
@@ -11,6 +11,7 @@ from slixmpp.exceptions import XMPPError
11
11
  from ..core import config
12
12
  from ..util.types import AnyBaseSession
13
13
  from .base import (
14
+ NODE_PREFIX,
14
15
  Command,
15
16
  CommandAccess,
16
17
  Confirmation,
@@ -21,6 +22,8 @@ from .base import (
21
22
  )
22
23
  from .categories import ADMINISTRATION
23
24
 
25
+ NODE_PREFIX = NODE_PREFIX + "admin/"
26
+
24
27
 
25
28
  class AdminCommand(Command):
26
29
  ACCESS = CommandAccess.ADMIN_ONLY
@@ -30,7 +33,8 @@ class AdminCommand(Command):
30
33
  class ListUsers(AdminCommand):
31
34
  NAME = "👤 List registered users"
32
35
  HELP = "List the users registered to this gateway"
33
- NODE = CHAT_COMMAND = "list_users"
36
+ CHAT_COMMAND = "list_users"
37
+ NODE = NODE_PREFIX + CHAT_COMMAND
34
38
 
35
39
  async def run(self, _session, _ifrom, *_):
36
40
  items = []
@@ -51,7 +55,8 @@ class ListUsers(AdminCommand):
51
55
  class SlidgeInfo(AdminCommand):
52
56
  NAME = "ℹ️ Server information"
53
57
  HELP = "List the users registered to this gateway"
54
- NODE = CHAT_COMMAND = "info"
58
+ CHAT_COMMAND = "info"
59
+ NODE = NODE_PREFIX + CHAT_COMMAND
55
60
  ACCESS = CommandAccess.ANY
56
61
 
57
62
  async def run(self, _session, _ifrom, *_):
@@ -105,7 +110,8 @@ class SlidgeInfo(AdminCommand):
105
110
  class DeleteUser(AdminCommand):
106
111
  NAME = "❌ Delete a user"
107
112
  HELP = "Unregister a user from the gateway"
108
- NODE = CHAT_COMMAND = "delete_user"
113
+ CHAT_COMMAND = "delete_user"
114
+ NODE = NODE_PREFIX + CHAT_COMMAND
109
115
 
110
116
  async def run(self, _session, _ifrom, *_):
111
117
  return Form(
@@ -141,7 +147,8 @@ class DeleteUser(AdminCommand):
141
147
  class ChangeLoglevel(AdminCommand):
142
148
  NAME = "📋 Change the verbosity of the logs"
143
149
  HELP = "Set the logging level"
144
- NODE = CHAT_COMMAND = "loglevel"
150
+ CHAT_COMMAND = "loglevel"
151
+ NODE = NODE_PREFIX + CHAT_COMMAND
145
152
 
146
153
  async def run(self, _session, _ifrom, *_):
147
154
  return Form(
@@ -25,9 +25,12 @@ from slixmpp.types import JidStr
25
25
  from ..core import config
26
26
  from ..util.types import AnyBaseSession, FieldType
27
27
 
28
+ NODE_PREFIX = "https://slidge.im/command/core/"
29
+
28
30
  if TYPE_CHECKING:
29
31
  from ..core.gateway import BaseGateway
30
32
  from ..core.session import BaseSession
33
+ from .categories import CommandCategory
31
34
 
32
35
 
33
36
  HandlerType = Union[
@@ -178,8 +181,8 @@ class Form:
178
181
  """
179
182
  form = SlixForm() # type: ignore[no-untyped-call]
180
183
  form["type"] = "form"
181
- form["instructions"] = self.instructions
182
184
  form["title"] = self.title
185
+ form["instructions"] = self.instructions
183
186
  for fi in self.fields:
184
187
  form.append(fi.get_xml())
185
188
  return form
@@ -347,7 +350,7 @@ class Command(ABC):
347
350
  Who can use this command
348
351
  """
349
352
 
350
- CATEGORY: Optional[str] = None
353
+ CATEGORY: Optional[Union[str, "CommandCategory"]] = None
351
354
  """
352
355
  If used, the command will be under this top-level category.
353
356
  Use the same string for several commands to group them.
@@ -0,0 +1,13 @@
1
+ from typing import NamedTuple
2
+
3
+ from .base import NODE_PREFIX
4
+
5
+
6
+ class CommandCategory(NamedTuple):
7
+ name: str
8
+ node: str
9
+
10
+
11
+ ADMINISTRATION = CommandCategory("🛷️ Slidge administration", NODE_PREFIX + "admin")
12
+ CONTACTS = CommandCategory("👤 Contacts", NODE_PREFIX + "contacts")
13
+ GROUPS = CommandCategory("👥 Groups", NODE_PREFIX + "groups")
@@ -13,6 +13,7 @@ from slixmpp.exceptions import XMPPError
13
13
  from slixmpp.types import JidStr, MessageTypes
14
14
 
15
15
  from . import Command, CommandResponseType, Confirmation, Form, TableResult
16
+ from .categories import CommandCategory
16
17
 
17
18
  if TYPE_CHECKING:
18
19
  from ..core.gateway import BaseGateway
@@ -280,7 +281,19 @@ class ChatCommandProvider:
280
281
  def _help(self, mfrom: JID):
281
282
  msg = "Available commands:"
282
283
  for c in sorted(
283
- self._commands.values(), key=lambda co: (co.CATEGORY or "", co.CHAT_COMMAND)
284
+ self._commands.values(),
285
+ key=lambda co: (
286
+ (
287
+ co.CATEGORY
288
+ if isinstance(co.CATEGORY, str)
289
+ else (
290
+ co.CATEGORY.name
291
+ if isinstance(co.CATEGORY, CommandCategory)
292
+ else ""
293
+ )
294
+ ),
295
+ co.CHAT_COMMAND,
296
+ ),
284
297
  ):
285
298
  try:
286
299
  c.raise_if_not_authorized(mfrom)
@@ -26,8 +26,8 @@ if TYPE_CHECKING:
26
26
  class Search(Command):
27
27
  NAME = "🔎 Search for contacts"
28
28
  HELP = "Search for contacts via this gateway"
29
- NODE = "search"
30
29
  CHAT_COMMAND = "find"
30
+ NODE = CONTACTS.node + "/" + CHAT_COMMAND
31
31
  ACCESS = CommandAccess.USER_LOGGED
32
32
  CATEGORY = CONTACTS
33
33
 
@@ -64,7 +64,8 @@ class SyncContacts(Command):
64
64
  "Synchronize your XMPP roster with your legacy contacts. "
65
65
  "Slidge will only add/remove/modify contacts in its dedicated roster group"
66
66
  )
67
- NODE = CHAT_COMMAND = "sync-contacts"
67
+ CHAT_COMMAND = "sync-contacts"
68
+ NODE = CONTACTS.node + "/" + CHAT_COMMAND
68
69
  ACCESS = CommandAccess.USER_LOGGED
69
70
  CATEGORY = CONTACTS
70
71
 
@@ -123,7 +124,8 @@ class SyncContacts(Command):
123
124
 
124
125
  class ListContacts(Command):
125
126
  NAME = HELP = "👤 List your legacy contacts"
126
- NODE = CHAT_COMMAND = "contacts"
127
+ CHAT_COMMAND = "contacts"
128
+ NODE = CONTACTS.node + "/" + CHAT_COMMAND
127
129
  ACCESS = CommandAccess.USER_LOGGED
128
130
  CATEGORY = CONTACTS
129
131
 
@@ -143,7 +145,8 @@ class ListContacts(Command):
143
145
 
144
146
  class ListGroups(Command):
145
147
  NAME = HELP = "👥 List your legacy groups"
146
- NODE = CHAT_COMMAND = "groups"
148
+ CHAT_COMMAND = "groups"
149
+ NODE = GROUPS.node + "/" + CHAT_COMMAND
147
150
  ACCESS = CommandAccess.USER_LOGGED
148
151
  CATEGORY = GROUPS
149
152
 
@@ -162,7 +165,8 @@ class ListGroups(Command):
162
165
  class Login(Command):
163
166
  NAME = "🔐 Re-login to the legacy network"
164
167
  HELP = "Login to the legacy service"
165
- NODE = CHAT_COMMAND = "re-login"
168
+ CHAT_COMMAND = "re-login"
169
+ NODE = "https://slidge.im/command/core/" + CHAT_COMMAND
166
170
 
167
171
  ACCESS = CommandAccess.USER_NON_LOGGED
168
172
 
@@ -184,7 +188,8 @@ class Login(Command):
184
188
  class CreateGroup(Command):
185
189
  NAME = "🆕 New legacy group"
186
190
  HELP = "Create a group on the legacy service"
187
- NODE = CHAT_COMMAND = "create-group"
191
+ CHAT_COMMAND = "create-group"
192
+ NODE = GROUPS.node + "/" + CHAT_COMMAND
188
193
  CATEGORY = GROUPS
189
194
 
190
195
  ACCESS = CommandAccess.USER_LOGGED
@@ -233,7 +238,8 @@ class CreateGroup(Command):
233
238
  class Preferences(Command):
234
239
  NAME = "⚙️ Preferences"
235
240
  HELP = "Customize the gateway behaviour to your liking"
236
- NODE = CHAT_COMMAND = "preferences"
241
+ CHAT_COMMAND = "preferences"
242
+ NODE = "https://slidge.im/command/core/preferences"
237
243
  ACCESS = CommandAccess.USER
238
244
 
239
245
  async def run(
@@ -268,7 +274,8 @@ class Preferences(Command):
268
274
  class Unregister(Command):
269
275
  NAME = "❌ Unregister from the gateway"
270
276
  HELP = "Unregister from the gateway"
271
- NODE = CHAT_COMMAND = "unregister"
277
+ CHAT_COMMAND = "unregister"
278
+ NODE = "https://slidge.im/command/core/unregister"
272
279
  ACCESS = CommandAccess.USER
273
280
 
274
281
  async def run(
@@ -290,7 +297,8 @@ class Unregister(Command):
290
297
 
291
298
  class LeaveGroup(Command):
292
299
  NAME = HELP = "❌ Leave a legacy group"
293
- NODE = CHAT_COMMAND = "leave-group"
300
+ CHAT_COMMAND = "leave-group"
301
+ NODE = GROUPS.node + "/" + CHAT_COMMAND
294
302
  ACCESS = CommandAccess.USER_LOGGED
295
303
  CATEGORY = GROUPS
296
304
 
@@ -214,3 +214,9 @@ DEV_MODE__DOC = (
214
214
  "Enables an interactive python shell via chat commands, for admins."
215
215
  "Not safe to use in prod, but great during dev."
216
216
  )
217
+
218
+ STRIP_LEADING_EMOJI_ADHOC = False
219
+ STRIP_LEADING_EMOJI_ADHOC__DOC = (
220
+ "Strip the leading emoji in ad-hoc command names, if present, in case you "
221
+ "are a emoji-hater."
222
+ )
@@ -59,6 +59,8 @@ room_table = sa.Table(
59
59
 
60
60
 
61
61
  def upgrade() -> None:
62
+ if op.get_bind().engine.name == "postgresql":
63
+ return
62
64
  with op.batch_alter_table(
63
65
  "room",
64
66
  schema=None,
@@ -37,7 +37,7 @@ def upgrade() -> None:
37
37
  native_enum=False,
38
38
  ),
39
39
  nullable=False,
40
- server_default=sa.text("pc"),
40
+ server_default=sa.literal("pc"),
41
41
  )
42
42
  )
43
43
 
@@ -95,6 +95,12 @@ def upgrade() -> None:
95
95
  op.add_column("room", sa.Column("description", sa.String(), nullable=True))
96
96
  op.add_column("room", sa.Column("subject", sa.String(), nullable=True))
97
97
  op.add_column("room", sa.Column("subject_date", sa.DateTime(), nullable=True))
98
+
99
+ if op.get_bind().engine.name == "postgresql":
100
+ op.execute(
101
+ "CREATE TYPE muctype AS ENUM ('GROUP', 'CHANNEL', 'CHANNEL_NON_ANONYMOUS')"
102
+ )
103
+
98
104
  op.add_column(
99
105
  "room",
100
106
  sa.Column(
@@ -37,12 +37,13 @@ def upgrade() -> None:
37
37
 
38
38
  with op.batch_alter_table("room", schema=None) as batch_op:
39
39
  batch_op.add_column(sa.Column("avatar_legacy_id", sa.String(), nullable=True))
40
- batch_op.create_unique_constraint(
41
- "uq_room_user_account_id_jid", ["user_account_id", "jid"]
42
- )
43
- batch_op.create_unique_constraint(
44
- "uq_room_user_account_id_legacy_id", ["user_account_id", "legacy_id"]
45
- )
40
+ if op.get_bind().engine.name != "postgresql":
41
+ batch_op.create_unique_constraint(
42
+ "uq_room_user_account_id_jid", ["user_account_id", "jid"]
43
+ )
44
+ batch_op.create_unique_constraint(
45
+ "uq_room_user_account_id_legacy_id", ["user_account_id", "legacy_id"]
46
+ )
46
47
 
47
48
  for room_pk, avatar_legacy_id in room_avatars:
48
49
  conn.execute(
@@ -24,6 +24,10 @@ def upgrade() -> None:
24
24
  # since we don't want source to be nullable, we drop all rows first.
25
25
  # This is what you get by using alpha versions!
26
26
  op.execute(sa.delete(ArchivedMessage))
27
+
28
+ if op.get_bind().engine.name == "postgresql":
29
+ op.execute("CREATE TYPE archivedmessagesource AS ENUM ('LIVE', 'BACKFILL')")
30
+
27
31
  # ### commands auto generated by Alembic - please adjust! ###
28
32
 
29
33
  with op.batch_alter_table("mam", schema=None) as batch_op:
@@ -317,7 +317,7 @@ class LegacyIdsMulti(Base):
317
317
 
318
318
  legacy_id: Mapped[str] = mapped_column(nullable=False)
319
319
  xmpp_ids: Mapped[list["XmppIdsMulti"]] = relationship(
320
- back_populates="legacy_ids_multi"
320
+ back_populates="legacy_ids_multi", cascade="all, delete-orphan"
321
321
  )
322
322
 
323
323
 
@@ -9,6 +9,13 @@ from pathlib import Path
9
9
  from time import time
10
10
  from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, Type, TypeVar
11
11
 
12
+ try:
13
+ import emoji
14
+ except ImportError:
15
+ EMOJI_LIB_AVAILABLE = False
16
+ else:
17
+ EMOJI_LIB_AVAILABLE = True
18
+
12
19
  from .types import Mention, ResourceDict
13
20
 
14
21
  if TYPE_CHECKING:
@@ -318,3 +325,14 @@ def timeit(func):
318
325
  return r
319
326
 
320
327
  return wrapped
328
+
329
+
330
+ def strip_leading_emoji(text: str) -> str:
331
+ if not EMOJI_LIB_AVAILABLE:
332
+ return text
333
+ words = text.split(" ")
334
+ # is_emoji returns False for 🛷️ for obscure reasons,
335
+ # purely_emoji seems better
336
+ if len(words) > 1 and emoji.purely_emoji(words[0]):
337
+ return " ".join(words[1:])
338
+ return text
@@ -1,3 +0,0 @@
1
- ADMINISTRATION = "🛷️ Slidge administration"
2
- CONTACTS = "👤 Contacts"
3
- GROUPS = "👥 Groups"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1001,9 +1001,9 @@ class ParticipantStore(EngineMixin):
1001
1001
  def __init__(self, *a, **kw):
1002
1002
  super().__init__(*a, **kw)
1003
1003
  with self.session() as session:
1004
- session.execute(delete(Participant))
1005
- session.execute(delete(Hat))
1006
1004
  session.execute(delete(participant_hats))
1005
+ session.execute(delete(Hat))
1006
+ session.execute(delete(Participant))
1007
1007
  session.commit()
1008
1008
 
1009
1009
  def add(self, room_pk: int, nickname: str) -> int:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes