slidge 0.2.0a9__py3-none-any.whl → 0.2.0b0__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/__main__.py +2 -3
- slidge/__version__.py +1 -1
- slidge/command/adhoc.py +1 -1
- slidge/command/base.py +4 -4
- slidge/command/user.py +5 -1
- slidge/contact/roster.py +9 -0
- slidge/core/config.py +0 -3
- slidge/core/dispatcher/__init__.py +3 -0
- slidge/core/{gateway → dispatcher}/caps.py +6 -4
- slidge/core/{gateway → dispatcher}/disco.py +11 -17
- slidge/core/dispatcher/message/__init__.py +10 -0
- slidge/core/dispatcher/message/chat_state.py +40 -0
- slidge/core/dispatcher/message/marker.py +62 -0
- slidge/core/dispatcher/message/message.py +397 -0
- slidge/core/dispatcher/muc/__init__.py +12 -0
- slidge/core/dispatcher/muc/admin.py +98 -0
- slidge/core/{gateway → dispatcher/muc}/mam.py +26 -15
- slidge/core/dispatcher/muc/misc.py +121 -0
- slidge/core/dispatcher/muc/owner.py +96 -0
- slidge/core/{gateway → dispatcher/muc}/ping.py +10 -15
- slidge/core/dispatcher/presence.py +177 -0
- slidge/core/{gateway → dispatcher}/registration.py +23 -2
- slidge/core/{gateway → dispatcher}/search.py +9 -14
- slidge/core/dispatcher/session_dispatcher.py +84 -0
- slidge/core/dispatcher/util.py +174 -0
- slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +26 -12
- slidge/core/{gateway/base.py → gateway.py} +42 -137
- slidge/core/mixins/attachment.py +24 -8
- slidge/core/mixins/base.py +2 -2
- slidge/core/mixins/lock.py +10 -8
- slidge/core/mixins/message.py +9 -203
- slidge/core/mixins/message_text.py +211 -0
- slidge/core/pubsub.py +2 -1
- slidge/core/session.py +28 -2
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +83 -0
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +12 -1
- slidge/db/models.py +16 -1
- slidge/db/store.py +144 -11
- slidge/group/bookmarks.py +23 -1
- slidge/group/participant.py +5 -5
- slidge/group/room.py +10 -1
- slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
- slidge/util/test.py +9 -5
- slidge/util/types.py +6 -0
- slidge/util/util.py +5 -2
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/METADATA +2 -1
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/RECORD +51 -40
- slidge-0.2.0b0.dist-info/entry_points.txt +3 -0
- slidge/core/gateway/__init__.py +0 -3
- slidge/core/gateway/muc_admin.py +0 -35
- slidge/core/gateway/presence.py +0 -95
- slidge/core/gateway/session_dispatcher.py +0 -895
- slidge-0.2.0a9.dist-info/entry_points.txt +0 -3
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/LICENSE +0 -0
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
import logging
|
2
|
+
from functools import wraps
|
3
|
+
from typing import TYPE_CHECKING, Any, Awaitable, Callable, TypeVar
|
4
|
+
|
5
|
+
from slixmpp import JID, Iq, Message, Presence
|
6
|
+
from slixmpp.exceptions import XMPPError
|
7
|
+
from slixmpp.xmlstream import StanzaBase
|
8
|
+
|
9
|
+
from ...util.types import Recipient, RecipientType
|
10
|
+
from ..session import BaseSession
|
11
|
+
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from slidge import BaseGateway
|
14
|
+
from slidge.group import LegacyMUC
|
15
|
+
|
16
|
+
|
17
|
+
class Ignore(BaseException):
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class DispatcherMixin:
|
22
|
+
def __init__(self, xmpp: "BaseGateway"):
|
23
|
+
self.xmpp = xmpp
|
24
|
+
|
25
|
+
async def _get_session(
|
26
|
+
self,
|
27
|
+
stanza: Message | Presence | Iq,
|
28
|
+
timeout: int | None = 10,
|
29
|
+
wait_for_ready=True,
|
30
|
+
logged=False,
|
31
|
+
) -> BaseSession:
|
32
|
+
xmpp = self.xmpp
|
33
|
+
if stanza.get_from().server == xmpp.boundjid.bare:
|
34
|
+
log.debug("Ignoring echo")
|
35
|
+
raise Ignore
|
36
|
+
if (
|
37
|
+
isinstance(stanza, Message)
|
38
|
+
and stanza.get_type() == "chat"
|
39
|
+
and stanza.get_to() == xmpp.boundjid.bare
|
40
|
+
):
|
41
|
+
log.debug("Ignoring message to component")
|
42
|
+
raise Ignore
|
43
|
+
session = await self._get_session_from_jid(
|
44
|
+
stanza.get_from(), timeout, wait_for_ready, logged
|
45
|
+
)
|
46
|
+
if isinstance(stanza, Message) and _ignore(session, stanza):
|
47
|
+
raise Ignore
|
48
|
+
return session
|
49
|
+
|
50
|
+
async def _get_session_from_jid(
|
51
|
+
self,
|
52
|
+
jid: JID,
|
53
|
+
timeout: int | None = 10,
|
54
|
+
wait_for_ready=True,
|
55
|
+
logged=False,
|
56
|
+
) -> BaseSession:
|
57
|
+
session = self.xmpp.get_session_from_jid(jid)
|
58
|
+
if session is None:
|
59
|
+
raise XMPPError("registration-required")
|
60
|
+
if logged:
|
61
|
+
session.raise_if_not_logged()
|
62
|
+
if wait_for_ready:
|
63
|
+
await session.wait_for_ready(timeout)
|
64
|
+
return session
|
65
|
+
|
66
|
+
async def get_muc_from_stanza(self, iq: Iq | Message | Presence) -> "LegacyMUC":
|
67
|
+
ito = iq.get_to()
|
68
|
+
if ito == self.xmpp.boundjid.bare:
|
69
|
+
raise XMPPError("bad-request", text="This is only handled for MUCs")
|
70
|
+
|
71
|
+
session = await self._get_session(iq, logged=True)
|
72
|
+
muc = await session.bookmarks.by_jid(ito)
|
73
|
+
return muc
|
74
|
+
|
75
|
+
def _xmpp_msg_id_to_legacy(self, session: "BaseSession", xmpp_id: str):
|
76
|
+
sent = self.xmpp.store.sent.get_legacy_id(session.user_pk, xmpp_id)
|
77
|
+
if sent is not None:
|
78
|
+
return self.xmpp.LEGACY_MSG_ID_TYPE(sent)
|
79
|
+
|
80
|
+
multi = self.xmpp.store.multi.get_legacy_id(session.user_pk, xmpp_id)
|
81
|
+
if multi:
|
82
|
+
return self.xmpp.LEGACY_MSG_ID_TYPE(multi)
|
83
|
+
|
84
|
+
try:
|
85
|
+
return session.xmpp_to_legacy_msg_id(xmpp_id)
|
86
|
+
except XMPPError:
|
87
|
+
raise
|
88
|
+
except Exception as e:
|
89
|
+
log.debug("Couldn't convert xmpp msg ID to legacy ID.", exc_info=e)
|
90
|
+
raise XMPPError(
|
91
|
+
"internal-server-error", "Couldn't convert xmpp msg ID to legacy ID."
|
92
|
+
)
|
93
|
+
|
94
|
+
async def _get_session_entity_thread(
|
95
|
+
self, msg: Message
|
96
|
+
) -> tuple["BaseSession", Recipient, int | str]:
|
97
|
+
session = await self._get_session(msg)
|
98
|
+
e: Recipient = await _get_entity(session, msg)
|
99
|
+
legacy_thread = await self._xmpp_to_legacy_thread(session, msg, e)
|
100
|
+
return session, e, legacy_thread
|
101
|
+
|
102
|
+
async def _xmpp_to_legacy_thread(
|
103
|
+
self, session: "BaseSession", msg: Message, recipient: RecipientType
|
104
|
+
):
|
105
|
+
xmpp_thread = msg["thread"]
|
106
|
+
if not xmpp_thread:
|
107
|
+
return None
|
108
|
+
|
109
|
+
if session.MESSAGE_IDS_ARE_THREAD_IDS:
|
110
|
+
return self._xmpp_msg_id_to_legacy(session, xmpp_thread)
|
111
|
+
|
112
|
+
legacy_thread_str = session.xmpp.store.sent.get_legacy_thread(
|
113
|
+
session.user_pk, xmpp_thread
|
114
|
+
)
|
115
|
+
if legacy_thread_str is not None:
|
116
|
+
return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread_str)
|
117
|
+
async with session.thread_creation_lock:
|
118
|
+
legacy_thread = await recipient.create_thread(xmpp_thread)
|
119
|
+
session.xmpp.store.sent.set_thread(
|
120
|
+
session.user_pk, str(legacy_thread), xmpp_thread
|
121
|
+
)
|
122
|
+
return legacy_thread
|
123
|
+
|
124
|
+
|
125
|
+
def _ignore(session: "BaseSession", msg: Message):
|
126
|
+
i = msg.get_id()
|
127
|
+
if i.startswith("slidge-carbon-"):
|
128
|
+
return True
|
129
|
+
if i not in session.ignore_messages:
|
130
|
+
return False
|
131
|
+
session.log.debug("Ignored sent carbon: %s", i)
|
132
|
+
session.ignore_messages.remove(i)
|
133
|
+
return True
|
134
|
+
|
135
|
+
|
136
|
+
async def _get_entity(session: "BaseSession", m: Message) -> RecipientType:
|
137
|
+
session.raise_if_not_logged()
|
138
|
+
if m.get_type() == "groupchat":
|
139
|
+
muc = await session.bookmarks.by_jid(m.get_to())
|
140
|
+
r = m.get_from().resource
|
141
|
+
if r not in muc.get_user_resources():
|
142
|
+
session.create_task(muc.kick_resource(r))
|
143
|
+
raise XMPPError("not-acceptable", "You are not connected to this chat")
|
144
|
+
return muc
|
145
|
+
else:
|
146
|
+
return await session.contacts.by_jid(m.get_to())
|
147
|
+
|
148
|
+
|
149
|
+
StanzaType = TypeVar("StanzaType", bound=StanzaBase)
|
150
|
+
HandlerType = Callable[[Any, StanzaType], Awaitable[None]]
|
151
|
+
|
152
|
+
|
153
|
+
def exceptions_to_xmpp_errors(cb: HandlerType) -> HandlerType:
|
154
|
+
@wraps(cb)
|
155
|
+
async def wrapped(*args):
|
156
|
+
try:
|
157
|
+
await cb(*args)
|
158
|
+
except Ignore:
|
159
|
+
pass
|
160
|
+
except XMPPError:
|
161
|
+
raise
|
162
|
+
except NotImplementedError:
|
163
|
+
log.debug("NotImplementedError raised in %s", cb)
|
164
|
+
raise XMPPError(
|
165
|
+
"feature-not-implemented", "Not implemented by the legacy module"
|
166
|
+
)
|
167
|
+
except Exception as e:
|
168
|
+
log.error("Failed to handle incoming stanza: %s", args, exc_info=e)
|
169
|
+
raise XMPPError("internal-server-error", str(e))
|
170
|
+
|
171
|
+
return wrapped
|
172
|
+
|
173
|
+
|
174
|
+
log = logging.getLogger(__name__)
|
@@ -1,5 +1,4 @@
|
|
1
1
|
from copy import copy
|
2
|
-
from typing import TYPE_CHECKING
|
3
2
|
|
4
3
|
from slixmpp import CoroutineCallback, Iq, StanzaPath, register_stanza_plugin
|
5
4
|
from slixmpp.exceptions import XMPPError
|
@@ -9,21 +8,23 @@ from slixmpp.plugins.xep_0292.stanza import NS as VCard4NS
|
|
9
8
|
from ...contact import LegacyContact
|
10
9
|
from ...core.session import BaseSession
|
11
10
|
from ...group import LegacyParticipant
|
11
|
+
from .util import DispatcherMixin, exceptions_to_xmpp_errors
|
12
12
|
|
13
|
-
if TYPE_CHECKING:
|
14
|
-
from .base import BaseGateway
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
class VCardMixin(DispatcherMixin):
|
15
|
+
def __init__(self, xmpp):
|
16
|
+
super().__init__(xmpp)
|
17
|
+
xmpp.register_handler(
|
18
|
+
CoroutineCallback(
|
19
|
+
"get_vcard", StanzaPath("iq@type=get/vcard"), self.on_get_vcard
|
20
|
+
)
|
21
|
+
)
|
22
|
+
xmpp.remove_handler("VCardTemp")
|
22
23
|
xmpp.register_handler(
|
23
24
|
CoroutineCallback(
|
24
25
|
"VCardTemp",
|
25
26
|
StanzaPath("iq/vcard_temp"),
|
26
|
-
self.
|
27
|
+
self.__vcard_temp_handler,
|
27
28
|
)
|
28
29
|
)
|
29
30
|
# TODO: MR to slixmpp adding this to XEP-0084
|
@@ -32,7 +33,20 @@ class VCardTemp:
|
|
32
33
|
MetaData,
|
33
34
|
)
|
34
35
|
|
35
|
-
|
36
|
+
@exceptions_to_xmpp_errors
|
37
|
+
async def on_get_vcard(self, iq: Iq):
|
38
|
+
session = await self._get_session(iq, logged=True)
|
39
|
+
contact = await session.contacts.by_jid(iq.get_to())
|
40
|
+
vcard = await contact.get_vcard()
|
41
|
+
reply = iq.reply()
|
42
|
+
if vcard:
|
43
|
+
reply.append(vcard)
|
44
|
+
else:
|
45
|
+
reply.enable("vcard")
|
46
|
+
reply.send()
|
47
|
+
|
48
|
+
@exceptions_to_xmpp_errors
|
49
|
+
async def __vcard_temp_handler(self, iq: Iq):
|
36
50
|
if iq["type"] == "get":
|
37
51
|
return await self.__handle_get_vcard_temp(iq)
|
38
52
|
|
@@ -105,7 +119,7 @@ class VCardTemp:
|
|
105
119
|
reply.send()
|
106
120
|
|
107
121
|
async def __handle_set_vcard_temp(self, iq: Iq):
|
108
|
-
muc = await self.
|
122
|
+
muc = await self.get_muc_from_stanza(iq)
|
109
123
|
to = iq.get_to()
|
110
124
|
|
111
125
|
if to.resource:
|
@@ -8,16 +8,7 @@ import re
|
|
8
8
|
import tempfile
|
9
9
|
from copy import copy
|
10
10
|
from datetime import datetime
|
11
|
-
from typing import
|
12
|
-
TYPE_CHECKING,
|
13
|
-
Any,
|
14
|
-
Callable,
|
15
|
-
Collection,
|
16
|
-
Mapping,
|
17
|
-
Optional,
|
18
|
-
Sequence,
|
19
|
-
Union,
|
20
|
-
)
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, Mapping, Optional, Sequence, Union
|
21
12
|
|
22
13
|
import aiohttp
|
23
14
|
import qrcode
|
@@ -27,40 +18,30 @@ from slixmpp.plugins.xep_0060.stanza import OwnerAffiliation
|
|
27
18
|
from slixmpp.types import MessageTypes
|
28
19
|
from slixmpp.xmlstream.xmlstream import NotConnectedError
|
29
20
|
|
30
|
-
from
|
31
|
-
from
|
32
|
-
from
|
33
|
-
from
|
34
|
-
from
|
35
|
-
from
|
36
|
-
from
|
37
|
-
from
|
38
|
-
from
|
39
|
-
from
|
40
|
-
from
|
41
|
-
from
|
42
|
-
from
|
43
|
-
from
|
44
|
-
from
|
45
|
-
from
|
46
|
-
from .
|
47
|
-
from .
|
48
|
-
from .disco import Disco
|
49
|
-
from .mam import Mam
|
50
|
-
from .muc_admin import MucAdmin
|
51
|
-
from .ping import Ping
|
52
|
-
from .presence import PresenceHandlerMixin
|
53
|
-
from .registration import Registration
|
54
|
-
from .search import Search
|
55
|
-
from .session_dispatcher import SessionDispatcher
|
56
|
-
from .vcard_temp import VCardTemp
|
21
|
+
from slidge import command # noqa: F401
|
22
|
+
from slidge.command.adhoc import AdhocProvider
|
23
|
+
from slidge.command.admin import Exec
|
24
|
+
from slidge.command.base import Command, FormField
|
25
|
+
from slidge.command.chat_command import ChatCommandProvider
|
26
|
+
from slidge.command.register import RegistrationType
|
27
|
+
from slidge.core import config
|
28
|
+
from slidge.core.dispatcher.session_dispatcher import SessionDispatcher
|
29
|
+
from slidge.core.mixins import MessageMixin
|
30
|
+
from slidge.core.pubsub import PubSubComponent
|
31
|
+
from slidge.core.session import BaseSession
|
32
|
+
from slidge.db import GatewayUser, SlidgeStore
|
33
|
+
from slidge.db.avatar import avatar_cache
|
34
|
+
from slidge.slixfix.delivery_receipt import DeliveryReceipt
|
35
|
+
from slidge.slixfix.roster import RosterBackend
|
36
|
+
from slidge.util import ABCSubclassableOnceAtMost
|
37
|
+
from slidge.util.types import AvatarType, MessageOrPresenceTypeVar
|
38
|
+
from slidge.util.util import timeit
|
57
39
|
|
58
40
|
if TYPE_CHECKING:
|
59
|
-
|
41
|
+
pass
|
60
42
|
|
61
43
|
|
62
44
|
class BaseGateway(
|
63
|
-
PresenceHandlerMixin,
|
64
45
|
ComponentXMPP,
|
65
46
|
MessageMixin,
|
66
47
|
metaclass=ABCSubclassableOnceAtMost,
|
@@ -118,7 +99,7 @@ class BaseGateway(
|
|
118
99
|
Path, bytes or URL used by the component as an avatar.
|
119
100
|
"""
|
120
101
|
|
121
|
-
REGISTRATION_FIELDS:
|
102
|
+
REGISTRATION_FIELDS: Sequence[FormField] = [
|
122
103
|
FormField(var="username", label="User name", required=True),
|
123
104
|
FormField(var="password", label="Password", required=True, private=True),
|
124
105
|
]
|
@@ -315,7 +296,7 @@ class BaseGateway(
|
|
315
296
|
self.session_cls: BaseSession = BaseSession.get_unique_subclass()
|
316
297
|
self.session_cls.xmpp = self
|
317
298
|
|
318
|
-
from
|
299
|
+
from ..group.room import LegacyMUC
|
319
300
|
|
320
301
|
LegacyMUC.get_self_or_unique_subclass().xmpp = self
|
321
302
|
|
@@ -328,6 +309,7 @@ class BaseGateway(
|
|
328
309
|
|
329
310
|
self.register_plugins()
|
330
311
|
self.__register_slixmpp_events()
|
312
|
+
self.__register_slixmpp_api()
|
331
313
|
self.roster.set_backend(RosterBackend(self))
|
332
314
|
|
333
315
|
self.register_plugin("pubsub", {"component_name": self.COMPONENT_NAME})
|
@@ -355,21 +337,11 @@ class BaseGateway(
|
|
355
337
|
# why does mypy need these type annotations? no idea
|
356
338
|
self.__adhoc_handler: AdhocProvider = AdhocProvider(self)
|
357
339
|
self.__chat_commands_handler: ChatCommandProvider = ChatCommandProvider(self)
|
358
|
-
|
359
|
-
|
360
|
-
self.__ping_handler = Ping(self)
|
361
|
-
self.__mam_handler = Mam(self)
|
362
|
-
self.__search_handler = Search(self)
|
363
|
-
self.__caps_handler = Caps(self)
|
364
|
-
self.__vcard_temp_handler = VCardTemp(self)
|
365
|
-
self.__muc_admin_handler = MucAdmin(self)
|
366
|
-
self.__registration = Registration(self)
|
340
|
+
|
367
341
|
self.__dispatcher = SessionDispatcher(self)
|
368
342
|
|
369
343
|
self.__register_commands()
|
370
344
|
|
371
|
-
self.__mam_cleanup_task = self.loop.create_task(self.__mam_cleanup())
|
372
|
-
|
373
345
|
MessageMixin.__init__(self) # ComponentXMPP does not call super().__init__()
|
374
346
|
|
375
347
|
async def __set_http(self):
|
@@ -378,13 +350,6 @@ class BaseGateway(
|
|
378
350
|
return
|
379
351
|
avatar_cache.http = self.http
|
380
352
|
|
381
|
-
async def __mam_cleanup(self):
|
382
|
-
if not config.MAM_MAX_DAYS:
|
383
|
-
return
|
384
|
-
while True:
|
385
|
-
await asyncio.sleep(3600 * 6)
|
386
|
-
self.store.mam.nuke_older_than(config.MAM_MAX_DAYS)
|
387
|
-
|
388
353
|
def __register_commands(self):
|
389
354
|
for cls in Command.subclasses:
|
390
355
|
if any(x is NotImplemented for x in [cls.CHAT_COMMAND, cls.NODE, cls.NAME]):
|
@@ -421,40 +386,27 @@ class BaseGateway(
|
|
421
386
|
loop.stop()
|
422
387
|
|
423
388
|
def __register_slixmpp_events(self):
|
389
|
+
self.del_event_handler("presence_subscribe", self._handle_subscribe)
|
390
|
+
self.del_event_handler("presence_unsubscribe", self._handle_unsubscribe)
|
391
|
+
self.del_event_handler("presence_subscribed", self._handle_subscribed)
|
392
|
+
self.del_event_handler("presence_unsubscribed", self._handle_unsubscribed)
|
393
|
+
self.del_event_handler(
|
394
|
+
"roster_subscription_request", self._handle_new_subscription
|
395
|
+
)
|
396
|
+
self.del_event_handler("presence_probe", self._handle_probe)
|
424
397
|
self.add_event_handler("session_start", self.__on_session_start)
|
425
398
|
self.add_event_handler("disconnected", self.connect)
|
426
|
-
|
427
|
-
|
428
|
-
self.
|
399
|
+
|
400
|
+
def __register_slixmpp_api(self) -> None:
|
401
|
+
self.plugin["xep_0231"].api.register(self.store.bob.get_bob, "get_bob")
|
402
|
+
self.plugin["xep_0231"].api.register(self.store.bob.set_bob, "set_bob")
|
403
|
+
self.plugin["xep_0231"].api.register(self.store.bob.del_bob, "del_bob")
|
429
404
|
|
430
405
|
@property # type: ignore
|
431
406
|
def jid(self):
|
432
407
|
# Override to avoid slixmpp deprecation warnings.
|
433
408
|
return self.boundjid
|
434
409
|
|
435
|
-
async def __on_group_chat_error(self, msg: Message):
|
436
|
-
condition = msg["error"].get_condition()
|
437
|
-
if condition not in KICKABLE_ERRORS:
|
438
|
-
return
|
439
|
-
|
440
|
-
try:
|
441
|
-
muc = await self.get_muc_from_stanza(msg)
|
442
|
-
except XMPPError as e:
|
443
|
-
log.debug("Not removing resource", exc_info=e)
|
444
|
-
return
|
445
|
-
mfrom = msg.get_from()
|
446
|
-
resource = mfrom.resource
|
447
|
-
try:
|
448
|
-
muc.remove_user_resource(resource)
|
449
|
-
except KeyError:
|
450
|
-
# this actually happens quite frequently on for both beagle and monal
|
451
|
-
# (not sure why?), but is of no consequence
|
452
|
-
log.debug("%s was not in the resources of %s", resource, muc)
|
453
|
-
else:
|
454
|
-
log.info(
|
455
|
-
"Removed %s from the resources of %s because of error", resource, muc
|
456
|
-
)
|
457
|
-
|
458
410
|
async def __on_session_start(self, event):
|
459
411
|
log.debug("Gateway session start: %s", event)
|
460
412
|
|
@@ -490,7 +442,7 @@ class BaseGateway(
|
|
490
442
|
pto=user.jid.bare, ptype="probe"
|
491
443
|
) # ensure we get all resources for user
|
492
444
|
session = self.session_cls.from_user(user)
|
493
|
-
session.create_task(self.
|
445
|
+
session.create_task(self.login_wrap(session))
|
494
446
|
if cached_avatar is not None:
|
495
447
|
await self.pubsub.broadcast_avatar(
|
496
448
|
self.boundjid.bare, session.user_jid, cached_avatar
|
@@ -551,7 +503,7 @@ class BaseGateway(
|
|
551
503
|
)
|
552
504
|
|
553
505
|
@timeit
|
554
|
-
async def
|
506
|
+
async def login_wrap(self, session: "BaseSession"):
|
555
507
|
session.send_gateway_status("Logging in…", show="dnd")
|
556
508
|
try:
|
557
509
|
status = await session.login()
|
@@ -612,21 +564,6 @@ class BaseGateway(
|
|
612
564
|
stanza.send()
|
613
565
|
return stanza
|
614
566
|
|
615
|
-
async def _on_user_register(self, iq: Iq):
|
616
|
-
session = self.get_session_from_stanza(iq)
|
617
|
-
for jid in config.ADMINS:
|
618
|
-
self.send_message(
|
619
|
-
mto=jid,
|
620
|
-
mbody=f"{iq.get_from()} has registered",
|
621
|
-
mtype="chat",
|
622
|
-
mfrom=self.boundjid.bare,
|
623
|
-
)
|
624
|
-
session.send_gateway_message(self.WELCOME_MESSAGE)
|
625
|
-
await self.__login_wrap(session)
|
626
|
-
|
627
|
-
async def _on_user_unregister(self, iq: Iq):
|
628
|
-
await self.session_cls.kill_by_jid(iq.get_from())
|
629
|
-
|
630
567
|
def raise_if_not_allowed_jid(self, jid: JID):
|
631
568
|
if not self.jid_validator.match(jid.bare):
|
632
569
|
raise XMPPError(
|
@@ -665,26 +602,6 @@ class BaseGateway(
|
|
665
602
|
except XMPPError:
|
666
603
|
pass
|
667
604
|
|
668
|
-
async def get_muc_from_stanza(self, iq: Union[Iq, Message]) -> "LegacyMUC":
|
669
|
-
ito = iq.get_to()
|
670
|
-
|
671
|
-
if ito == self.boundjid.bare:
|
672
|
-
raise XMPPError(
|
673
|
-
text="No MAM on the component itself, use a JID with a resource"
|
674
|
-
)
|
675
|
-
|
676
|
-
ifrom = iq.get_from()
|
677
|
-
user = self.store.users.get(ifrom)
|
678
|
-
if user is None:
|
679
|
-
raise XMPPError("registration-required")
|
680
|
-
|
681
|
-
session = self.get_session_from_user(user)
|
682
|
-
session.raise_if_not_logged()
|
683
|
-
|
684
|
-
muc = await session.bookmarks.by_jid(ito)
|
685
|
-
|
686
|
-
return muc
|
687
|
-
|
688
605
|
def exception(self, exception: Exception):
|
689
606
|
# """
|
690
607
|
# Called when a task created by slixmpp's internal (eg, on slix events) raises an Exception.
|
@@ -719,7 +636,7 @@ class BaseGateway(
|
|
719
636
|
async def w():
|
720
637
|
session.cancel_all_tasks()
|
721
638
|
await session.logout()
|
722
|
-
await self.
|
639
|
+
await self.login_wrap(session)
|
723
640
|
|
724
641
|
session.create_task(w())
|
725
642
|
|
@@ -949,20 +866,6 @@ class BaseGateway(
|
|
949
866
|
return tasks
|
950
867
|
|
951
868
|
|
952
|
-
KICKABLE_ERRORS = {
|
953
|
-
"gone",
|
954
|
-
"internal-server-error",
|
955
|
-
"item-not-found",
|
956
|
-
"jid-malformed",
|
957
|
-
"recipient-unavailable",
|
958
|
-
"redirect",
|
959
|
-
"remote-server-not-found",
|
960
|
-
"remote-server-timeout",
|
961
|
-
"service-unavailable",
|
962
|
-
"malformed error",
|
963
|
-
}
|
964
|
-
|
965
|
-
|
966
869
|
SLIXMPP_PLUGINS = [
|
967
870
|
"link_preview", # https://wiki.soprani.ca/CheogramApp/LinkPreviews
|
968
871
|
"xep_0030", # Service discovery
|
@@ -972,6 +875,7 @@ SLIXMPP_PLUGINS = [
|
|
972
875
|
"xep_0055", # Jabber search
|
973
876
|
"xep_0059", # Result Set Management
|
974
877
|
"xep_0066", # Out of Band Data
|
878
|
+
"xep_0071", # XHTML-IM (for stickers and custom emojis maybe later)
|
975
879
|
"xep_0077", # In-band registration
|
976
880
|
"xep_0084", # User Avatar
|
977
881
|
"xep_0085", # Chat state notifications
|
@@ -984,6 +888,7 @@ SLIXMPP_PLUGINS = [
|
|
984
888
|
"xep_0184", # Message Delivery Receipts
|
985
889
|
"xep_0199", # XMPP Ping
|
986
890
|
"xep_0221", # Data Forms Media Element
|
891
|
+
"xep_0231", # Bits of Binary (for stickers and custom emojis maybe later)
|
987
892
|
"xep_0249", # Direct MUC Invitations
|
988
893
|
"xep_0264", # Jingle Content Thumbnails
|
989
894
|
"xep_0280", # Carbons
|
slidge/core/mixins/attachment.py
CHANGED
@@ -33,17 +33,14 @@ from ...util.types import (
|
|
33
33
|
)
|
34
34
|
from ...util.util import fix_suffix
|
35
35
|
from .. import config
|
36
|
-
from .
|
36
|
+
from .message_text import TextMessageMixin
|
37
37
|
|
38
38
|
|
39
|
-
class AttachmentMixin(
|
39
|
+
class AttachmentMixin(TextMessageMixin):
|
40
40
|
def __init__(self, *a, **kw):
|
41
41
|
super().__init__(*a, **kw)
|
42
42
|
self.__store = self.xmpp.store.attachments
|
43
43
|
|
44
|
-
def send_text(self, *_, **k) -> Optional[Message]:
|
45
|
-
raise NotImplementedError
|
46
|
-
|
47
44
|
async def __upload(
|
48
45
|
self,
|
49
46
|
file_path: Path,
|
@@ -261,7 +258,7 @@ class AttachmentMixin(MessageMaker):
|
|
261
258
|
thumbnail["width"] = x
|
262
259
|
thumbnail["height"] = y
|
263
260
|
thumbnail["media-type"] = "image/thumbhash"
|
264
|
-
thumbnail["uri"] = "data:image/thumbhash," + urlquote(h)
|
261
|
+
thumbnail["uri"] = "data:image/thumbhash;base64," + urlquote(h)
|
265
262
|
|
266
263
|
self.__store.set_sims(uploaded_url, str(ref))
|
267
264
|
|
@@ -304,6 +301,7 @@ class AttachmentMixin(MessageMaker):
|
|
304
301
|
caption: Optional[str] = None,
|
305
302
|
carbon=False,
|
306
303
|
when: Optional[datetime] = None,
|
304
|
+
correction=False,
|
307
305
|
**kwargs,
|
308
306
|
) -> list[Message]:
|
309
307
|
msg["oob"]["url"] = uploaded_url
|
@@ -311,11 +309,19 @@ class AttachmentMixin(MessageMaker):
|
|
311
309
|
if caption:
|
312
310
|
m1 = self._send(msg, carbon=carbon, **kwargs)
|
313
311
|
m2 = self.send_text(
|
314
|
-
caption,
|
312
|
+
caption,
|
313
|
+
legacy_msg_id=legacy_msg_id,
|
314
|
+
when=when,
|
315
|
+
carbon=carbon,
|
316
|
+
correction=correction,
|
317
|
+
**kwargs,
|
315
318
|
)
|
316
319
|
return [m1, m2] if m2 else [m1]
|
317
320
|
else:
|
318
|
-
|
321
|
+
if correction:
|
322
|
+
msg["replace"]["id"] = self._replace_id(legacy_msg_id)
|
323
|
+
else:
|
324
|
+
self._set_msg_id(msg, legacy_msg_id)
|
319
325
|
return [self._send(msg, carbon=carbon, **kwargs)]
|
320
326
|
|
321
327
|
async def send_file(
|
@@ -358,6 +364,16 @@ class AttachmentMixin(MessageMaker):
|
|
358
364
|
carbon = kwargs.pop("carbon", False)
|
359
365
|
mto = kwargs.pop("mto", None)
|
360
366
|
store_multi = kwargs.pop("store_multi", True)
|
367
|
+
correction = kwargs.get("correction", False)
|
368
|
+
if correction and (original_xmpp_id := self._legacy_to_xmpp(legacy_msg_id)):
|
369
|
+
xmpp_ids = self.xmpp.store.multi.get_xmpp_ids(
|
370
|
+
self.session.user_pk, original_xmpp_id
|
371
|
+
)
|
372
|
+
|
373
|
+
for xmpp_id in xmpp_ids:
|
374
|
+
if xmpp_id == original_xmpp_id:
|
375
|
+
continue
|
376
|
+
self.retract(xmpp_id, thread)
|
361
377
|
msg = self._make_message(
|
362
378
|
when=when,
|
363
379
|
reply_to=reply_to,
|
slidge/core/mixins/base.py
CHANGED
@@ -6,8 +6,8 @@ from slixmpp import JID
|
|
6
6
|
from ...util.types import MessageOrPresenceTypeVar
|
7
7
|
|
8
8
|
if TYPE_CHECKING:
|
9
|
-
from
|
10
|
-
from
|
9
|
+
from ..gateway import BaseGateway
|
10
|
+
from ..session import BaseSession
|
11
11
|
|
12
12
|
|
13
13
|
class MetaBase(ABCMeta):
|
slidge/core/mixins/lock.py
CHANGED
@@ -15,14 +15,16 @@ class NamedLockMixin:
|
|
15
15
|
locks = self.__locks
|
16
16
|
if not locks.get(id_):
|
17
17
|
locks[id_] = asyncio.Lock()
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
try:
|
19
|
+
async with locks[id_]:
|
20
|
+
log.trace("acquired %s", id_) # type:ignore
|
21
|
+
yield
|
22
|
+
finally:
|
23
|
+
log.trace("releasing %s", id_) # type:ignore
|
24
|
+
waiters = locks[id_]._waiters # type:ignore
|
25
|
+
if not waiters:
|
26
|
+
del locks[id_]
|
27
|
+
log.trace("erasing %s", id_) # type:ignore
|
26
28
|
|
27
29
|
def get_lock(self, id_: Hashable):
|
28
30
|
return self.__locks.get(id_)
|