slidge 0.1.2__py3-none-any.whl → 0.2.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 (63) hide show
  1. slidge/__init__.py +3 -5
  2. slidge/__main__.py +2 -196
  3. slidge/__version__.py +5 -0
  4. slidge/command/adhoc.py +8 -1
  5. slidge/command/admin.py +5 -6
  6. slidge/command/base.py +1 -2
  7. slidge/command/register.py +32 -16
  8. slidge/command/user.py +85 -5
  9. slidge/contact/contact.py +93 -31
  10. slidge/contact/roster.py +54 -39
  11. slidge/core/config.py +13 -7
  12. slidge/core/gateway/base.py +139 -34
  13. slidge/core/gateway/disco.py +2 -4
  14. slidge/core/gateway/mam.py +1 -4
  15. slidge/core/gateway/ping.py +2 -3
  16. slidge/core/gateway/presence.py +1 -1
  17. slidge/core/gateway/registration.py +32 -21
  18. slidge/core/gateway/search.py +3 -5
  19. slidge/core/gateway/session_dispatcher.py +109 -51
  20. slidge/core/gateway/vcard_temp.py +6 -4
  21. slidge/core/mixins/__init__.py +11 -1
  22. slidge/core/mixins/attachment.py +15 -10
  23. slidge/core/mixins/avatar.py +66 -18
  24. slidge/core/mixins/base.py +8 -2
  25. slidge/core/mixins/message.py +11 -7
  26. slidge/core/mixins/message_maker.py +17 -9
  27. slidge/core/mixins/presence.py +14 -4
  28. slidge/core/pubsub.py +54 -212
  29. slidge/core/session.py +65 -33
  30. slidge/db/__init__.py +4 -0
  31. slidge/db/alembic/env.py +64 -0
  32. slidge/db/alembic/script.py.mako +26 -0
  33. slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
  34. slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
  35. slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
  36. slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +76 -0
  37. slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
  38. slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
  39. slidge/db/avatar.py +224 -0
  40. slidge/db/meta.py +65 -0
  41. slidge/db/models.py +365 -0
  42. slidge/db/store.py +976 -0
  43. slidge/group/archive.py +13 -14
  44. slidge/group/bookmarks.py +59 -56
  45. slidge/group/participant.py +81 -29
  46. slidge/group/room.py +242 -142
  47. slidge/main.py +201 -0
  48. slidge/migration.py +30 -0
  49. slidge/slixfix/__init__.py +35 -2
  50. slidge/slixfix/roster.py +11 -4
  51. slidge/slixfix/xep_0292/vcard4.py +1 -0
  52. slidge/util/db.py +1 -47
  53. slidge/util/test.py +21 -4
  54. slidge/util/types.py +24 -4
  55. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
  56. slidge-0.2.0a0.dist-info/RECORD +108 -0
  57. slidge/core/cache.py +0 -183
  58. slidge/util/schema.sql +0 -126
  59. slidge/util/sql.py +0 -508
  60. slidge-0.1.2.dist-info/RECORD +0 -96
  61. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
  62. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
  63. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/entry_points.txt +0 -0
slidge/util/sql.py DELETED
@@ -1,508 +0,0 @@
1
- import logging
2
- import os
3
- import sqlite3
4
- import tempfile
5
- from asyncio import AbstractEventLoop, Task, sleep
6
- from datetime import datetime, timezone
7
- from functools import lru_cache
8
- from pathlib import Path
9
- from time import time
10
- from typing import (
11
- TYPE_CHECKING,
12
- Collection,
13
- Generic,
14
- Iterator,
15
- NamedTuple,
16
- Optional,
17
- TypeVar,
18
- Union,
19
- )
20
-
21
- from slixmpp import JID
22
- from slixmpp.exceptions import XMPPError
23
- from slixmpp.types import PresenceShows, PresenceTypes
24
-
25
- from ..core import config
26
- from .archive_msg import HistoryMessage
27
-
28
- if TYPE_CHECKING:
29
- from .db import GatewayUser
30
-
31
- KeyType = TypeVar("KeyType")
32
- ValueType = TypeVar("ValueType")
33
-
34
-
35
- class CachedPresence(NamedTuple):
36
- last_seen: Optional[datetime] = None
37
- ptype: Optional[PresenceTypes] = None
38
- pstatus: Optional[str] = None
39
- pshow: Optional[PresenceShows] = None
40
-
41
-
42
- class MamMetadata(NamedTuple):
43
- id: str
44
- sent_on: datetime
45
-
46
-
47
- class Base:
48
- def __init__(self):
49
- handler, filename = tempfile.mkstemp()
50
-
51
- os.close(handler)
52
- self.__filename = filename
53
-
54
- self.con = sqlite3.connect(filename)
55
- self.cur = self.con.cursor()
56
- self.cur.executescript((Path(__file__).parent / "schema.sql").read_text())
57
-
58
- self.__mam_cleanup_task: Optional[Task] = None
59
-
60
- def __del__(self):
61
- self.con.close()
62
- os.unlink(self.__filename)
63
-
64
-
65
- class MAMMixin(Base):
66
- def __init__(self):
67
- super().__init__()
68
- self.__mam_cleanup_task: Optional[Task] = None
69
- self.__msg_cur = msg_cur = self.con.cursor()
70
- msg_cur.row_factory = self.__msg_factory # type:ignore
71
- self.__metadata_cur = metadata_cur = self.con.cursor()
72
- metadata_cur.row_factory = self.__metadata_factory # type:ignore
73
-
74
- @staticmethod
75
- def __msg_factory(_cur, row: tuple[str, float]) -> HistoryMessage:
76
- return HistoryMessage(
77
- row[0], when=datetime.fromtimestamp(row[1], tz=timezone.utc)
78
- )
79
-
80
- @staticmethod
81
- def __metadata_factory(_cur, row: tuple[str, float]) -> MamMetadata:
82
- return MamMetadata(row[0], datetime.fromtimestamp(row[1], tz=timezone.utc))
83
-
84
- def mam_nuke(self):
85
- self.cur.execute("DELETE FROM mam_message")
86
- self.con.commit()
87
-
88
- def mam_add_muc(self, jid: str, user: "GatewayUser"):
89
- try:
90
- self.cur.execute(
91
- "INSERT INTO "
92
- "muc(jid, user_id) "
93
- "VALUES("
94
- " ?, "
95
- " (SELECT id FROM user WHERE jid = ?)"
96
- ")",
97
- (jid, user.bare_jid),
98
- )
99
- except sqlite3.IntegrityError:
100
- log.debug("Tried to add a MUC that was already here: (%s, %s)", user, jid)
101
- else:
102
- self.con.commit()
103
-
104
- def mam_add_msg(self, muc_jid: str, msg: "HistoryMessage", user: "GatewayUser"):
105
- self.cur.execute(
106
- "REPLACE INTO "
107
- "mam_message(message_id, sender_jid, sent_on, xml, muc_id, user_id)"
108
- "VALUES(?, ?, ?, ?,"
109
- "(SELECT id FROM muc WHERE jid = ?),"
110
- "(SELECT id FROM user WHERE jid = ?)"
111
- ")",
112
- (
113
- msg.id,
114
- str(msg.stanza.get_from()),
115
- msg.when.timestamp(),
116
- str(msg.stanza),
117
- muc_jid,
118
- user.bare_jid,
119
- ),
120
- )
121
- self.con.commit()
122
-
123
- def mam_launch_cleanup_task(self, loop: AbstractEventLoop):
124
- self.__mam_cleanup_task = loop.create_task(self.__mam_cleanup())
125
-
126
- async def __mam_cleanup(self):
127
- await sleep(6 * 3600)
128
- self.mam_cleanup()
129
-
130
- def mam_cleanup(self):
131
- self.cur.execute(
132
- "DELETE FROM mam_message WHERE sent_on < ?",
133
- (time() - config.MAM_MAX_DAYS * 24 * 3600,),
134
- )
135
- self.con.commit()
136
-
137
- def __mam_get_sent_on(self, muc_jid: str, mid: str, user: "GatewayUser"):
138
- res = self.cur.execute(
139
- "SELECT sent_on "
140
- "FROM mam_message "
141
- "WHERE message_id = ? "
142
- "AND muc_id = (SELECT id FROM muc WHERE jid = ?) "
143
- "AND user_id = (SELECT id FROM user WHERE jid = ?)",
144
- (mid, muc_jid, user.bare_jid),
145
- )
146
- row = res.fetchone()
147
- if row is None:
148
- raise XMPPError("item-not-found", f"Message {mid} not found")
149
- return row[0]
150
-
151
- def __mam_bound(
152
- self,
153
- muc_jid: str,
154
- user: "GatewayUser",
155
- date: Optional[datetime] = None,
156
- id_: Optional[str] = None,
157
- comparator=min,
158
- ):
159
- if id_ is not None:
160
- after_id_sent_on = self.__mam_get_sent_on(muc_jid, id_, user)
161
- if date:
162
- timestamp = comparator(after_id_sent_on, date.timestamp())
163
- else:
164
- timestamp = after_id_sent_on
165
- return " AND sent_on > ?", timestamp
166
- elif date is None:
167
- raise TypeError
168
- else:
169
- return " AND sent_on >= ?", date.timestamp()
170
-
171
- def mam_get_messages(
172
- self,
173
- user: "GatewayUser",
174
- muc_jid: str,
175
- start_date: Optional[datetime] = None,
176
- end_date: Optional[datetime] = None,
177
- before_id: Optional[str] = None,
178
- after_id: Optional[str] = None,
179
- ids: Collection[str] = (),
180
- last_page_n: Optional[int] = None,
181
- sender: Optional[str] = None,
182
- flip=False,
183
- ) -> Iterator[HistoryMessage]:
184
- query = (
185
- "SELECT xml, sent_on FROM mam_message "
186
- "WHERE muc_id = (SELECT id FROM muc WHERE jid = ?) "
187
- "AND user_id = (SELECT id FROM user WHERE jid = ?) "
188
- )
189
- params: list[Union[str, float, int]] = [muc_jid, user.bare_jid]
190
-
191
- if start_date or after_id:
192
- subquery, timestamp = self.__mam_bound(
193
- muc_jid, user, start_date, after_id, max
194
- )
195
- query += subquery
196
- params.append(timestamp)
197
- if end_date or before_id:
198
- subquery, timestamp = self.__mam_bound(
199
- muc_jid, user, end_date, before_id, min
200
- )
201
- query += subquery
202
- params.append(timestamp)
203
- if sender:
204
- query += " AND sender_jid = ?"
205
- params.append(sender)
206
- if ids:
207
- query += f" AND message_id IN ({','.join('?' * len(ids))})"
208
- params.extend(ids)
209
- if last_page_n:
210
- # TODO: optimize query further when <flip> and last page are
211
- # combined.
212
- query = f"SELECT * FROM ({query} ORDER BY sent_on DESC LIMIT ?)"
213
- params.append(last_page_n)
214
- query += " ORDER BY sent_on"
215
- if flip:
216
- query += " DESC"
217
-
218
- res = self.__msg_cur.execute(query, params)
219
-
220
- if ids:
221
- rows = res.fetchall()
222
- if len(rows) != len(ids):
223
- raise XMPPError(
224
- "item-not-found",
225
- "One of the requested messages IDs could not be found "
226
- "with the given constraints.",
227
- )
228
- for row in rows:
229
- yield row
230
-
231
- while row := res.fetchone():
232
- yield row
233
-
234
- def mam_get_first_and_last(self, muc_jid: str) -> list[MamMetadata]:
235
- res = self.__metadata_cur.execute(
236
- "SELECT message_id, sent_on "
237
- "FROM mam_message "
238
- "JOIN muc ON muc.jid = ? "
239
- "WHERE sent_on = (SELECT MIN(sent_on) FROM mam_message WHERE muc_id = muc.id) "
240
- " OR sent_on = (SELECT MAX(sent_on) FROM mam_message WHERE muc_id = muc.id) "
241
- " ORDER BY sent_on",
242
- (muc_jid,),
243
- )
244
- return res.fetchall()
245
-
246
-
247
- class AttachmentMixin(Base):
248
- def attachment_remove(self, legacy_id):
249
- self.cur.execute("DELETE FROM attachment WHERE legacy_id = ?", (legacy_id,))
250
- self.con.commit()
251
-
252
- def attachment_store_url(self, legacy_id, url: str):
253
- self.cur.execute(
254
- "REPLACE INTO attachment(legacy_id, url) VALUES (?,?)", (legacy_id, url)
255
- )
256
- self.con.commit()
257
-
258
- def attachment_store_sims(self, url: str, sims: str):
259
- self.cur.execute("UPDATE attachment SET sims = ? WHERE url = ?", (sims, url))
260
- self.con.commit()
261
-
262
- def attachment_store_sfs(self, url: str, sfs: str):
263
- self.cur.execute("UPDATE attachment SET sfs = ? WHERE url = ?", (sfs, url))
264
- self.con.commit()
265
-
266
- def attachment_get_url(self, legacy_id):
267
- res = self.cur.execute(
268
- "SELECT url FROM attachment WHERE legacy_id = ?", (legacy_id,)
269
- )
270
- return first_of_tuple_or_none(res.fetchone())
271
-
272
- def attachment_get_sims(self, url: str):
273
- res = self.cur.execute("SELECT sims FROM attachment WHERE url = ?", (url,))
274
- return first_of_tuple_or_none(res.fetchone())
275
-
276
- def attachment_get_sfs(self, url: str):
277
- res = self.cur.execute("SELECT sfs FROM attachment WHERE url = ?", (url,))
278
- return first_of_tuple_or_none(res.fetchone())
279
-
280
- def attachment_store_legacy_to_multi_xmpp_msg_ids(
281
- self, legacy_id, xmpp_ids: list[str]
282
- ):
283
- with self.con:
284
- res = self.cur.execute(
285
- "INSERT OR IGNORE INTO attachment_legacy_msg_id(legacy_id) VALUES (?)",
286
- (legacy_id,),
287
- )
288
- row_id = res.lastrowid
289
- # for xmpp_id in xmpp_ids:
290
- self.cur.executemany(
291
- "INSERT INTO attachment_xmpp_ids(legacy_msg_id, xmpp_id) VALUES (?, ?)",
292
- ((row_id, i) for i in xmpp_ids),
293
- )
294
-
295
- def attachment_get_xmpp_ids_for_legacy_msg_id(self, legacy_id) -> list:
296
- res = self.cur.execute(
297
- "SELECT xmpp_id FROM attachment_xmpp_ids "
298
- "WHERE legacy_msg_id = (SELECT id FROM attachment_legacy_msg_id WHERE legacy_id = ?)",
299
- (legacy_id,),
300
- )
301
- return [r[0] for r in res.fetchall()]
302
-
303
- def attachment_get_associated_xmpp_ids(self, xmpp_id: str):
304
- res = self.cur.execute(
305
- "SELECT xmpp_id FROM attachment_xmpp_ids "
306
- "WHERE legacy_msg_id = "
307
- "(SELECT legacy_msg_id FROM attachment_xmpp_ids WHERE xmpp_id = ?)",
308
- (xmpp_id,),
309
- )
310
- return [r[0] for r in res.fetchall() if r[0] != xmpp_id]
311
-
312
- def attachment_get_legacy_id_for_xmpp_id(self, xmpp_id: str):
313
- res = self.cur.execute(
314
- "SELECT legacy_id FROM attachment_legacy_msg_id "
315
- "WHERE id = (SELECT legacy_msg_id FROM attachment_xmpp_ids WHERE xmpp_id = ?)",
316
- (xmpp_id,),
317
- )
318
- return first_of_tuple_or_none(res.fetchone())
319
-
320
-
321
- class NickMixin(Base):
322
- def nick_get(self, jid: JID, user: "GatewayUser"):
323
- res = self.cur.execute(
324
- "SELECT nick FROM nick "
325
- "WHERE jid = ? "
326
- "AND user_id = (SELECT id FROM user WHERE jid = ?)",
327
- (str(jid), user.bare_jid),
328
- )
329
- return first_of_tuple_or_none(res.fetchone())
330
-
331
- def nick_store(self, jid: JID, nick: str, user: "GatewayUser"):
332
- self.cur.execute(
333
- "REPLACE INTO nick(jid, nick, user_id) "
334
- "VALUES (?,?,(SELECT id FROM user WHERE jid = ?))",
335
- (str(jid), nick, user.bare_jid),
336
- )
337
- self.con.commit()
338
-
339
-
340
- class AvatarMixin(Base):
341
- def avatar_get(self, jid: JID):
342
- res = self.cur.execute(
343
- "SELECT cached_id FROM avatar WHERE jid = ?", (str(jid),)
344
- )
345
- return first_of_tuple_or_none(res.fetchone())
346
-
347
- def avatar_store(self, jid: JID, cached_id: Union[int, str]):
348
- self.cur.execute(
349
- "REPLACE INTO avatar(jid, cached_id) VALUES (?,?)", (str(jid), cached_id)
350
- )
351
- self.con.commit()
352
-
353
- def avatar_delete(self, jid: JID):
354
- self.cur.execute("DELETE FROM avatar WHERE jid = ?", (str(jid),))
355
- self.con.commit()
356
-
357
-
358
- class PresenceMixin(Base):
359
- def __init__(self):
360
- super().__init__()
361
- self.__cur = cur = self.con.cursor()
362
- cur.row_factory = self.__row_factory # type:ignore
363
-
364
- @staticmethod
365
- def __row_factory(
366
- _cur: sqlite3.Cursor,
367
- row: tuple[
368
- Optional[int],
369
- Optional[PresenceTypes],
370
- Optional[str],
371
- Optional[PresenceShows],
372
- ],
373
- ):
374
- if row[0] is not None:
375
- last_seen = datetime.fromtimestamp(row[0], tz=timezone.utc)
376
- else:
377
- last_seen = None
378
- return CachedPresence(last_seen, *row[1:])
379
-
380
- def presence_nuke(self):
381
- # useful for tests
382
- self.cur.execute("DELETE FROM presence")
383
- self.con.commit()
384
-
385
- def presence_store(self, jid: JID, presence: CachedPresence, user: "GatewayUser"):
386
- self.cur.execute(
387
- "REPLACE INTO presence(jid, last_seen, ptype, pstatus, pshow, user_id) "
388
- "VALUES (?,?,?,?,?,(SELECT id FROM user WHERE jid = ?))",
389
- (
390
- str(jid),
391
- presence[0].timestamp() if presence[0] else None,
392
- *presence[1:],
393
- user.bare_jid,
394
- ),
395
- )
396
- self.con.commit()
397
-
398
- def presence_delete(self, jid: JID, user: "GatewayUser"):
399
- self.cur.execute(
400
- "DELETE FROM presence WHERE (jid = ? and user_id = (SELECT id FROM user WHERE jid = ?))",
401
- (str(jid), user.bare_jid),
402
- )
403
- self.con.commit()
404
-
405
- def presence_get(self, jid: JID, user: "GatewayUser") -> Optional[CachedPresence]:
406
- return self.__cur.execute(
407
- "SELECT last_seen, ptype, pstatus, pshow FROM presence "
408
- "WHERE jid = ? AND user_id = (SELECT id FROM user WHERE jid = ?)",
409
- (str(jid), user.bare_jid),
410
- ).fetchone()
411
-
412
-
413
- class UserMixin(Base):
414
- def user_store(self, user: "GatewayUser"):
415
- try:
416
- self.cur.execute("INSERT INTO user(jid) VALUES (?)", (user.bare_jid,))
417
- except sqlite3.IntegrityError:
418
- log.debug("User has already been added.")
419
- else:
420
- self.con.commit()
421
-
422
- def user_del(self, user: "GatewayUser"):
423
- self.cur.execute("DELETE FROM user WHERE jid = ?", (user.bare_jid,))
424
- self.con.commit()
425
-
426
-
427
- def first_of_tuple_or_none(x: Optional[tuple]):
428
- if x is None:
429
- return None
430
- return x[0]
431
-
432
-
433
- class SQLBiDict(Generic[KeyType, ValueType]):
434
- def __init__(
435
- self,
436
- table: str,
437
- key1: str,
438
- key2: str,
439
- user: "GatewayUser",
440
- sql: Optional[Base] = None,
441
- create_table=False,
442
- is_inverse=False,
443
- ):
444
- if sql is None:
445
- sql = db
446
- self.db = sql
447
- self.table = table
448
- self.key1 = key1
449
- self.key2 = key2
450
- self.user = user
451
- if create_table:
452
- sql.cur.execute(
453
- f"CREATE TABLE {table} (id "
454
- "INTEGER PRIMARY KEY,"
455
- "user_id INTEGER,"
456
- f"{key1} UNIQUE,"
457
- f"{key2} UNIQUE,"
458
- f"FOREIGN KEY(user_id) REFERENCES user(id))",
459
- )
460
- if is_inverse:
461
- return
462
- self.inverse = SQLBiDict[ValueType, KeyType](
463
- table, key2, key1, user, sql=sql, is_inverse=True
464
- )
465
-
466
- def __setitem__(self, key: KeyType, value: ValueType):
467
- self.db.cur.execute(
468
- f"REPLACE INTO {self.table}"
469
- f"(user_id, {self.key1}, {self.key2}) "
470
- "VALUES ((SELECT id FROM user WHERE jid = ?), ?, ?)",
471
- (self.user.bare_jid, key, value),
472
- )
473
- self.db.con.commit()
474
-
475
- def __getitem__(self, item: KeyType) -> ValueType:
476
- v = self.get(item)
477
- if v is None:
478
- raise KeyError(item)
479
- return v
480
-
481
- def __contains__(self, item: KeyType) -> bool:
482
- res = self.db.cur.execute(
483
- f"SELECT {self.key1} FROM {self.table} "
484
- f"WHERE {self.key1} = ? AND user_id = (SELECT id FROM user WHERE jid = ?)",
485
- (item, self.user.bare_jid),
486
- ).fetchone()
487
- return res is not None
488
-
489
- @lru_cache(100)
490
- def get(self, item: KeyType) -> Optional[ValueType]:
491
- res = self.db.cur.execute(
492
- f"SELECT {self.key2} FROM {self.table} "
493
- f"WHERE {self.key1} = ? AND user_id = (SELECT id FROM user WHERE jid = ?)",
494
- (item, self.user.bare_jid),
495
- ).fetchone()
496
- if res is None:
497
- return res
498
- return res[0]
499
-
500
-
501
- class TemporaryDB(
502
- AvatarMixin, AttachmentMixin, NickMixin, MAMMixin, UserMixin, PresenceMixin
503
- ):
504
- pass
505
-
506
-
507
- db = TemporaryDB()
508
- log = logging.getLogger(__name__)
@@ -1,96 +0,0 @@
1
- slidge/__init__.py,sha256=ykOurwgxvgYkuEim5t3M5zdbgyFyVs21SE20d4XkCKY,1624
2
- slidge/__main__.py,sha256=QCG_5nFBtMTsX_au_FYKebU3TIog0aZipNmxLYRmloI,5804
3
- slidge/command/__init__.py,sha256=UYf1mjCYbZ5G7PIgaFTWSQRAzEJkQ6dTH8Fu_e_XnO0,613
4
- slidge/command/adhoc.py,sha256=ga1i36m2Heg10h9Elb9U5dBDRT-Q5lNk4HM64nzvKoU,9188
5
- slidge/command/admin.py,sha256=aZCuQFfAWqOO8RgQu_EzKBg1yjOtohYz6r9Vtg9KQ0Y,5759
6
- slidge/command/base.py,sha256=ht0Rgq4RCx5hHthATPbbWIbX2VqfFKSUolzcHpncxk0,13073
7
- slidge/command/categories.py,sha256=BJCfaga2qoAxnHfgHD7I_RKZuBA5nnNOukkWHJwsUFE,99
8
- slidge/command/chat_command.py,sha256=kMnxrzmD7LhWgyO1w9Rgz1eA7PhfAJ2Rf34YWjoKrwQ,9975
9
- slidge/command/register.py,sha256=uU8_21do7_uSl6JpvjTu7RHypjIxOPkKFX9tHgZVGGc,6014
10
- slidge/command/user.py,sha256=2mpIfjVJt7x9AZHQq4zcOlJGk8WVahUQhh4TYiW9T2Q,8781
11
- slidge/contact/__init__.py,sha256=WMMaHk7UW7YT9EH2LtPdkU0bHQaOp4ikBhbBQskmoc8,191
12
- slidge/contact/contact.py,sha256=9umQ-fCXDHU4IgkC3kuuejqU1Bnr8GV_xfCIwXUR6PE,16101
13
- slidge/contact/roster.py,sha256=XhcAJEs_j7VIn88DVmPReXK2VopzIQVoH2bwoeCqk_U,7489
14
- slidge/core/__init__.py,sha256=RG7Jj5JCJERjhqJ31lOLYV-7bH_oblClQD1KF9LsTXo,68
15
- slidge/core/cache.py,sha256=Tkw8Bsor8zTN5wPIsBzxfnEk8GaOXMIauO4iBXxgv3M,5443
16
- slidge/core/config.py,sha256=upZsWObT14foXc5HaXlOcZPLhGL2KSeK0K-QwPDHL3I,7392
17
- slidge/core/gateway/__init__.py,sha256=rZckY2gAE-mon77_DSsAW1XtWqhBAETE2d4FqZ8pJXk,58
18
- slidge/core/gateway/base.py,sha256=SOjoEV6MME2gGDS7oZgA3RdWBlqdrZeQs5Usf80ijcY,34645
19
- slidge/core/gateway/caps.py,sha256=Qosd3cZ356mGzzQkdP6Y8OZ5c0l7H3Lr2wptoFrD1qU,1919
20
- slidge/core/gateway/delivery_receipt.py,sha256=AT_9gvZrtWpSRsDJcYjE8CmF7TW-YBbUPdqNW5zWAdo,1352
21
- slidge/core/gateway/disco.py,sha256=_ylFGQAzBvKSe1iB85uhBnRUXExExDrEaFa5MEM3E7Y,2230
22
- slidge/core/gateway/mam.py,sha256=4appZbiaFICw1kySE-y1lgNzlUzY4jbgxC0IdGv87zw,2471
23
- slidge/core/gateway/muc_admin.py,sha256=z2l4Zt0h-Z9WzEcA6MEiPNPrbYwggEqBVXpia5HtpLI,1033
24
- slidge/core/gateway/ping.py,sha256=YUmreEyEw89l0M65D_AylL5oW4xj4lvJvhwucyiKofw,1753
25
- slidge/core/gateway/presence.py,sha256=Ow2tWRx-tBL9mGeYRrHLboxD2CdJ32w9yor1kHZEL4U,2732
26
- slidge/core/gateway/registration.py,sha256=KmxDFV3N01gCs_58Re9QIxJukYP7S3_i6IbukZxzEIs,1619
27
- slidge/core/gateway/search.py,sha256=ZZPnZlTYFedsQAY-80ZX2el_oej6X7HfIwydYdTxl-o,3518
28
- slidge/core/gateway/session_dispatcher.py,sha256=YNcyVhoe-iyGfCpjoMz9py9HCkZ_Wag48rx-jnyoVOw,29193
29
- slidge/core/gateway/vcard_temp.py,sha256=uCSefV1D0Xx7Ex40t4uS0KHF7cbnq-xixAQQBxTtme8,4639
30
- slidge/core/mixins/__init__.py,sha256=iAVi4gzekXIR2xDS8ic1gSguvBbfVSScWArM4t87L-U,368
31
- slidge/core/mixins/attachment.py,sha256=yu7AzrRUCvKM_4AXYO1a83I4o_Sc8h3zteNRYOP2LgU,17808
32
- slidge/core/mixins/avatar.py,sha256=f3zSWFbj5sEo8HzV5btKsqt-clr_s1IkO5XrOjl2sWE,5493
33
- slidge/core/mixins/base.py,sha256=eWKV_9Io59roAt7BGRt6DKJgTxSaJEX4ptS9KneF32A,702
34
- slidge/core/mixins/disco.py,sha256=SKPVRVq8NUcc-zerHit7_K3Xmo1yJF1ipYN9lhHUDBU,3935
35
- slidge/core/mixins/lock.py,sha256=mVzwVVEoq1hrAMgGLh4K84BTLt7JTJ33B8HSGSorTdY,913
36
- slidge/core/mixins/message.py,sha256=yK1f6SsngJR9mQQzPwFvnscvaH3ln7pqGSv7-omWeIE,14790
37
- slidge/core/mixins/message_maker.py,sha256=cZy2eq87Cl019zVN_aay3Cxww9PUi1Z68QPorRcPJ2E,5606
38
- slidge/core/mixins/presence.py,sha256=I4jAtJd2WIHQxqPBBKLIPB4aP09vjt8yYbY-u6ojfHk,7216
39
- slidge/core/mixins/recipient.py,sha256=U-YppozUO8pA94jmD3-qmhkykTebPNaOVWc3JDPC9w8,1302
40
- slidge/core/pubsub.py,sha256=u0KvRqsOcwyc2yapzDH4eNjeqrwo8jtUYLtjYjC9W3E,18268
41
- slidge/core/session.py,sha256=xKyFxmi_tbp0WJNB7uJX_AENA2u1Pc0EzGr54ajmzfA,26134
42
- slidge/group/__init__.py,sha256=yFt7cHqeaKIMN6f9ZyhhspOcJJvBtLedGv-iICG7lto,258
43
- slidge/group/archive.py,sha256=5lnhUZ27RoVsYgcs-KiYiOLHZtB7VfRozLiTf0ZGNjM,3426
44
- slidge/group/bookmarks.py,sha256=2uX7EiMlJJhYOnTXS1oJOchF5u_hFQAlp8VT2mcPqi0,5944
45
- slidge/group/participant.py,sha256=CQnH3ayAO0MYVnVmeIehYU16gUByyjzwD0UlhIacq60,14908
46
- slidge/group/room.py,sha256=I6cysvz8rfL6mZZ2AloYufJeG4FlDkEmESLH5oG8b-0,38972
47
- slidge/migration.py,sha256=PPSjYMFsc9WGWWiSHWqUskpV_L9OJcrlecRf-Z0Vw_E,334
48
- slidge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- slidge/slixfix/__init__.py,sha256=eYYLj3e4zJMRasjDlWxV4fe3zN0VJfwxbsc_rkOHefw,1777
50
- slidge/slixfix/link_preview/__init__.py,sha256=TDPTSEH5FQxgGpQpQIde-D72AHg-6YVWG-tOj4KpKmU,290
51
- slidge/slixfix/link_preview/link_preview.py,sha256=9PgdfnoyVMHnXS0w5OFp0wz3ku96Ck-HtRXbVUlDi1U,448
52
- slidge/slixfix/link_preview/stanza.py,sha256=YAXoNw2MD0a3nzvldGKlvSemjUMbUEG23regzmj4Ntc,2664
53
- slidge/slixfix/roster.py,sha256=PkoLroNkvmrOFuVSvxj_kiQ_gWVc45DilhmZZDMNgkQ,1545
54
- slidge/slixfix/xep_0077/__init__.py,sha256=0lY1YXdgAsfrfxI_Woxaf1etHCJXe35Xtntq_icF6nA,325
55
- slidge/slixfix/xep_0077/register.py,sha256=6nwTfHNL7Z9-1wUhpAF743TNbjQLCMP7Rflkdad8d60,10431
56
- slidge/slixfix/xep_0077/stanza.py,sha256=Lngly7F1ChCkNKn7yl1QmN838fO-KqkAhkazxzDsz80,2410
57
- slidge/slixfix/xep_0100/__init__.py,sha256=AtEXDQOrEWodkN3fgKR0W3Ezsz_Zza6cgO5ZaZS-JOo,107
58
- slidge/slixfix/xep_0100/gateway.py,sha256=Fhxs2sUPnVRPa1o7RXCncnbBO2gjeZx3pbItug-8OiA,4501
59
- slidge/slixfix/xep_0100/stanza.py,sha256=7vCzej9VFQupsTpGGl0cJWuGNH4I6oVcckBu_-fE55c,232
60
- slidge/slixfix/xep_0153/__init__.py,sha256=wZCTLaVsXQn_wYEHA8YXdGCsspvj2rvq92iyi16W0xA,319
61
- slidge/slixfix/xep_0153/stanza.py,sha256=qhVfxlJEKuYDuWnd_I_VT4TtJbpX2opN6kG8oxYrc3o,735
62
- slidge/slixfix/xep_0153/vcard_avatar.py,sha256=mzaReW5c1OaeL39CeGvuzIkh3j-Z5mc2joLqn5cNc5Q,658
63
- slidge/slixfix/xep_0264/__init__.py,sha256=c6g_y-PAwQJZ4ZLWcwXc6Q5xRPeXTvvvJH4ZKQpkj3o,109
64
- slidge/slixfix/xep_0264/stanza.py,sha256=YvkI9rsGztkc9yOZBjf5PNKReW8aeGdF6MnrsfDckYs,864
65
- slidge/slixfix/xep_0264/thumbnail.py,sha256=6ukgPCWJTFUnew4USB6hNtEk_ZcpWcFAvHr2r0T5znw,456
66
- slidge/slixfix/xep_0292/__init__.py,sha256=_MvS9wGra6ig3P_dPAVlCPDJkiOFvUWGjaRsHj1woUg,98
67
- slidge/slixfix/xep_0292/vcard4.py,sha256=V-RJy15elHraKP5oj8Rx5n-y17yJNbzI5sGwzErQvNw,3113
68
- slidge/slixfix/xep_0313/__init__.py,sha256=rpvXxN4Fi-ey4Ww39OEAXoiaeWs3XMqvzR64hA6j_x4,368
69
- slidge/slixfix/xep_0313/mam.py,sha256=2USgMGgklnGXPcw_1F3482HxIyd41TLx0uit43_RthA,9211
70
- slidge/slixfix/xep_0313/stanza.py,sha256=WriAx6XKiiyZTkoTw5RHcgu3ZYdkDd6hjQ0wHqDkXE0,10249
71
- slidge/slixfix/xep_0317/__init__.py,sha256=KRMpj3n2TYTyXzlGHdGXzNbMEvbHxn7Qd1WxqRIkrhU,104
72
- slidge/slixfix/xep_0317/hats.py,sha256=HAZ7y5DNehpdPVpNpgo1JxnG2GVhVSElLYdARiL9KJQ,290
73
- slidge/slixfix/xep_0317/stanza.py,sha256=HYSJSUHqPNHmrpJsschb__qazfb2Jm3WUxLsrmPqC4I,659
74
- slidge/slixfix/xep_0356_old/__init__.py,sha256=3jGWJX2m5gWgDCxcVqCsCCVPRTcfmU96yenwvAJtOKE,180
75
- slidge/slixfix/xep_0356_old/privilege.py,sha256=kcJzFbzhOHtQMtzOJpvvwm1pghSpealWnqhC0zc8dGo,5338
76
- slidge/slixfix/xep_0356_old/stanza.py,sha256=i7aqcaTg6PBhVwbHToLtlrwxBj7uO-M7VrYSyElyEKI,1229
77
- slidge/slixfix/xep_0424/__init__.py,sha256=ngz0GBepiJs_cocaohkDRTiaB8mvaEwXoqQBv9Av9o8,284
78
- slidge/slixfix/xep_0424/retraction.py,sha256=VmJQCj4umtjslnU1ydVOL6KquZnVvqgPTAyoE1KQKwE,2448
79
- slidge/slixfix/xep_0424/stanza.py,sha256=mT8QzRX3YcyTqTSfaaWKxyhag1s1-iQy5QW9nH6cyU8,753
80
- slidge/slixfix/xep_0490/__init__.py,sha256=Rhu_1h1P34LfIP_yr4JQ_PBHcuNBLDzLdJMnNmNJRrE,158
81
- slidge/slixfix/xep_0490/mds.py,sha256=KaITeEzSWgpGyU7RP-iwipjSyCEdVMPiVIT5vHVK-RM,1527
82
- slidge/slixfix/xep_0490/stanza.py,sha256=ztec_ipyhUFz_uWQYkS0Q6LlsNiSBBC5aZK-qSmhHXk,443
83
- slidge/util/__init__.py,sha256=BELovoTMPcPPGz3D48esBr8A4BRRHXTvavfgnArBgEc,301
84
- slidge/util/archive_msg.py,sha256=2gq0N8GMVN8PKB0HxkWJza5FdgCDob_WPo0yVcx7DJk,1686
85
- slidge/util/conf.py,sha256=1j2OnOsCBar1tOObErhXR5RC3Vl3faliOZ1U8J3My58,6613
86
- slidge/util/db.py,sha256=q4IZJa2JIz_QiQHf3jU2JqZxuFjyJAXFmnUYRHR-IgU,6604
87
- slidge/util/schema.sql,sha256=zBq4K7DbaX5cjX25JnXiEXLA9loft3mMFLJN3TGRCJE,2682
88
- slidge/util/sql.py,sha256=xjSldsDwgpZc20cmk6UfS4nhdc7Hbuml7kjH6IZgT0I,16517
89
- slidge/util/test.py,sha256=8cTrL-traDv2jfvfTDNgqewsBJH19ePQ1bf7qzp1RHQ,10688
90
- slidge/util/types.py,sha256=djIOI4kBBKl_2w8gm7QLtE3NDOCA832rD_GU7A3N6U4,4689
91
- slidge/util/util.py,sha256=t8t1nPybcitpmuSlYsOwZ8gDjfiEwTjaa4D7vtzrvQ8,8587
92
- slidge-0.1.2.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
93
- slidge-0.1.2.dist-info/METADATA,sha256=yEVZPfmmBIGsoFz9G6XVDjn_8l4aZjC0gNxN9f0zb1U,4736
94
- slidge-0.1.2.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
95
- slidge-0.1.2.dist-info/entry_points.txt,sha256=SNl72KSocF5plsu_67xyH6wVWfGTXQbzkQgXbLtzDrQ,47
96
- slidge-0.1.2.dist-info/RECORD,,