slidge 0.1.0rc1__py3-none-any.whl → 0.1.2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- slidge/__init__.py +54 -31
- slidge/__main__.py +51 -5
- 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 +2 -0
- slidge/core/cache.py +121 -39
- slidge/core/config.py +116 -11
- slidge/core/gateway/__init__.py +3 -0
- slidge/core/gateway/base.py +895 -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 +795 -0
- slidge/core/gateway/vcard_temp.py +130 -0
- slidge/core/mixins/__init__.py +9 -1
- slidge/core/mixins/attachment.py +506 -0
- slidge/core/mixins/avatar.py +167 -0
- slidge/core/mixins/base.py +6 -19
- slidge/core/mixins/disco.py +66 -15
- slidge/core/mixins/lock.py +31 -0
- slidge/core/mixins/message.py +254 -252
- slidge/core/mixins/message_maker.py +154 -0
- slidge/core/mixins/presence.py +128 -31
- slidge/core/mixins/recipient.py +43 -0
- slidge/core/pubsub.py +275 -116
- slidge/core/session.py +586 -518
- slidge/group/__init__.py +10 -0
- slidge/group/archive.py +125 -0
- slidge/group/bookmarks.py +163 -0
- slidge/group/participant.py +458 -0
- slidge/group/room.py +1103 -0
- slidge/migration.py +18 -0
- slidge/slixfix/__init__.py +68 -0
- slidge/{util/xep_0050 → slixfix/link_preview}/__init__.py +4 -5
- slidge/slixfix/link_preview/link_preview.py +17 -0
- slidge/slixfix/link_preview/stanza.py +99 -0
- slidge/slixfix/roster.py +60 -0
- slidge/{util → slixfix}/xep_0077/register.py +1 -2
- slidge/slixfix/xep_0077/stanza.py +104 -0
- slidge/{util → slixfix}/xep_0100/gateway.py +17 -12
- 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/{util → slixfix}/xep_0356_old/privilege.py +9 -7
- 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 +4 -6
- slidge/util/archive_msg.py +61 -0
- slidge/util/conf.py +25 -4
- slidge/util/db.py +23 -69
- slidge/util/schema.sql +126 -0
- slidge/util/sql.py +508 -0
- slidge/util/test.py +136 -86
- slidge/util/types.py +155 -14
- slidge/util/util.py +225 -51
- slidge-0.1.2.dist-info/METADATA +111 -0
- slidge-0.1.2.dist-info/RECORD +96 -0
- {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/WHEEL +1 -1
- slidge/core/adhoc.py +0 -492
- slidge/core/chat_command.py +0 -197
- slidge/core/contact.py +0 -441
- slidge/core/disco.py +0 -59
- slidge/core/gateway.py +0 -899
- slidge/core/muc/__init__.py +0 -3
- slidge/core/muc/bookmarks.py +0 -74
- slidge/core/muc/participant.py +0 -152
- slidge/core/muc/room.py +0 -348
- slidge/plugins/discord/__init__.py +0 -121
- slidge/plugins/discord/client.py +0 -121
- slidge/plugins/discord/session.py +0 -172
- slidge/plugins/dummy.py +0 -334
- slidge/plugins/facebook.py +0 -591
- slidge/plugins/hackernews.py +0 -209
- slidge/plugins/mattermost/__init__.py +0 -1
- slidge/plugins/mattermost/api.py +0 -288
- slidge/plugins/mattermost/gateway.py +0 -417
- slidge/plugins/mattermost/websocket.py +0 -248
- slidge/plugins/signal/__init__.py +0 -4
- slidge/plugins/signal/config.py +0 -4
- slidge/plugins/signal/contact.py +0 -104
- slidge/plugins/signal/gateway.py +0 -379
- slidge/plugins/signal/group.py +0 -76
- slidge/plugins/signal/session.py +0 -515
- slidge/plugins/signal/txt.py +0 -13
- slidge/plugins/signal/util.py +0 -32
- slidge/plugins/skype.py +0 -310
- slidge/plugins/steam.py +0 -400
- slidge/plugins/telegram/__init__.py +0 -6
- slidge/plugins/telegram/client.py +0 -325
- slidge/plugins/telegram/config.py +0 -21
- slidge/plugins/telegram/contact.py +0 -154
- slidge/plugins/telegram/gateway.py +0 -182
- slidge/plugins/telegram/group.py +0 -184
- slidge/plugins/telegram/session.py +0 -275
- slidge/plugins/telegram/util.py +0 -153
- slidge/plugins/whatsapp/__init__.py +0 -6
- slidge/plugins/whatsapp/config.py +0 -17
- slidge/plugins/whatsapp/contact.py +0 -33
- slidge/plugins/whatsapp/event.go +0 -455
- slidge/plugins/whatsapp/gateway.go +0 -156
- slidge/plugins/whatsapp/gateway.py +0 -69
- slidge/plugins/whatsapp/go.mod +0 -17
- slidge/plugins/whatsapp/go.sum +0 -22
- slidge/plugins/whatsapp/session.go +0 -371
- slidge/plugins/whatsapp/session.py +0 -370
- slidge/util/xep_0030/__init__.py +0 -13
- slidge/util/xep_0030/disco.py +0 -811
- slidge/util/xep_0030/stanza/__init__.py +0 -7
- slidge/util/xep_0030/stanza/info.py +0 -270
- slidge/util/xep_0030/stanza/items.py +0 -147
- slidge/util/xep_0030/static.py +0 -467
- slidge/util/xep_0050/adhoc.py +0 -631
- slidge/util/xep_0050/stanza.py +0 -180
- slidge/util/xep_0077/stanza.py +0 -71
- slidge/util/xep_0292/__init__.py +0 -1
- slidge/util/xep_0292/stanza.py +0 -167
- slidge/util/xep_0292/vcard4.py +0 -74
- slidge/util/xep_0356/__init__.py +0 -7
- slidge/util/xep_0356/permissions.py +0 -35
- slidge/util/xep_0356/privilege.py +0 -160
- slidge/util/xep_0356/stanza.py +0 -44
- slidge/util/xep_0461/__init__.py +0 -6
- slidge/util/xep_0461/reply.py +0 -48
- slidge/util/xep_0461/stanza.py +0 -80
- slidge-0.1.0rc1.dist-info/METADATA +0 -171
- slidge-0.1.0rc1.dist-info/RECORD +0 -99
- /slidge/{plugins/__init__.py → py.typed} +0 -0
- /slidge/{util → slixfix}/xep_0077/__init__.py +0 -0
- /slidge/{util → slixfix}/xep_0100/__init__.py +0 -0
- /slidge/{util → slixfix}/xep_0100/stanza.py +0 -0
- /slidge/{util → slixfix}/xep_0356_old/__init__.py +0 -0
- /slidge/{util → slixfix}/xep_0356_old/stanza.py +0 -0
- {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/LICENSE +0 -0
- {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,458 @@
|
|
1
|
+
import logging
|
2
|
+
import string
|
3
|
+
import stringprep
|
4
|
+
import uuid
|
5
|
+
import warnings
|
6
|
+
from copy import copy
|
7
|
+
from datetime import datetime
|
8
|
+
from functools import cached_property
|
9
|
+
from typing import TYPE_CHECKING, Optional, Union
|
10
|
+
|
11
|
+
from slixmpp import JID, InvalidJID, Message, Presence
|
12
|
+
from slixmpp.plugins.xep_0045.stanza import MUCAdminItem
|
13
|
+
from slixmpp.stringprep import StringprepError, resourceprep
|
14
|
+
from slixmpp.types import MessageTypes, OptJid
|
15
|
+
from slixmpp.util.stringprep_profiles import StringPrepError, prohibit_output
|
16
|
+
|
17
|
+
from ..contact import LegacyContact
|
18
|
+
from ..core.mixins import ChatterDiscoMixin, MessageMixin, PresenceMixin
|
19
|
+
from ..util import SubclassableOnce, strip_illegal_chars
|
20
|
+
from ..util.sql import CachedPresence
|
21
|
+
from ..util.types import (
|
22
|
+
Hat,
|
23
|
+
LegacyMessageType,
|
24
|
+
MessageOrPresenceTypeVar,
|
25
|
+
MucAffiliation,
|
26
|
+
MucRole,
|
27
|
+
)
|
28
|
+
|
29
|
+
if TYPE_CHECKING:
|
30
|
+
from .room import LegacyMUC
|
31
|
+
|
32
|
+
|
33
|
+
def strip_non_printable(nickname: str):
|
34
|
+
new = (
|
35
|
+
"".join(x for x in nickname if x in string.printable)
|
36
|
+
+ f"-slidge-{hash(nickname)}"
|
37
|
+
)
|
38
|
+
warnings.warn(f"Could not use {nickname} as a nickname, using {new}")
|
39
|
+
return new
|
40
|
+
|
41
|
+
|
42
|
+
class LegacyParticipant(
|
43
|
+
PresenceMixin,
|
44
|
+
MessageMixin,
|
45
|
+
ChatterDiscoMixin,
|
46
|
+
metaclass=SubclassableOnce,
|
47
|
+
):
|
48
|
+
"""
|
49
|
+
A legacy participant of a legacy group chat.
|
50
|
+
"""
|
51
|
+
|
52
|
+
mtype: MessageTypes = "groupchat"
|
53
|
+
_can_send_carbon = False
|
54
|
+
USE_STANZA_ID = True
|
55
|
+
STRIP_SHORT_DELAY = False
|
56
|
+
|
57
|
+
def __init__(
|
58
|
+
self,
|
59
|
+
muc: "LegacyMUC",
|
60
|
+
nickname: Optional[str] = None,
|
61
|
+
is_user=False,
|
62
|
+
is_system=False,
|
63
|
+
role: MucRole = "participant",
|
64
|
+
affiliation: MucAffiliation = "member",
|
65
|
+
):
|
66
|
+
super().__init__()
|
67
|
+
self._hats = list[Hat]()
|
68
|
+
self.muc = muc
|
69
|
+
self.session = session = muc.session
|
70
|
+
self.user = session.user
|
71
|
+
self.xmpp = session.xmpp
|
72
|
+
self._role = role
|
73
|
+
self._affiliation = affiliation
|
74
|
+
self.is_user: bool = is_user
|
75
|
+
self.is_system: bool = is_system
|
76
|
+
|
77
|
+
self._nickname = nickname
|
78
|
+
|
79
|
+
self.__update_jid(nickname)
|
80
|
+
log.debug("Instantiation of: %r", self)
|
81
|
+
|
82
|
+
self.contact: Optional["LegacyContact"] = None
|
83
|
+
# we track if we already sent a presence for this participant.
|
84
|
+
# if we didn't, we send it before the first message.
|
85
|
+
# this way, event in plugins that don't map "user has joined" events,
|
86
|
+
# we send a "join"-presence from the participant before the first message
|
87
|
+
self.__presence_sent = False
|
88
|
+
self.log = logging.getLogger(f"{self.user.bare_jid}:{self.jid}")
|
89
|
+
|
90
|
+
def __repr__(self):
|
91
|
+
return f"<Participant '{self.nickname}'/'{self.jid}' of '{self.muc}'>"
|
92
|
+
|
93
|
+
@property
|
94
|
+
def affiliation(self):
|
95
|
+
return self._affiliation
|
96
|
+
|
97
|
+
@affiliation.setter
|
98
|
+
def affiliation(self, affiliation: MucAffiliation):
|
99
|
+
if self._affiliation == affiliation:
|
100
|
+
return
|
101
|
+
self._affiliation = affiliation
|
102
|
+
if not self.__presence_sent:
|
103
|
+
return
|
104
|
+
self.send_last_presence(force=True, no_cache_online=True)
|
105
|
+
|
106
|
+
def send_affiliation_change(self):
|
107
|
+
# internal use by slidge
|
108
|
+
msg = self._make_message()
|
109
|
+
msg["muc"]["affiliation"] = self._affiliation
|
110
|
+
msg["type"] = "normal"
|
111
|
+
if not self.muc.is_anonymous and not self.is_system:
|
112
|
+
if self.contact:
|
113
|
+
msg["muc"]["jid"] = self.contact.jid
|
114
|
+
else:
|
115
|
+
warnings.warn(
|
116
|
+
f"Private group but no 1:1 JID associated to '{self}'",
|
117
|
+
)
|
118
|
+
self._send(msg)
|
119
|
+
|
120
|
+
@property
|
121
|
+
def role(self):
|
122
|
+
return self._role
|
123
|
+
|
124
|
+
@role.setter
|
125
|
+
def role(self, role: MucRole):
|
126
|
+
if self._role == role:
|
127
|
+
return
|
128
|
+
self._role = role
|
129
|
+
if not self.__presence_sent:
|
130
|
+
return
|
131
|
+
self.send_last_presence(force=True, no_cache_online=True)
|
132
|
+
|
133
|
+
def set_hats(self, hats: list[Hat]):
|
134
|
+
if self._hats == hats:
|
135
|
+
return
|
136
|
+
self._hats = hats
|
137
|
+
if not self.__presence_sent:
|
138
|
+
return
|
139
|
+
self.send_last_presence(force=True, no_cache_online=True)
|
140
|
+
|
141
|
+
def __update_jid(self, unescaped_nickname: Optional[str]):
|
142
|
+
j: JID = copy(self.muc.jid)
|
143
|
+
|
144
|
+
if self.is_system:
|
145
|
+
self.jid = j
|
146
|
+
return
|
147
|
+
|
148
|
+
nickname = unescaped_nickname
|
149
|
+
|
150
|
+
if nickname:
|
151
|
+
nickname = self._nickname_no_illegal = strip_illegal_chars(nickname)
|
152
|
+
else:
|
153
|
+
warnings.warn(
|
154
|
+
"Only the system participant is allowed to not have a nickname"
|
155
|
+
)
|
156
|
+
nickname = f"unnamed-{uuid.uuid4()}"
|
157
|
+
|
158
|
+
assert isinstance(nickname, str)
|
159
|
+
|
160
|
+
try:
|
161
|
+
# workaround for https://codeberg.org/poezio/slixmpp/issues/3480
|
162
|
+
prohibit_output(nickname, [stringprep.in_table_a1])
|
163
|
+
resourceprep(nickname)
|
164
|
+
except (StringPrepError, StringprepError):
|
165
|
+
nickname = nickname.encode("punycode").decode()
|
166
|
+
|
167
|
+
# at this point there still might be control chars
|
168
|
+
try:
|
169
|
+
j.resource = nickname
|
170
|
+
except InvalidJID:
|
171
|
+
j.resource = strip_non_printable(nickname)
|
172
|
+
|
173
|
+
if nickname != unescaped_nickname:
|
174
|
+
self.muc._participants_by_escaped_nicknames[nickname] = self # type:ignore
|
175
|
+
|
176
|
+
self.jid = j
|
177
|
+
|
178
|
+
def send_configuration_change(self, codes: tuple[int]):
|
179
|
+
if not self.is_system:
|
180
|
+
raise RuntimeError("This is only possible for the system participant")
|
181
|
+
msg = self._make_message()
|
182
|
+
msg["muc"]["status_codes"] = codes
|
183
|
+
self._send(msg)
|
184
|
+
|
185
|
+
@property
|
186
|
+
def nickname(self):
|
187
|
+
return self._nickname
|
188
|
+
|
189
|
+
@nickname.setter
|
190
|
+
def nickname(self, new_nickname: str):
|
191
|
+
old = self._nickname
|
192
|
+
if new_nickname == old:
|
193
|
+
return
|
194
|
+
|
195
|
+
cache = getattr(self, "_last_presence", None)
|
196
|
+
if cache:
|
197
|
+
last_seen = cache.last_seen
|
198
|
+
kwargs = cache.presence_kwargs
|
199
|
+
else:
|
200
|
+
last_seen = None
|
201
|
+
kwargs = {}
|
202
|
+
|
203
|
+
kwargs["status_codes"] = {303}
|
204
|
+
|
205
|
+
p = self._make_presence(ptype="unavailable", last_seen=last_seen, **kwargs)
|
206
|
+
# in this order so pfrom=old resource and we actually use the escaped nick
|
207
|
+
# in the muc/item/nick element
|
208
|
+
self.__update_jid(new_nickname)
|
209
|
+
p["muc"]["item"]["nick"] = self.jid.resource
|
210
|
+
self._send(p)
|
211
|
+
|
212
|
+
self._nickname = new_nickname
|
213
|
+
|
214
|
+
kwargs["status_codes"] = set()
|
215
|
+
p = self._make_presence(ptype="available", last_seen=last_seen, **kwargs)
|
216
|
+
self.__add_nick_element(p)
|
217
|
+
self._send(p)
|
218
|
+
|
219
|
+
if old:
|
220
|
+
self.muc.rename_participant(old, new_nickname)
|
221
|
+
|
222
|
+
def _make_presence(
|
223
|
+
self,
|
224
|
+
*,
|
225
|
+
last_seen: Optional[datetime] = None,
|
226
|
+
status_codes: Optional[set[int]] = None,
|
227
|
+
user_full_jid: Optional[JID] = None,
|
228
|
+
**presence_kwargs,
|
229
|
+
):
|
230
|
+
p = super()._make_presence(last_seen=last_seen, **presence_kwargs)
|
231
|
+
p["muc"]["affiliation"] = self.affiliation
|
232
|
+
p["muc"]["role"] = self.role
|
233
|
+
if self._hats:
|
234
|
+
p["hats"].add_hats(self._hats)
|
235
|
+
codes = status_codes or set()
|
236
|
+
if self.is_user:
|
237
|
+
codes.add(110)
|
238
|
+
if not self.muc.is_anonymous and not self.is_system:
|
239
|
+
if self.is_user:
|
240
|
+
if user_full_jid:
|
241
|
+
p["muc"]["jid"] = user_full_jid
|
242
|
+
else:
|
243
|
+
jid = copy(self.user.jid)
|
244
|
+
try:
|
245
|
+
jid.resource = next(
|
246
|
+
iter(self.muc.user_resources) # type:ignore
|
247
|
+
)
|
248
|
+
except StopIteration:
|
249
|
+
jid.resource = "pseudo-resource"
|
250
|
+
p["muc"]["jid"] = self.user.jid
|
251
|
+
codes.add(100)
|
252
|
+
elif self.contact:
|
253
|
+
p["muc"]["jid"] = self.contact.jid
|
254
|
+
if a := self.contact.get_avatar():
|
255
|
+
p["vcard_temp_update"]["photo"] = a.id
|
256
|
+
else:
|
257
|
+
warnings.warn(
|
258
|
+
f"Private group but no 1:1 JID associated to '{self}'",
|
259
|
+
)
|
260
|
+
if self.is_user and (hash_ := self.session.avatar_hash):
|
261
|
+
p["vcard_temp_update"]["photo"] = hash_
|
262
|
+
p["muc"]["status_codes"] = codes
|
263
|
+
return p
|
264
|
+
|
265
|
+
@property
|
266
|
+
def DISCO_NAME(self):
|
267
|
+
return self.nickname
|
268
|
+
|
269
|
+
def __send_presence_if_needed(
|
270
|
+
self, stanza: Union[Message, Presence], full_jid: JID, archive_only: bool
|
271
|
+
):
|
272
|
+
if (
|
273
|
+
archive_only
|
274
|
+
or self.is_system
|
275
|
+
or self.is_user
|
276
|
+
or self.__presence_sent
|
277
|
+
or stanza["subject"]
|
278
|
+
):
|
279
|
+
return
|
280
|
+
if isinstance(stanza, Message):
|
281
|
+
if stanza.get_plugin("muc", check=True):
|
282
|
+
return
|
283
|
+
self.send_initial_presence(full_jid)
|
284
|
+
|
285
|
+
@cached_property
|
286
|
+
def __occupant_id(self):
|
287
|
+
if self.contact:
|
288
|
+
return self.contact.jid
|
289
|
+
elif self.is_user:
|
290
|
+
return "slidge-user"
|
291
|
+
elif self.is_system:
|
292
|
+
return "room"
|
293
|
+
else:
|
294
|
+
return str(uuid.uuid4())
|
295
|
+
|
296
|
+
def _send(
|
297
|
+
self,
|
298
|
+
stanza: MessageOrPresenceTypeVar,
|
299
|
+
full_jid: Optional[JID] = None,
|
300
|
+
archive_only=False,
|
301
|
+
**send_kwargs,
|
302
|
+
) -> MessageOrPresenceTypeVar:
|
303
|
+
stanza["occupant-id"]["id"] = self.__occupant_id
|
304
|
+
if isinstance(stanza, Presence):
|
305
|
+
if stanza["type"] == "unavailable" and not self.__presence_sent:
|
306
|
+
return stanza # type:ignore
|
307
|
+
self.__presence_sent = True
|
308
|
+
if full_jid:
|
309
|
+
stanza["to"] = full_jid
|
310
|
+
self.__send_presence_if_needed(stanza, full_jid, archive_only)
|
311
|
+
if self.is_user:
|
312
|
+
assert stanza.stream is not None
|
313
|
+
stanza.stream.send(stanza, use_filters=False)
|
314
|
+
else:
|
315
|
+
stanza.send()
|
316
|
+
else:
|
317
|
+
if isinstance(stanza, Message):
|
318
|
+
self.muc.archive.add(stanza, self)
|
319
|
+
if archive_only:
|
320
|
+
return stanza
|
321
|
+
for user_full_jid in self.muc.user_full_jids():
|
322
|
+
stanza = copy(stanza)
|
323
|
+
stanza["to"] = user_full_jid
|
324
|
+
self.__send_presence_if_needed(stanza, user_full_jid, archive_only)
|
325
|
+
stanza.send()
|
326
|
+
return stanza
|
327
|
+
|
328
|
+
def mucadmin_item(self):
|
329
|
+
item = MUCAdminItem()
|
330
|
+
item["nick"] = self.nickname
|
331
|
+
item["affiliation"] = self.affiliation
|
332
|
+
item["role"] = self.role
|
333
|
+
if not self.muc.is_anonymous:
|
334
|
+
if self.is_user:
|
335
|
+
item["jid"] = self.user.bare_jid
|
336
|
+
elif self.contact:
|
337
|
+
item["jid"] = self.contact.jid.bare
|
338
|
+
else:
|
339
|
+
warnings.warn(
|
340
|
+
(
|
341
|
+
f"Public group but no contact JID associated to {self.jid} in"
|
342
|
+
f" {self}"
|
343
|
+
),
|
344
|
+
)
|
345
|
+
return item
|
346
|
+
|
347
|
+
def __add_nick_element(self, p: Presence):
|
348
|
+
if (nick := self._nickname_no_illegal) != self.jid.resource:
|
349
|
+
n = self.xmpp.plugin["xep_0172"].stanza.UserNick()
|
350
|
+
n["nick"] = nick
|
351
|
+
p.append(n)
|
352
|
+
|
353
|
+
def _get_last_presence(self) -> Optional[CachedPresence]:
|
354
|
+
own = super()._get_last_presence()
|
355
|
+
if own is None and self.contact:
|
356
|
+
return self.contact._get_last_presence()
|
357
|
+
return own
|
358
|
+
|
359
|
+
def send_initial_presence(
|
360
|
+
self,
|
361
|
+
full_jid: JID,
|
362
|
+
nick_change=False,
|
363
|
+
presence_id: Optional[str] = None,
|
364
|
+
):
|
365
|
+
"""
|
366
|
+
Called when the user joins a MUC, as a mechanism
|
367
|
+
to indicate to the joining XMPP client the list of "participants".
|
368
|
+
|
369
|
+
Can be called this to trigger a "participant has joined the group" event.
|
370
|
+
|
371
|
+
:param full_jid: Set this to only send to a specific user XMPP resource.
|
372
|
+
:param nick_change: Used when the user joins and the MUC renames them (code 210)
|
373
|
+
:param presence_id: set the presence ID. used internally by slidge
|
374
|
+
"""
|
375
|
+
# MUC status codes: https://xmpp.org/extensions/xep-0045.html#registrar-statuscodes
|
376
|
+
codes = set()
|
377
|
+
if nick_change:
|
378
|
+
codes.add(210)
|
379
|
+
|
380
|
+
if self.is_user:
|
381
|
+
# the "initial presence" of the user has to be vanilla, as it is
|
382
|
+
# a crucial part of the MUC join sequence for XMPP clients.
|
383
|
+
kwargs = {}
|
384
|
+
else:
|
385
|
+
cache = self._get_last_presence()
|
386
|
+
self.log.debug("Join muc, initial presence: %s", cache)
|
387
|
+
if cache:
|
388
|
+
ptype = cache.ptype
|
389
|
+
if ptype == "unavailable":
|
390
|
+
return
|
391
|
+
kwargs = dict(
|
392
|
+
last_seen=cache.last_seen, pstatus=cache.pstatus, pshow=cache.pshow
|
393
|
+
)
|
394
|
+
else:
|
395
|
+
kwargs = {}
|
396
|
+
p = self._make_presence(
|
397
|
+
status_codes=codes,
|
398
|
+
user_full_jid=full_jid,
|
399
|
+
**kwargs, # type:ignore
|
400
|
+
)
|
401
|
+
if presence_id:
|
402
|
+
p["id"] = presence_id
|
403
|
+
self.__add_nick_element(p)
|
404
|
+
self._send(p, full_jid)
|
405
|
+
|
406
|
+
def leave(self):
|
407
|
+
"""
|
408
|
+
Call this when the participant leaves the room
|
409
|
+
"""
|
410
|
+
self.muc.remove_participant(self)
|
411
|
+
|
412
|
+
def kick(self):
|
413
|
+
"""
|
414
|
+
Call this when the participant is kicked from the room
|
415
|
+
"""
|
416
|
+
self.muc.remove_participant(self, kick=True)
|
417
|
+
|
418
|
+
def ban(self):
|
419
|
+
"""
|
420
|
+
Call this when the participant is banned from the room
|
421
|
+
"""
|
422
|
+
self.muc.remove_participant(self, ban=True)
|
423
|
+
|
424
|
+
def get_disco_info(self, jid: OptJid = None, node: Optional[str] = None):
|
425
|
+
if self.contact is not None:
|
426
|
+
return self.contact.get_disco_info()
|
427
|
+
return super().get_disco_info()
|
428
|
+
|
429
|
+
def moderate(self, legacy_msg_id: LegacyMessageType, reason: Optional[str] = None):
|
430
|
+
m = self.muc.get_system_participant()._make_message()
|
431
|
+
m["apply_to"]["id"] = self._legacy_to_xmpp(legacy_msg_id)
|
432
|
+
m["apply_to"]["moderated"].enable("retract")
|
433
|
+
m["apply_to"]["moderated"]["by"] = self.jid
|
434
|
+
if reason:
|
435
|
+
m["apply_to"]["moderated"]["reason"] = reason
|
436
|
+
self._send(m)
|
437
|
+
|
438
|
+
def set_room_subject(
|
439
|
+
self,
|
440
|
+
subject: str,
|
441
|
+
full_jid: Optional[JID] = None,
|
442
|
+
when: Optional[datetime] = None,
|
443
|
+
update_muc=True,
|
444
|
+
):
|
445
|
+
if update_muc:
|
446
|
+
self.muc._subject = subject # type: ignore
|
447
|
+
self.muc.subject_setter = self
|
448
|
+
self.muc.subject_date = when
|
449
|
+
|
450
|
+
msg = self._make_message()
|
451
|
+
if when is not None:
|
452
|
+
msg["delay"].set_stamp(when)
|
453
|
+
msg["delay"]["from"] = self.muc.jid
|
454
|
+
msg["subject"] = subject or str(self.muc.name)
|
455
|
+
self._send(msg, full_jid)
|
456
|
+
|
457
|
+
|
458
|
+
log = logging.getLogger(__name__)
|