slidge 0.2.12__py3-none-any.whl → 0.3.0a0__py3-none-any.whl

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 (77) hide show
  1. slidge/__init__.py +5 -2
  2. slidge/command/adhoc.py +9 -3
  3. slidge/command/admin.py +16 -12
  4. slidge/command/base.py +16 -12
  5. slidge/command/chat_command.py +25 -16
  6. slidge/command/user.py +7 -8
  7. slidge/contact/contact.py +119 -209
  8. slidge/contact/roster.py +106 -105
  9. slidge/core/config.py +2 -43
  10. slidge/core/dispatcher/caps.py +9 -2
  11. slidge/core/dispatcher/disco.py +13 -3
  12. slidge/core/dispatcher/message/__init__.py +1 -1
  13. slidge/core/dispatcher/message/chat_state.py +17 -8
  14. slidge/core/dispatcher/message/marker.py +7 -5
  15. slidge/core/dispatcher/message/message.py +117 -92
  16. slidge/core/dispatcher/muc/__init__.py +1 -1
  17. slidge/core/dispatcher/muc/admin.py +4 -4
  18. slidge/core/dispatcher/muc/mam.py +10 -6
  19. slidge/core/dispatcher/muc/misc.py +4 -2
  20. slidge/core/dispatcher/muc/owner.py +5 -3
  21. slidge/core/dispatcher/muc/ping.py +3 -1
  22. slidge/core/dispatcher/presence.py +21 -15
  23. slidge/core/dispatcher/registration.py +20 -12
  24. slidge/core/dispatcher/search.py +7 -3
  25. slidge/core/dispatcher/session_dispatcher.py +13 -5
  26. slidge/core/dispatcher/util.py +37 -27
  27. slidge/core/dispatcher/vcard.py +7 -4
  28. slidge/core/gateway.py +168 -84
  29. slidge/core/mixins/__init__.py +1 -11
  30. slidge/core/mixins/attachment.py +163 -148
  31. slidge/core/mixins/avatar.py +100 -177
  32. slidge/core/mixins/db.py +50 -2
  33. slidge/core/mixins/message.py +19 -17
  34. slidge/core/mixins/message_maker.py +29 -15
  35. slidge/core/mixins/message_text.py +38 -30
  36. slidge/core/mixins/presence.py +91 -35
  37. slidge/core/pubsub.py +42 -47
  38. slidge/core/session.py +88 -57
  39. slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +183 -0
  40. slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +56 -0
  41. slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +50 -0
  42. slidge/db/alembic/versions/58b98dacf819_refactor.py +118 -0
  43. slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +74 -0
  44. slidge/db/avatar.py +150 -119
  45. slidge/db/meta.py +33 -22
  46. slidge/db/models.py +68 -117
  47. slidge/db/store.py +412 -1094
  48. slidge/group/archive.py +61 -54
  49. slidge/group/bookmarks.py +74 -55
  50. slidge/group/participant.py +135 -142
  51. slidge/group/room.py +315 -312
  52. slidge/main.py +28 -18
  53. slidge/migration.py +2 -12
  54. slidge/slixfix/__init__.py +20 -4
  55. slidge/slixfix/delivery_receipt.py +6 -4
  56. slidge/slixfix/link_preview/link_preview.py +1 -1
  57. slidge/slixfix/link_preview/stanza.py +1 -1
  58. slidge/slixfix/roster.py +5 -7
  59. slidge/slixfix/xep_0077/register.py +8 -8
  60. slidge/slixfix/xep_0077/stanza.py +7 -7
  61. slidge/slixfix/xep_0100/gateway.py +12 -13
  62. slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
  63. slidge/slixfix/xep_0292/vcard4.py +1 -1
  64. slidge/util/archive_msg.py +11 -5
  65. slidge/util/conf.py +23 -20
  66. slidge/util/jid_escaping.py +1 -1
  67. slidge/{core/mixins → util}/lock.py +6 -6
  68. slidge/util/test.py +30 -29
  69. slidge/util/types.py +22 -18
  70. slidge/util/util.py +19 -22
  71. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/METADATA +1 -1
  72. slidge-0.3.0a0.dist-info/RECORD +117 -0
  73. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/WHEEL +1 -1
  74. slidge-0.2.12.dist-info/RECORD +0 -112
  75. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/entry_points.txt +0 -0
  76. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
  77. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,9 @@ from ..util import DispatcherMixin, exceptions_to_xmpp_errors
16
16
 
17
17
 
18
18
  class MessageContentMixin(DispatcherMixin):
19
- def __init__(self, xmpp):
19
+ __slots__: list[str] = []
20
+
21
+ def __init__(self, xmpp) -> None:
20
22
  super().__init__(xmpp)
21
23
  xmpp.add_event_handler("legacy_message", self.on_legacy_message)
22
24
  xmpp.add_event_handler("message_correction", self.on_message_correction)
@@ -28,7 +30,7 @@ class MessageContentMixin(DispatcherMixin):
28
30
  await self.on_legacy_message(msg)
29
31
 
30
32
  @exceptions_to_xmpp_errors
31
- async def on_legacy_message(self, msg: Message):
33
+ async def on_legacy_message(self, msg: Message) -> None:
32
34
  """
33
35
  Meant to be called from :class:`BaseGateway` only.
34
36
 
@@ -65,16 +67,27 @@ class MessageContentMixin(DispatcherMixin):
65
67
  if src is not None and src.startswith("cid:"):
66
68
  cid = src.removeprefix("cid:")
67
69
 
68
- session, entity, thread = await self._get_session_entity_thread(msg)
70
+ session, recipient, thread = await self._get_session_recipient_thread(msg)
69
71
 
70
72
  if msg.get_plugin("oob", check=True) is not None:
71
73
  url = msg["oob"]["url"]
74
+ elif (
75
+ "reference" in msg
76
+ and "sims" in msg["reference"]
77
+ and "sources" in msg["reference"]["sims"]
78
+ ):
79
+ for source in msg["reference"]["sims"]["sources"]["substanzas"]:
80
+ if source["uri"].startswith("http"):
81
+ url = source["uri"]
82
+ break
83
+ else:
84
+ url = None
72
85
  else:
73
86
  url = None
74
87
 
75
88
  if msg.get_plugin("reply", check=True):
76
89
  text, reply_to_msg_id, reply_to, reply_fallback = await self.__get_reply(
77
- msg, session, entity
90
+ msg, session, recipient
78
91
  )
79
92
  else:
80
93
  text = msg["body"]
@@ -93,7 +106,7 @@ class MessageContentMixin(DispatcherMixin):
93
106
  legacy_msg_id = await self.__send_url(
94
107
  url,
95
108
  session,
96
- entity,
109
+ recipient,
97
110
  reply_to_msg_id=reply_to_msg_id,
98
111
  reply_to_fallback_text=reply_fallback,
99
112
  reply_to=reply_to,
@@ -104,19 +117,19 @@ class MessageContentMixin(DispatcherMixin):
104
117
  msg.get_from(),
105
118
  cid,
106
119
  session,
107
- entity,
120
+ recipient,
108
121
  reply_to_msg_id=reply_to_msg_id,
109
122
  reply_to_fallback_text=reply_fallback,
110
123
  reply_to=reply_to,
111
124
  thread=thread,
112
125
  )
113
126
  elif text:
114
- if isinstance(entity, LegacyMUC):
115
- mentions = {"mentions": await entity.parse_mentions(text)}
127
+ if isinstance(recipient, LegacyMUC):
128
+ mentions = {"mentions": await recipient.parse_mentions(text)}
116
129
  else:
117
130
  mentions = {}
118
131
  legacy_msg_id = await session.on_text(
119
- entity,
132
+ recipient,
120
133
  text,
121
134
  reply_to_msg_id=reply_to_msg_id,
122
135
  reply_to_fallback_text=reply_fallback,
@@ -129,43 +142,49 @@ class MessageContentMixin(DispatcherMixin):
129
142
  log.debug("Ignoring %s", msg.get_id())
130
143
  return
131
144
 
132
- if isinstance(entity, LegacyMUC):
133
- await entity.echo(msg, legacy_msg_id)
134
- if legacy_msg_id is not None:
135
- self.xmpp.store.sent.set_group_message(
136
- session.user_pk, str(legacy_msg_id), msg.get_id()
137
- )
145
+ if isinstance(recipient, LegacyMUC):
146
+ await recipient.echo(msg, legacy_msg_id)
138
147
  else:
139
148
  self.__ack(msg)
140
- if legacy_msg_id is not None:
141
- self.xmpp.store.sent.set_message(
142
- session.user_pk, str(legacy_msg_id), msg.get_id()
149
+
150
+ if legacy_msg_id is None:
151
+ return
152
+
153
+ with self.xmpp.store.session() as orm:
154
+ self.xmpp.store.id_map.set_msg(
155
+ orm,
156
+ recipient.stored.id,
157
+ str(legacy_msg_id),
158
+ [msg.get_id()],
159
+ recipient.is_group,
160
+ )
161
+ if session.MESSAGE_IDS_ARE_THREAD_IDS and (t := msg["thread"]):
162
+ self.xmpp.store.id_map.set_thread(
163
+ orm, recipient.stored.id, t, str(legacy_msg_id), recipient.is_group
143
164
  )
144
- if session.MESSAGE_IDS_ARE_THREAD_IDS and (t := msg["thread"]):
145
- self.xmpp.store.sent.set_thread(
146
- session.user_pk, t, str(legacy_msg_id)
147
- )
165
+ orm.commit()
148
166
 
149
167
  @exceptions_to_xmpp_errors
150
- async def on_message_correction(self, msg: Message):
168
+ async def on_message_correction(self, msg: Message) -> None:
151
169
  if msg.get_plugin("retract", check=True) is not None:
152
170
  # ignore message retraction fallback (fallback=last msg correction)
153
171
  return
154
- session, entity, thread = await self._get_session_entity_thread(msg)
172
+ session, recipient, thread = await self._get_session_recipient_thread(msg)
155
173
  xmpp_id = msg["replace"]["id"]
156
- if isinstance(entity, LegacyMUC):
157
- legacy_id_str = self.xmpp.store.sent.get_group_legacy_id(
158
- session.user_pk, xmpp_id
159
- )
174
+ if isinstance(recipient, LegacyMUC):
175
+ with self.xmpp.store.session() as orm:
176
+ legacy_id_str = self.xmpp.store.id_map.get_legacy(
177
+ orm, recipient.stored.id, xmpp_id, True
178
+ )
160
179
  if legacy_id_str is None:
161
- legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
180
+ legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id, recipient)
162
181
  else:
163
182
  legacy_id = self.xmpp.LEGACY_MSG_ID_TYPE(legacy_id_str)
164
183
  else:
165
- legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
184
+ legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id, recipient)
166
185
 
167
- if isinstance(entity, LegacyMUC):
168
- mentions = await entity.parse_mentions(msg["body"])
186
+ if isinstance(recipient, LegacyMUC):
187
+ mentions = await recipient.parse_mentions(msg["body"])
169
188
  else:
170
189
  mentions = None
171
190
 
@@ -177,22 +196,18 @@ class MessageContentMixin(DispatcherMixin):
177
196
  if legacy_id is None:
178
197
  log.debug("Did not find legacy ID to correct")
179
198
  new_legacy_msg_id = await session.on_text(
180
- entity,
199
+ recipient,
181
200
  "Correction:" + msg["body"],
182
201
  thread=thread,
183
202
  mentions=mentions,
184
203
  link_previews=link_previews,
185
204
  )
186
- elif (
187
- not msg["body"].strip()
188
- and config.CORRECTION_EMPTY_BODY_AS_RETRACTION
189
- and entity.RETRACTION
190
- ):
191
- await session.on_retract(entity, legacy_id, thread=thread)
205
+ elif not msg["body"].strip() and recipient.RETRACTION:
206
+ await session.on_retract(recipient, legacy_id, thread=thread)
192
207
  new_legacy_msg_id = None
193
- elif entity.CORRECTION:
208
+ elif recipient.CORRECTION:
194
209
  new_legacy_msg_id = await session.on_correct(
195
- entity,
210
+ recipient,
196
211
  msg["body"],
197
212
  legacy_id,
198
213
  thread=thread,
@@ -204,60 +219,59 @@ class MessageContentMixin(DispatcherMixin):
204
219
  "Last message correction is not supported by this legacy service. "
205
220
  "Slidge will send your correction as new message."
206
221
  )
207
- if (
208
- config.LAST_MESSAGE_CORRECTION_RETRACTION_WORKAROUND
209
- and entity.RETRACTION
210
- and legacy_id is not None
211
- ):
222
+ if recipient.RETRACTION and legacy_id is not None:
212
223
  if legacy_id is not None:
213
224
  session.send_gateway_message(
214
225
  "Slidge will attempt to retract the original message you wanted"
215
226
  " to edit."
216
227
  )
217
- await session.on_retract(entity, legacy_id, thread=thread)
228
+ await session.on_retract(recipient, legacy_id, thread=thread)
218
229
 
219
230
  new_legacy_msg_id = await session.on_text(
220
- entity,
231
+ recipient,
221
232
  "Correction: " + msg["body"],
222
233
  thread=thread,
223
234
  mentions=mentions,
224
235
  link_previews=link_previews,
225
236
  )
226
237
 
227
- if isinstance(entity, LegacyMUC):
228
- if new_legacy_msg_id is not None:
229
- self.xmpp.store.sent.set_group_message(
230
- session.user_pk, new_legacy_msg_id, msg.get_id()
231
- )
232
- await entity.echo(msg, new_legacy_msg_id)
238
+ if isinstance(recipient, LegacyMUC):
239
+ await recipient.echo(msg, new_legacy_msg_id)
233
240
  else:
234
241
  self.__ack(msg)
235
- if new_legacy_msg_id is not None:
236
- self.xmpp.store.sent.set_message(
237
- session.user_pk, new_legacy_msg_id, msg.get_id()
238
- )
242
+ if new_legacy_msg_id is None:
243
+ return
244
+ with self.xmpp.store.session() as orm:
245
+ self.xmpp.store.id_map.set_msg(
246
+ orm,
247
+ recipient.stored.id,
248
+ new_legacy_msg_id,
249
+ msg.get_id(),
250
+ recipient.is_group,
251
+ )
252
+ orm.commit()
239
253
 
240
254
  @exceptions_to_xmpp_errors
241
255
  async def on_message_retract(self, msg: Message):
242
- session, entity, thread = await self._get_session_entity_thread(msg)
243
- if not entity.RETRACTION:
256
+ session, recipient, thread = await self._get_session_recipient_thread(msg)
257
+ if not recipient.RETRACTION:
244
258
  raise XMPPError(
245
259
  "bad-request",
246
260
  "This legacy service does not support message retraction.",
247
261
  )
248
262
  xmpp_id: str = msg["retract"]["id"]
249
- legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
263
+ legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id, recipient)
250
264
  if legacy_id:
251
- await session.on_retract(entity, legacy_id, thread=thread)
252
- if isinstance(entity, LegacyMUC):
253
- await entity.echo(msg, None)
265
+ await session.on_retract(recipient, legacy_id, thread=thread)
266
+ if isinstance(recipient, LegacyMUC):
267
+ await recipient.echo(msg, None)
254
268
  else:
255
269
  log.debug("Ignored retraction from user")
256
270
  self.__ack(msg)
257
271
 
258
272
  @exceptions_to_xmpp_errors
259
273
  async def on_reactions(self, msg: Message):
260
- session, entity, thread = await self._get_session_entity_thread(msg)
274
+ session, recipient, thread = await self._get_session_recipient_thread(msg)
261
275
  react_to: str = msg["reactions"]["id"]
262
276
 
263
277
  special_msg = session.SPECIAL_MSG_ID_PREFIX and react_to.startswith(
@@ -267,7 +281,7 @@ class MessageContentMixin(DispatcherMixin):
267
281
  if special_msg:
268
282
  legacy_id = react_to
269
283
  else:
270
- legacy_id = self._xmpp_msg_id_to_legacy(session, react_to)
284
+ legacy_id = self._xmpp_msg_id_to_legacy(session, react_to, recipient)
271
285
 
272
286
  if not legacy_id:
273
287
  log.debug("Ignored reaction from user")
@@ -280,58 +294,65 @@ class MessageContentMixin(DispatcherMixin):
280
294
  remove_emoji_variation_selector_16(r["value"]) for r in msg["reactions"]
281
295
  ]
282
296
  error_msg = None
283
- entity = entity
297
+ recipient = recipient
284
298
 
285
299
  if not special_msg:
286
- if entity.REACTIONS_SINGLE_EMOJI and len(emojis) > 1:
300
+ if recipient.REACTIONS_SINGLE_EMOJI and len(emojis) > 1:
287
301
  error_msg = "Maximum 1 emoji/message"
288
302
 
289
- if not error_msg and (subset := await entity.available_emojis(legacy_id)):
303
+ if not error_msg and (
304
+ subset := await recipient.available_emojis(legacy_id)
305
+ ):
290
306
  if not set(emojis).issubset(subset):
291
307
  error_msg = f"You can only react with the following emojis: {''.join(subset)}"
292
308
 
293
309
  if error_msg:
294
310
  session.send_gateway_message(error_msg)
295
- if not isinstance(entity, LegacyMUC):
311
+ if not isinstance(recipient, LegacyMUC):
296
312
  # no need to carbon for groups, we just don't echo the stanza
297
- entity.react(legacy_id, carbon=True) # type: ignore
298
- await session.on_react(entity, legacy_id, [], thread=thread)
313
+ recipient.react(legacy_id, carbon=True)
314
+ await session.on_react(recipient, legacy_id, [], thread=thread)
299
315
  raise XMPPError(
300
- "policy-violation", # type:ignore
316
+ "policy-violation",
301
317
  text=error_msg,
302
318
  )
303
319
 
304
- await session.on_react(entity, legacy_id, emojis, thread=thread)
305
- if isinstance(entity, LegacyMUC):
306
- await entity.echo(msg, None)
320
+ await session.on_react(recipient, legacy_id, emojis, thread=thread)
321
+ if isinstance(recipient, LegacyMUC):
322
+ await recipient.echo(msg, None)
307
323
  else:
308
324
  self.__ack(msg)
309
325
 
310
- multi = self.xmpp.store.multi.get_xmpp_ids(session.user_pk, react_to)
326
+ with self.xmpp.store.session() as orm:
327
+ multi = self.xmpp.store.id_map.get_xmpp(
328
+ orm, recipient.stored.id, legacy_id, recipient.is_group
329
+ )
311
330
  if not multi:
312
331
  return
313
332
  multi = [m for m in multi if react_to != m]
314
333
 
315
- if isinstance(entity, LegacyMUC):
334
+ if isinstance(recipient, LegacyMUC):
316
335
  for xmpp_id in multi:
317
336
  mc = copy(msg)
318
337
  mc["reactions"]["id"] = xmpp_id
319
- await entity.echo(mc)
320
- elif isinstance(entity, LegacyContact):
338
+ await recipient.echo(mc)
339
+ elif isinstance(recipient, LegacyContact):
321
340
  for xmpp_id in multi:
322
- entity.react(legacy_id, emojis, xmpp_id=xmpp_id, carbon=True)
341
+ recipient.react(legacy_id, emojis, xmpp_id=xmpp_id, carbon=True)
323
342
 
324
- def __ack(self, msg: Message):
343
+ def __ack(self, msg: Message) -> None:
325
344
  if not self.xmpp.PROPER_RECEIPTS:
326
345
  self.xmpp.delivery_receipt.ack(msg)
327
346
 
328
347
  async def __get_reply(
329
- self, msg: Message, session: BaseSession, entity: Recipient
348
+ self, msg: Message, session: BaseSession, recipient: Recipient
330
349
  ) -> tuple[
331
350
  str, str | int | None, LegacyContact | LegacyParticipant | None, str | None
332
351
  ]:
333
352
  try:
334
- reply_to_msg_id = self._xmpp_msg_id_to_legacy(session, msg["reply"]["id"])
353
+ reply_to_msg_id = self._xmpp_msg_id_to_legacy(
354
+ session, msg["reply"]["id"], recipient
355
+ )
335
356
  except XMPPError:
336
357
  session.log.debug(
337
358
  "Could not determine reply-to legacy msg ID, sending quote instead."
@@ -359,7 +380,7 @@ class MessageContentMixin(DispatcherMixin):
359
380
  )
360
381
 
361
382
  if msg.get_plugin("fallback", check=True) and (
362
- isinstance(entity, LegacyMUC) or entity.REPLIES
383
+ isinstance(recipient, LegacyMUC) or recipient.REPLIES
363
384
  ):
364
385
  text = msg["fallback"].get_stripped_body(self.xmpp["xep_0461"].namespace)
365
386
  try:
@@ -373,7 +394,7 @@ class MessageContentMixin(DispatcherMixin):
373
394
  return text, reply_to_msg_id, reply_to, reply_fallback
374
395
 
375
396
  async def __send_url(
376
- self, url: str, session: BaseSession, entity: Recipient, **kwargs
397
+ self, url: str, session: BaseSession, recipient: Recipient, **kwargs
377
398
  ) -> int | str | None:
378
399
  async with self.xmpp.http.get(url) as response:
379
400
  if response.status >= 400:
@@ -382,19 +403,23 @@ class MessageContentMixin(DispatcherMixin):
382
403
  " instead.",
383
404
  response,
384
405
  )
385
- return await session.on_text(entity, url, **kwargs)
406
+ return await session.on_text(recipient, url, **kwargs)
386
407
 
387
- return await session.on_file(entity, url, http_response=response, **kwargs)
408
+ return await session.on_file(
409
+ recipient, url, http_response=response, **kwargs
410
+ )
388
411
 
389
412
  async def __send_bob(
390
- self, from_: JID, cid: str, session: BaseSession, entity: Recipient, **kwargs
413
+ self, from_: JID, cid: str, session: BaseSession, recipient: Recipient, **kwargs
391
414
  ) -> int | str | None:
392
- sticker = self.xmpp.store.bob.get_sticker(cid)
415
+ with self.xmpp.store.session() as orm:
416
+ sticker = self.xmpp.store.bob.get_sticker(orm, cid)
393
417
  if sticker is None:
394
418
  await self.xmpp.plugin["xep_0231"].get_bob(from_, cid)
395
- sticker = self.xmpp.store.bob.get_sticker(cid)
419
+ with self.xmpp.store.session() as orm:
420
+ sticker = self.xmpp.store.bob.get_sticker(orm, cid)
396
421
  assert sticker is not None
397
- return await session.on_sticker(entity, sticker, **kwargs)
422
+ return await session.on_sticker(recipient, sticker, **kwargs)
398
423
 
399
424
 
400
425
  log = logging.getLogger(__name__)
@@ -6,7 +6,7 @@ from .ping import PingMixin
6
6
 
7
7
 
8
8
  class MucMixin(PingMixin, MamMixin, MucAdminMixin, MucOwnerMixin, MucMiscMixin):
9
- pass
9
+ __slots__: list[str] = []
10
10
 
11
11
 
12
12
  __all__ = ("MucMixin",)
@@ -6,6 +6,8 @@ from ..util import DispatcherMixin, exceptions_to_xmpp_errors
6
6
 
7
7
 
8
8
  class MucAdminMixin(DispatcherMixin):
9
+ __slots__: list[str] = []
10
+
9
11
  def __init__(self, xmpp) -> None:
10
12
  super().__init__(xmpp)
11
13
  self.xmpp.register_handler(
@@ -46,7 +48,7 @@ class MucAdminMixin(DispatcherMixin):
46
48
  "Slidge only implements moderation/retraction",
47
49
  )
48
50
 
49
- legacy_id = self._xmpp_msg_id_to_legacy(muc.session, xmpp_id)
51
+ legacy_id = self._xmpp_msg_id_to_legacy(muc.session, xmpp_id, muc)
50
52
  await muc.session.on_moderate(muc, legacy_id, moderate["reason"] or None)
51
53
  iq.reply(clear=True).send()
52
54
 
@@ -90,8 +92,6 @@ class MucAdminMixin(DispatcherMixin):
90
92
 
91
93
  reply = iq.reply()
92
94
  reply.enable("mucadmin_query")
93
- async for participant in muc.get_participants():
94
- if not participant.affiliation == affiliation:
95
- continue
95
+ async for participant in muc.get_participants(affiliation):
96
96
  reply["mucadmin_query"].append(participant.mucadmin_item())
97
97
  reply.send()
@@ -13,9 +13,11 @@ if TYPE_CHECKING:
13
13
 
14
14
 
15
15
  class MamMixin(DispatcherMixin):
16
- def __init__(self, xmpp: "BaseGateway"):
16
+ __slots__: list[str] = []
17
+
18
+ def __init__(self, xmpp: "BaseGateway") -> None:
17
19
  super().__init__(xmpp)
18
- self.__mam_cleanup_task = xmpp.loop.create_task(self.__mam_cleanup())
20
+ self.__mam_cleanup_task = xmpp.loop.create_task(self.__mam_cleanup()) # type:ignore[misc]
19
21
  xmpp.register_handler(
20
22
  CoroutineCallback(
21
23
  "MAM_query",
@@ -38,15 +40,17 @@ class MamMixin(DispatcherMixin):
38
40
  )
39
41
  )
40
42
 
41
- async def __mam_cleanup(self):
43
+ async def __mam_cleanup(self) -> None:
42
44
  if not config.MAM_MAX_DAYS:
43
45
  return
44
46
  while True:
45
47
  await asyncio.sleep(3600 * 6)
46
- self.xmpp.store.mam.nuke_older_than(config.MAM_MAX_DAYS)
48
+ with self.xmpp.store.session() as orm:
49
+ self.xmpp.store.mam.nuke_older_than(orm, config.MAM_MAX_DAYS)
50
+ orm.commit()
47
51
 
48
52
  @exceptions_to_xmpp_errors
49
- async def __handle_mam(self, iq: Iq):
53
+ async def __handle_mam(self, iq: Iq) -> None:
50
54
  muc = await self.get_muc_from_stanza(iq)
51
55
  await muc.send_mam(iq)
52
56
 
@@ -78,6 +82,6 @@ class MamMixin(DispatcherMixin):
78
82
  reply.send()
79
83
 
80
84
  @exceptions_to_xmpp_errors
81
- async def __handle_mam_metadata(self, iq: Iq):
85
+ async def __handle_mam_metadata(self, iq: Iq) -> None:
82
86
  muc = await self.get_muc_from_stanza(iq)
83
87
  await muc.send_mam_metadata(iq)
@@ -7,7 +7,9 @@ from ..util import DispatcherMixin, exceptions_to_xmpp_errors
7
7
 
8
8
 
9
9
  class MucMiscMixin(DispatcherMixin):
10
- def __init__(self, xmpp):
10
+ __slots__: list[str] = []
11
+
12
+ def __init__(self, xmpp) -> None:
11
13
  super().__init__(xmpp)
12
14
  xmpp.register_handler(
13
15
  CoroutineCallback(
@@ -22,7 +24,7 @@ class MucMiscMixin(DispatcherMixin):
22
24
  xmpp.add_event_handler("groupchat_subject", self.on_groupchat_subject)
23
25
  xmpp.add_event_handler("groupchat_message_error", self.__on_group_chat_error)
24
26
 
25
- async def __on_group_chat_error(self, msg: Message):
27
+ async def __on_group_chat_error(self, msg: Message) -> None:
26
28
  condition = msg["error"].get_condition()
27
29
  if condition not in KICKABLE_ERRORS:
28
30
  return
@@ -7,7 +7,9 @@ from ..util import DispatcherMixin, exceptions_to_xmpp_errors
7
7
 
8
8
 
9
9
  class MucOwnerMixin(DispatcherMixin):
10
- def __init__(self, xmpp):
10
+ __slots__: list[str] = []
11
+
12
+ def __init__(self, xmpp) -> None:
11
13
  super().__init__(xmpp)
12
14
  xmpp.register_handler(
13
15
  CoroutineCallback(
@@ -81,8 +83,8 @@ class MucOwnerMixin(DispatcherMixin):
81
83
  reason = destroy["reason"] or None
82
84
  await muc.on_destroy_request(reason)
83
85
  user_participant = await muc.get_user_participant()
84
- user_participant._affiliation = "none"
85
- user_participant._role = "none"
86
+ user_participant.stored.affiliation = "none"
87
+ user_participant.stored.role = "none"
86
88
  presence = user_participant._make_presence(ptype="unavailable", force=True)
87
89
  presence["muc"].enable("destroy")
88
90
  if reason is not None:
@@ -11,7 +11,9 @@ if TYPE_CHECKING:
11
11
 
12
12
 
13
13
  class PingMixin(DispatcherMixin):
14
- def __init__(self, xmpp: "BaseGateway"):
14
+ __slots__: list[str] = []
15
+
16
+ def __init__(self, xmpp: "BaseGateway") -> None:
15
17
  super().__init__(xmpp)
16
18
 
17
19
  xmpp.remove_handler("Ping")
@@ -9,12 +9,14 @@ from .util import DispatcherMixin, exceptions_to_xmpp_errors
9
9
 
10
10
 
11
11
  class _IsDirectedAtComponent(Exception):
12
- def __init__(self, session: BaseSession):
12
+ def __init__(self, session: BaseSession) -> None:
13
13
  self.session = session
14
14
 
15
15
 
16
16
  class PresenceHandlerMixin(DispatcherMixin):
17
- def __init__(self, xmpp):
17
+ __slots__: list[str] = []
18
+
19
+ def __init__(self, xmpp) -> None:
18
20
  super().__init__(xmpp)
19
21
 
20
22
  xmpp.add_event_handler("presence_subscribe", self._handle_subscribe)
@@ -33,7 +35,7 @@ class PresenceHandlerMixin(DispatcherMixin):
33
35
  return await sess.contacts.by_jid(pto)
34
36
 
35
37
  @exceptions_to_xmpp_errors
36
- async def _handle_subscribe(self, pres: Presence):
38
+ async def _handle_subscribe(self, pres: Presence) -> None:
37
39
  try:
38
40
  contact = await self.__get_contact(pres)
39
41
  except _IsDirectedAtComponent:
@@ -46,7 +48,7 @@ class PresenceHandlerMixin(DispatcherMixin):
46
48
  await contact.on_friend_request(pres["status"])
47
49
 
48
50
  @exceptions_to_xmpp_errors
49
- async def _handle_unsubscribe(self, pres: Presence):
51
+ async def _handle_unsubscribe(self, pres: Presence) -> None:
50
52
  pres.reply().send()
51
53
 
52
54
  try:
@@ -60,7 +62,7 @@ class PresenceHandlerMixin(DispatcherMixin):
60
62
  await contact.on_friend_delete(pres["status"])
61
63
 
62
64
  @exceptions_to_xmpp_errors
63
- async def _handle_subscribed(self, pres: Presence):
65
+ async def _handle_subscribed(self, pres: Presence) -> None:
64
66
  try:
65
67
  contact = await self.__get_contact(pres)
66
68
  except _IsDirectedAtComponent:
@@ -69,7 +71,7 @@ class PresenceHandlerMixin(DispatcherMixin):
69
71
  await contact.on_friend_accept()
70
72
 
71
73
  @exceptions_to_xmpp_errors
72
- async def _handle_unsubscribed(self, pres: Presence):
74
+ async def _handle_unsubscribed(self, pres: Presence) -> None:
73
75
  try:
74
76
  contact = await self.__get_contact(pres)
75
77
  except _IsDirectedAtComponent:
@@ -80,7 +82,7 @@ class PresenceHandlerMixin(DispatcherMixin):
80
82
  await contact.on_friend_delete(pres["status"])
81
83
 
82
84
  @exceptions_to_xmpp_errors
83
- async def _handle_probe(self, pres: Presence):
85
+ async def _handle_probe(self, pres: Presence) -> None:
84
86
  try:
85
87
  contact = await self.__get_contact(pres)
86
88
  except _IsDirectedAtComponent:
@@ -120,16 +122,20 @@ class PresenceHandlerMixin(DispatcherMixin):
120
122
  resources = self.xmpp.roster[self.xmpp.boundjid.bare][
121
123
  p.get_from()
122
124
  ].resources
123
- await session.on_presence(
124
- p.get_from().resource,
125
- ptype, # type: ignore
126
- p["status"],
127
- resources,
128
- merge_resources(resources),
129
- )
125
+ try:
126
+ await session.on_presence(
127
+ p.get_from().resource,
128
+ ptype, # type: ignore
129
+ p["status"],
130
+ resources,
131
+ merge_resources(resources),
132
+ )
133
+ except NotImplementedError:
134
+ pass
130
135
  if p.get_type() == "available":
131
136
  await self.xmpp.pubsub.on_presence_available(p, None)
132
- return
137
+ for contact in session.contacts:
138
+ await self.xmpp.pubsub.on_presence_available(p, contact)
133
139
 
134
140
  if p.get_type() == "available":
135
141
  try: