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.
- slidge/__init__.py +61 -0
- slidge/__main__.py +192 -0
- slidge/command/__init__.py +28 -0
- slidge/command/adhoc.py +258 -0
- slidge/command/admin.py +193 -0
- slidge/command/base.py +441 -0
- slidge/command/categories.py +3 -0
- slidge/command/chat_command.py +288 -0
- slidge/command/register.py +179 -0
- slidge/command/user.py +250 -0
- slidge/contact/__init__.py +8 -0
- slidge/contact/contact.py +452 -0
- slidge/contact/roster.py +192 -0
- slidge/core/__init__.py +3 -0
- slidge/core/cache.py +183 -0
- slidge/core/config.py +209 -0
- slidge/core/gateway/__init__.py +3 -0
- slidge/core/gateway/base.py +892 -0
- slidge/core/gateway/caps.py +63 -0
- slidge/core/gateway/delivery_receipt.py +52 -0
- slidge/core/gateway/disco.py +80 -0
- slidge/core/gateway/mam.py +75 -0
- slidge/core/gateway/muc_admin.py +35 -0
- slidge/core/gateway/ping.py +66 -0
- slidge/core/gateway/presence.py +95 -0
- slidge/core/gateway/registration.py +53 -0
- slidge/core/gateway/search.py +102 -0
- slidge/core/gateway/session_dispatcher.py +757 -0
- slidge/core/gateway/vcard_temp.py +130 -0
- slidge/core/mixins/__init__.py +19 -0
- slidge/core/mixins/attachment.py +506 -0
- slidge/core/mixins/avatar.py +167 -0
- slidge/core/mixins/base.py +31 -0
- slidge/core/mixins/disco.py +130 -0
- slidge/core/mixins/lock.py +31 -0
- slidge/core/mixins/message.py +398 -0
- slidge/core/mixins/message_maker.py +154 -0
- slidge/core/mixins/presence.py +217 -0
- slidge/core/mixins/recipient.py +43 -0
- slidge/core/pubsub.py +525 -0
- slidge/core/session.py +752 -0
- slidge/group/__init__.py +10 -0
- slidge/group/archive.py +125 -0
- slidge/group/bookmarks.py +163 -0
- slidge/group/participant.py +440 -0
- slidge/group/room.py +1095 -0
- slidge/migration.py +18 -0
- slidge/py.typed +0 -0
- slidge/slixfix/__init__.py +68 -0
- slidge/slixfix/link_preview/__init__.py +10 -0
- slidge/slixfix/link_preview/link_preview.py +17 -0
- slidge/slixfix/link_preview/stanza.py +99 -0
- slidge/slixfix/roster.py +60 -0
- slidge/slixfix/xep_0077/__init__.py +10 -0
- slidge/slixfix/xep_0077/register.py +289 -0
- slidge/slixfix/xep_0077/stanza.py +104 -0
- slidge/slixfix/xep_0100/__init__.py +5 -0
- slidge/slixfix/xep_0100/gateway.py +121 -0
- slidge/slixfix/xep_0100/stanza.py +9 -0
- slidge/slixfix/xep_0153/__init__.py +10 -0
- slidge/slixfix/xep_0153/stanza.py +25 -0
- slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
- slidge/slixfix/xep_0264/__init__.py +5 -0
- slidge/slixfix/xep_0264/stanza.py +36 -0
- slidge/slixfix/xep_0264/thumbnail.py +23 -0
- slidge/slixfix/xep_0292/__init__.py +5 -0
- slidge/slixfix/xep_0292/vcard4.py +100 -0
- slidge/slixfix/xep_0313/__init__.py +12 -0
- slidge/slixfix/xep_0313/mam.py +262 -0
- slidge/slixfix/xep_0313/stanza.py +359 -0
- slidge/slixfix/xep_0317/__init__.py +5 -0
- slidge/slixfix/xep_0317/hats.py +17 -0
- slidge/slixfix/xep_0317/stanza.py +28 -0
- slidge/slixfix/xep_0356_old/__init__.py +7 -0
- slidge/slixfix/xep_0356_old/privilege.py +167 -0
- slidge/slixfix/xep_0356_old/stanza.py +44 -0
- slidge/slixfix/xep_0424/__init__.py +9 -0
- slidge/slixfix/xep_0424/retraction.py +77 -0
- slidge/slixfix/xep_0424/stanza.py +28 -0
- slidge/slixfix/xep_0490/__init__.py +8 -0
- slidge/slixfix/xep_0490/mds.py +47 -0
- slidge/slixfix/xep_0490/stanza.py +17 -0
- slidge/util/__init__.py +15 -0
- slidge/util/archive_msg.py +61 -0
- slidge/util/conf.py +206 -0
- slidge/util/db.py +229 -0
- slidge/util/schema.sql +126 -0
- slidge/util/sql.py +508 -0
- slidge/util/test.py +295 -0
- slidge/util/types.py +180 -0
- slidge/util/util.py +295 -0
- slidge-0.1.0.dist-info/LICENSE +661 -0
- slidge-0.1.0.dist-info/METADATA +109 -0
- slidge-0.1.0.dist-info/RECORD +96 -0
- slidge-0.1.0.dist-info/WHEEL +4 -0
- slidge-0.1.0.dist-info/entry_points.txt +3 -0
slidge/group/__init__.py
ADDED
@@ -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")
|
slidge/group/archive.py
ADDED
@@ -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
|
+
)
|