slidge 0.1.0__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 (96) hide show
  1. slidge/__init__.py +61 -0
  2. slidge/__main__.py +192 -0
  3. slidge/command/__init__.py +28 -0
  4. slidge/command/adhoc.py +258 -0
  5. slidge/command/admin.py +193 -0
  6. slidge/command/base.py +441 -0
  7. slidge/command/categories.py +3 -0
  8. slidge/command/chat_command.py +288 -0
  9. slidge/command/register.py +179 -0
  10. slidge/command/user.py +250 -0
  11. slidge/contact/__init__.py +8 -0
  12. slidge/contact/contact.py +452 -0
  13. slidge/contact/roster.py +192 -0
  14. slidge/core/__init__.py +3 -0
  15. slidge/core/cache.py +183 -0
  16. slidge/core/config.py +209 -0
  17. slidge/core/gateway/__init__.py +3 -0
  18. slidge/core/gateway/base.py +892 -0
  19. slidge/core/gateway/caps.py +63 -0
  20. slidge/core/gateway/delivery_receipt.py +52 -0
  21. slidge/core/gateway/disco.py +80 -0
  22. slidge/core/gateway/mam.py +75 -0
  23. slidge/core/gateway/muc_admin.py +35 -0
  24. slidge/core/gateway/ping.py +66 -0
  25. slidge/core/gateway/presence.py +95 -0
  26. slidge/core/gateway/registration.py +53 -0
  27. slidge/core/gateway/search.py +102 -0
  28. slidge/core/gateway/session_dispatcher.py +757 -0
  29. slidge/core/gateway/vcard_temp.py +130 -0
  30. slidge/core/mixins/__init__.py +19 -0
  31. slidge/core/mixins/attachment.py +506 -0
  32. slidge/core/mixins/avatar.py +167 -0
  33. slidge/core/mixins/base.py +31 -0
  34. slidge/core/mixins/disco.py +130 -0
  35. slidge/core/mixins/lock.py +31 -0
  36. slidge/core/mixins/message.py +398 -0
  37. slidge/core/mixins/message_maker.py +154 -0
  38. slidge/core/mixins/presence.py +217 -0
  39. slidge/core/mixins/recipient.py +43 -0
  40. slidge/core/pubsub.py +525 -0
  41. slidge/core/session.py +752 -0
  42. slidge/group/__init__.py +10 -0
  43. slidge/group/archive.py +125 -0
  44. slidge/group/bookmarks.py +163 -0
  45. slidge/group/participant.py +440 -0
  46. slidge/group/room.py +1095 -0
  47. slidge/migration.py +18 -0
  48. slidge/py.typed +0 -0
  49. slidge/slixfix/__init__.py +68 -0
  50. slidge/slixfix/link_preview/__init__.py +10 -0
  51. slidge/slixfix/link_preview/link_preview.py +17 -0
  52. slidge/slixfix/link_preview/stanza.py +99 -0
  53. slidge/slixfix/roster.py +60 -0
  54. slidge/slixfix/xep_0077/__init__.py +10 -0
  55. slidge/slixfix/xep_0077/register.py +289 -0
  56. slidge/slixfix/xep_0077/stanza.py +104 -0
  57. slidge/slixfix/xep_0100/__init__.py +5 -0
  58. slidge/slixfix/xep_0100/gateway.py +121 -0
  59. slidge/slixfix/xep_0100/stanza.py +9 -0
  60. slidge/slixfix/xep_0153/__init__.py +10 -0
  61. slidge/slixfix/xep_0153/stanza.py +25 -0
  62. slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
  63. slidge/slixfix/xep_0264/__init__.py +5 -0
  64. slidge/slixfix/xep_0264/stanza.py +36 -0
  65. slidge/slixfix/xep_0264/thumbnail.py +23 -0
  66. slidge/slixfix/xep_0292/__init__.py +5 -0
  67. slidge/slixfix/xep_0292/vcard4.py +100 -0
  68. slidge/slixfix/xep_0313/__init__.py +12 -0
  69. slidge/slixfix/xep_0313/mam.py +262 -0
  70. slidge/slixfix/xep_0313/stanza.py +359 -0
  71. slidge/slixfix/xep_0317/__init__.py +5 -0
  72. slidge/slixfix/xep_0317/hats.py +17 -0
  73. slidge/slixfix/xep_0317/stanza.py +28 -0
  74. slidge/slixfix/xep_0356_old/__init__.py +7 -0
  75. slidge/slixfix/xep_0356_old/privilege.py +167 -0
  76. slidge/slixfix/xep_0356_old/stanza.py +44 -0
  77. slidge/slixfix/xep_0424/__init__.py +9 -0
  78. slidge/slixfix/xep_0424/retraction.py +77 -0
  79. slidge/slixfix/xep_0424/stanza.py +28 -0
  80. slidge/slixfix/xep_0490/__init__.py +8 -0
  81. slidge/slixfix/xep_0490/mds.py +47 -0
  82. slidge/slixfix/xep_0490/stanza.py +17 -0
  83. slidge/util/__init__.py +15 -0
  84. slidge/util/archive_msg.py +61 -0
  85. slidge/util/conf.py +206 -0
  86. slidge/util/db.py +229 -0
  87. slidge/util/schema.sql +126 -0
  88. slidge/util/sql.py +508 -0
  89. slidge/util/test.py +295 -0
  90. slidge/util/types.py +180 -0
  91. slidge/util/util.py +295 -0
  92. slidge-0.1.0.dist-info/LICENSE +661 -0
  93. slidge-0.1.0.dist-info/METADATA +109 -0
  94. slidge-0.1.0.dist-info/RECORD +96 -0
  95. slidge-0.1.0.dist-info/WHEEL +4 -0
  96. slidge-0.1.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,10 @@
1
+ """
2
+ Everything related to groups.
3
+ """
4
+
5
+ from ..util.types import MucType
6
+ from .bookmarks import LegacyBookmarks
7
+ from .participant import LegacyParticipant
8
+ from .room import LegacyMUC
9
+
10
+ __all__ = ("LegacyBookmarks", "LegacyParticipant", "LegacyMUC", "MucType")
@@ -0,0 +1,125 @@
1
+ import logging
2
+ import uuid
3
+ from copy import copy
4
+ from datetime import datetime
5
+ from typing import TYPE_CHECKING, Collection, Optional
6
+
7
+ from slixmpp import Iq, Message
8
+
9
+ from ..util.archive_msg import HistoryMessage
10
+ from ..util.db import GatewayUser
11
+ from ..util.sql import db
12
+
13
+ if TYPE_CHECKING:
14
+ from .participant import LegacyParticipant
15
+
16
+
17
+ class MessageArchive:
18
+ def __init__(self, db_id: str, user: GatewayUser):
19
+ self.db_id = db_id
20
+ self.user = user
21
+ db.mam_add_muc(db_id, user)
22
+
23
+ def add(
24
+ self,
25
+ msg: Message,
26
+ participant: Optional["LegacyParticipant"] = None,
27
+ ):
28
+ """
29
+ Add a message to the archive if it is deemed archivable
30
+
31
+ :param msg:
32
+ :param participant:
33
+ """
34
+ if not archivable(msg):
35
+ return
36
+ new_msg = copy(msg)
37
+ if participant and not participant.muc.is_anonymous:
38
+ new_msg["muc"]["role"] = participant.role
39
+ new_msg["muc"]["affiliation"] = participant.affiliation
40
+ if participant.contact:
41
+ new_msg["muc"]["jid"] = participant.contact.jid.bare
42
+ elif participant.is_user:
43
+ new_msg["muc"]["jid"] = participant.user.jid.bare
44
+ elif participant.is_system:
45
+ new_msg["muc"]["jid"] = participant.muc.jid
46
+ else:
47
+ log.warning("No real JID for participant in this group")
48
+ new_msg["muc"][
49
+ "jid"
50
+ ] = f"{uuid.uuid4()}@{participant.xmpp.boundjid.bare}"
51
+
52
+ db.mam_add_msg(self.db_id, HistoryMessage(new_msg), self.user)
53
+
54
+ def __iter__(self):
55
+ return iter(self.get_all())
56
+
57
+ def get_all(
58
+ self,
59
+ start_date: Optional[datetime] = None,
60
+ end_date: Optional[datetime] = None,
61
+ before_id: Optional[str] = None,
62
+ after_id: Optional[str] = None,
63
+ ids: Collection[str] = (),
64
+ last_page_n: Optional[int] = None,
65
+ sender: Optional[str] = None,
66
+ flip=False,
67
+ ):
68
+ for msg in db.mam_get_messages(
69
+ self.user,
70
+ self.db_id,
71
+ before_id=before_id,
72
+ after_id=after_id,
73
+ ids=ids,
74
+ last_page_n=last_page_n,
75
+ sender=sender,
76
+ start_date=start_date,
77
+ end_date=end_date,
78
+ flip=flip,
79
+ ):
80
+ yield msg
81
+
82
+ async def send_metadata(self, iq: Iq):
83
+ """
84
+ Send archive extent, as per the spec
85
+
86
+ :param iq:
87
+ :return:
88
+ """
89
+ reply = iq.reply()
90
+ messages = db.mam_get_first_and_last(self.db_id)
91
+ if messages:
92
+ for x, m in [("start", messages[0]), ("end", messages[-1])]:
93
+ reply["mam_metadata"][x]["id"] = m.id
94
+ reply["mam_metadata"][x]["timestamp"] = m.sent_on
95
+ else:
96
+ reply.enable("mam_metadata")
97
+ reply.send()
98
+
99
+
100
+ def archivable(msg: Message):
101
+ """
102
+ Determine if a message stanza is worth archiving, ie, convey meaningful
103
+ info
104
+
105
+ :param msg:
106
+ :return:
107
+ """
108
+
109
+ if msg.get_plugin("hint", check=True) and msg["hint"] == "no-store":
110
+ return False
111
+
112
+ if msg["body"]:
113
+ return True
114
+
115
+ if msg.get_plugin("apply_to", check=True):
116
+ # retractions
117
+ return True
118
+
119
+ if msg.get_plugin("reactions", check=True):
120
+ return True
121
+
122
+ return False
123
+
124
+
125
+ log = logging.getLogger(__name__)
@@ -0,0 +1,163 @@
1
+ import abc
2
+ import logging
3
+ from typing import TYPE_CHECKING, Generic, Type
4
+
5
+ from slixmpp import JID
6
+ from slixmpp.jid import _unescape_node
7
+
8
+ from ..contact.roster import ESCAPE_TABLE
9
+ from ..core.mixins.lock import NamedLockMixin
10
+ from ..util import SubclassableOnce
11
+ from ..util.types import LegacyGroupIdType, LegacyMUCType
12
+ from .room import LegacyMUC
13
+
14
+ if TYPE_CHECKING:
15
+ from slidge.core.session import BaseSession
16
+
17
+
18
+ class LegacyBookmarks(
19
+ Generic[LegacyGroupIdType, LegacyMUCType],
20
+ NamedLockMixin,
21
+ metaclass=SubclassableOnce,
22
+ ):
23
+ """
24
+ This is instantiated once per :class:`~slidge.BaseSession`
25
+ """
26
+
27
+ def __init__(self, session: "BaseSession"):
28
+ self.session = session
29
+ self.xmpp = session.xmpp
30
+ self.user = session.user
31
+
32
+ self._mucs_by_legacy_id = dict[LegacyGroupIdType, LegacyMUCType]()
33
+ self._mucs_by_bare_jid = dict[str, LegacyMUCType]()
34
+
35
+ self._muc_class: Type[LegacyMUCType] = LegacyMUC.get_self_or_unique_subclass()
36
+
37
+ self._user_nick: str = self.session.user.jid.node
38
+
39
+ super().__init__()
40
+ self.log = logging.getLogger(f"{self.user.bare_jid}:bookmarks")
41
+ self.ready = self.session.xmpp.loop.create_future()
42
+ if not self.xmpp.GROUPS:
43
+ self.ready.set_result(True)
44
+
45
+ @property
46
+ def user_nick(self):
47
+ return self._user_nick
48
+
49
+ @user_nick.setter
50
+ def user_nick(self, nick: str):
51
+ self._user_nick = nick
52
+
53
+ def __iter__(self):
54
+ return iter(self._mucs_by_legacy_id.values())
55
+
56
+ def __repr__(self):
57
+ return f"<Bookmarks of {self.user}>"
58
+
59
+ async def __finish_init_muc(self, legacy_id: LegacyGroupIdType, jid: JID):
60
+ muc = self._muc_class(self.session, legacy_id=legacy_id, jid=jid)
61
+ await muc.avatar_wrap_update_info()
62
+ if not muc.user_nick:
63
+ muc.user_nick = self._user_nick
64
+ self.log.debug("MUC created: %r", muc)
65
+ self._mucs_by_legacy_id[muc.legacy_id] = muc
66
+ self._mucs_by_bare_jid[muc.jid.bare] = muc
67
+ return muc
68
+
69
+ async def legacy_id_to_jid_local_part(self, legacy_id: LegacyGroupIdType):
70
+ return await self.legacy_id_to_jid_username(legacy_id)
71
+
72
+ async def jid_local_part_to_legacy_id(self, local_part: str):
73
+ return await self.jid_username_to_legacy_id(local_part)
74
+
75
+ async def legacy_id_to_jid_username(self, legacy_id: LegacyGroupIdType):
76
+ """
77
+ The default implementation calls ``str()`` on the legacy_id and
78
+ escape characters according to :xep:`0106`.
79
+
80
+ You can override this class and implement a more subtle logic to raise
81
+ an :class:`~slixmpp.exceptions.XMPPError` early
82
+
83
+ :param legacy_id:
84
+ :return:
85
+ """
86
+ return str(legacy_id).translate(ESCAPE_TABLE)
87
+
88
+ async def jid_username_to_legacy_id(self, username: str):
89
+ """
90
+
91
+ :param username:
92
+ :return:
93
+ """
94
+ return _unescape_node(username)
95
+
96
+ async def by_jid(self, jid: JID) -> LegacyMUCType:
97
+ bare = jid.bare
98
+ async with self.lock(("bare", bare)):
99
+ muc = self._mucs_by_bare_jid.get(bare)
100
+ if muc is None:
101
+ self.log.debug("Attempting to instantiate a new MUC for JID %s", jid)
102
+ local_part = jid.node
103
+ legacy_id = await self.jid_local_part_to_legacy_id(local_part)
104
+ if self.get_lock(("legacy_id", legacy_id)):
105
+ self.log.debug("Not instantiating %s after all", jid)
106
+ return await self.by_legacy_id(legacy_id)
107
+ self.log.debug("%r is group %r", local_part, legacy_id)
108
+ muc = await self.__finish_init_muc(legacy_id, JID(bare))
109
+ else:
110
+ self.log.trace("Found an existing MUC instance: %s", muc) # type:ignore
111
+ return muc
112
+
113
+ async def by_legacy_id(self, legacy_id: LegacyGroupIdType) -> LegacyMUCType:
114
+ async with self.lock(("legacy_id", legacy_id)):
115
+ muc = self._mucs_by_legacy_id.get(legacy_id)
116
+ if muc is None:
117
+ self.log.debug("Create new MUC instance for legacy ID %s", legacy_id)
118
+ local = await self.legacy_id_to_jid_local_part(legacy_id)
119
+ bare = f"{local}@{self.xmpp.boundjid}"
120
+ jid = JID(bare)
121
+ if self.get_lock(("bare", bare)):
122
+ self.log.debug("Not instantiating %s after all", legacy_id)
123
+ return await self.by_jid(jid)
124
+ muc = await self.__finish_init_muc(legacy_id, jid)
125
+ else:
126
+ self.log.trace("Found an existing MUC instance: %s", muc) # type:ignore
127
+
128
+ return muc
129
+
130
+ @abc.abstractmethod
131
+ async def fill(self):
132
+ """
133
+ Establish a user's known groups.
134
+
135
+ This has to be overridden in plugins with group support and at the
136
+ minimum, this should ``await self.by_legacy_id(group_id)`` for all
137
+ the groups a user is part of.
138
+
139
+ Slidge internals will call this on successful :meth:`BaseSession.login`
140
+
141
+ """
142
+ if self.xmpp.GROUPS:
143
+ raise NotImplementedError(
144
+ "The plugin advertised support for groups but"
145
+ " LegacyBookmarks.fill() was not overridden."
146
+ )
147
+
148
+ def remove(self, muc: LegacyMUC):
149
+ try:
150
+ del self._mucs_by_legacy_id[muc.legacy_id]
151
+ except KeyError:
152
+ self.log.warning("Removed a MUC that we didn't store by legacy ID")
153
+ try:
154
+ del self._mucs_by_bare_jid[muc.jid.bare]
155
+ except KeyError:
156
+ self.log.warning("Removed a MUC that we didn't store by JID")
157
+ for part in muc._participants_by_contacts.values():
158
+ try:
159
+ part.contact.participants.remove(part)
160
+ except KeyError:
161
+ part.log.warning(
162
+ "That participant wasn't stored in the contact's participants attribute"
163
+ )