slidge 0.2.0a9__py3-none-any.whl → 0.2.0a10__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/__version__.py +1 -1
- slidge/command/adhoc.py +1 -1
- slidge/command/base.py +4 -4
- slidge/contact/roster.py +7 -0
- 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 +67 -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 +118 -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/base.py +2 -2
- slidge/core/mixins/message.py +10 -4
- slidge/core/pubsub.py +2 -1
- slidge/core/session.py +28 -2
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
- slidge/db/models.py +13 -0
- slidge/db/store.py +128 -2
- slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
- slidge/util/test.py +5 -1
- slidge/util/types.py +6 -0
- slidge/util/util.py +5 -2
- {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/METADATA +2 -1
- {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/RECORD +40 -31
- 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 → slidge-0.2.0a10.dist-info}/LICENSE +0 -0
- {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/WHEEL +0 -0
- {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/entry_points.txt +0 -0
@@ -3,13 +3,15 @@ from typing import TYPE_CHECKING
|
|
3
3
|
from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
|
4
4
|
from slixmpp.exceptions import XMPPError
|
5
5
|
|
6
|
+
from .util import DispatcherMixin, exceptions_to_xmpp_errors
|
7
|
+
|
6
8
|
if TYPE_CHECKING:
|
7
|
-
from .
|
9
|
+
from slidge.core.gateway import BaseGateway
|
8
10
|
|
9
11
|
|
10
|
-
class
|
12
|
+
class SearchMixin(DispatcherMixin):
|
11
13
|
def __init__(self, xmpp: "BaseGateway"):
|
12
|
-
|
14
|
+
super().__init__(xmpp)
|
13
15
|
|
14
16
|
xmpp["xep_0055"].api.register(self.search_get_form, "search_get_form")
|
15
17
|
xmpp["xep_0055"].api.register(self._search_query, "search_query")
|
@@ -45,13 +47,9 @@ class Search:
|
|
45
47
|
"""
|
46
48
|
Handles a search request
|
47
49
|
"""
|
48
|
-
|
49
|
-
if user is None:
|
50
|
-
raise XMPPError(text="Search is only allowed for registered users")
|
50
|
+
session = await self._get_session(iq)
|
51
51
|
|
52
|
-
result = await
|
53
|
-
iq["search"]["form"].get_values()
|
54
|
-
)
|
52
|
+
result = await session.on_search(iq["search"]["form"].get_values())
|
55
53
|
|
56
54
|
if not result:
|
57
55
|
raise XMPPError("item-not-found", text="Nothing was found")
|
@@ -64,19 +62,17 @@ class Search:
|
|
64
62
|
form.add_item(item)
|
65
63
|
return reply
|
66
64
|
|
65
|
+
@exceptions_to_xmpp_errors
|
67
66
|
async def _handle_gateway_iq(self, iq: Iq):
|
68
67
|
if iq.get_to() != self.xmpp.boundjid.bare:
|
69
68
|
raise XMPPError("bad-request", "This can only be used on the component JID")
|
70
69
|
|
71
|
-
user = self.xmpp.store.users.get(iq.get_from())
|
72
|
-
if user is None:
|
73
|
-
raise XMPPError("not-authorized", "Register to the gateway first")
|
74
|
-
|
75
70
|
if len(self.xmpp.SEARCH_FIELDS) > 1:
|
76
71
|
raise XMPPError(
|
77
72
|
"feature-not-implemented", "Use jabber search for this gateway"
|
78
73
|
)
|
79
74
|
|
75
|
+
session = await self._get_session(iq)
|
80
76
|
field = self.xmpp.SEARCH_FIELDS[0]
|
81
77
|
|
82
78
|
reply = iq.reply()
|
@@ -85,7 +81,6 @@ class Search:
|
|
85
81
|
reply["gateway"]["prompt"] = field.label
|
86
82
|
elif iq["type"] == "set":
|
87
83
|
prompt = iq["gateway"]["prompt"]
|
88
|
-
session = self.xmpp.session_cls.from_user(user)
|
89
84
|
result = await session.on_search({field.var: prompt})
|
90
85
|
if result is None or not result.items:
|
91
86
|
raise XMPPError(
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
|
4
|
+
from slixmpp import Message
|
5
|
+
from slixmpp.exceptions import IqError
|
6
|
+
from slixmpp.plugins.xep_0084.stanza import Info
|
7
|
+
|
8
|
+
from ..session import BaseSession
|
9
|
+
from .caps import CapsMixin
|
10
|
+
from .disco import DiscoMixin
|
11
|
+
from .message import MessageMixin
|
12
|
+
from .muc import MucMixin
|
13
|
+
from .presence import PresenceHandlerMixin
|
14
|
+
from .registration import RegistrationMixin
|
15
|
+
from .search import SearchMixin
|
16
|
+
from .util import exceptions_to_xmpp_errors
|
17
|
+
from .vcard import VCardMixin
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from slidge.core.gateway import BaseGateway
|
21
|
+
|
22
|
+
|
23
|
+
class SessionDispatcher(
|
24
|
+
CapsMixin,
|
25
|
+
DiscoMixin,
|
26
|
+
RegistrationMixin,
|
27
|
+
MessageMixin,
|
28
|
+
MucMixin,
|
29
|
+
PresenceHandlerMixin,
|
30
|
+
SearchMixin,
|
31
|
+
VCardMixin,
|
32
|
+
):
|
33
|
+
def __init__(self, xmpp: "BaseGateway"):
|
34
|
+
super().__init__(xmpp)
|
35
|
+
xmpp.add_event_handler(
|
36
|
+
"avatar_metadata_publish", self.on_avatar_metadata_publish
|
37
|
+
)
|
38
|
+
|
39
|
+
@exceptions_to_xmpp_errors
|
40
|
+
async def on_avatar_metadata_publish(self, m: Message):
|
41
|
+
session = await self._get_session(m, timeout=None)
|
42
|
+
if not session.user.preferences.get("sync_avatar", False):
|
43
|
+
session.log.debug("User does not want to sync their avatar")
|
44
|
+
return
|
45
|
+
info = m["pubsub_event"]["items"]["item"]["avatar_metadata"]["info"]
|
46
|
+
|
47
|
+
await self.on_avatar_metadata_info(session, info)
|
48
|
+
|
49
|
+
async def on_avatar_metadata_info(self, session: BaseSession, info: Info):
|
50
|
+
hash_ = info["id"]
|
51
|
+
|
52
|
+
if session.user.avatar_hash == hash_:
|
53
|
+
session.log.debug("We already know this avatar hash")
|
54
|
+
return
|
55
|
+
self.xmpp.store.users.set_avatar_hash(session.user_pk, None)
|
56
|
+
|
57
|
+
if hash_:
|
58
|
+
try:
|
59
|
+
iq = await self.xmpp.plugin["xep_0084"].retrieve_avatar(
|
60
|
+
session.user_jid, hash_, ifrom=self.xmpp.boundjid.bare
|
61
|
+
)
|
62
|
+
except IqError as e:
|
63
|
+
session.log.warning("Could not fetch the user's avatar: %s", e)
|
64
|
+
return
|
65
|
+
bytes_ = iq["pubsub"]["items"]["item"]["avatar_data"]["value"]
|
66
|
+
type_ = info["type"]
|
67
|
+
height = info["height"]
|
68
|
+
width = info["width"]
|
69
|
+
else:
|
70
|
+
bytes_ = type_ = height = width = hash_ = None
|
71
|
+
try:
|
72
|
+
await session.on_avatar(bytes_, hash_, type_, width, height)
|
73
|
+
except NotImplementedError:
|
74
|
+
pass
|
75
|
+
except Exception as e:
|
76
|
+
# If something goes wrong here, replying an error stanza will to the
|
77
|
+
# avatar update will likely not show in most clients, so let's send
|
78
|
+
# a normal message from the component to the user.
|
79
|
+
session.send_gateway_message(
|
80
|
+
f"Something went wrong trying to set your avatar: {e!r}"
|
81
|
+
)
|
82
|
+
|
83
|
+
|
84
|
+
log = logging.getLogger(__name__)
|
@@ -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 _xmpp_to_legacy_thread(session, msg, e)
|
100
|
+
return session, e, legacy_thread
|
101
|
+
|
102
|
+
|
103
|
+
def _ignore(session: "BaseSession", msg: Message):
|
104
|
+
i = msg.get_id()
|
105
|
+
if i.startswith("slidge-carbon-"):
|
106
|
+
return True
|
107
|
+
if i not in session.ignore_messages:
|
108
|
+
return False
|
109
|
+
session.log.debug("Ignored sent carbon: %s", i)
|
110
|
+
session.ignore_messages.remove(i)
|
111
|
+
return True
|
112
|
+
|
113
|
+
|
114
|
+
async def _xmpp_to_legacy_thread(
|
115
|
+
session: "BaseSession", msg: Message, recipient: RecipientType
|
116
|
+
):
|
117
|
+
xmpp_thread = msg["thread"]
|
118
|
+
if not xmpp_thread:
|
119
|
+
return
|
120
|
+
|
121
|
+
if session.MESSAGE_IDS_ARE_THREAD_IDS:
|
122
|
+
return session.xmpp.store.sent.get_legacy_thread(session.user_pk, xmpp_thread)
|
123
|
+
|
124
|
+
async with session.thread_creation_lock:
|
125
|
+
legacy_thread_str = session.xmpp.store.sent.get_legacy_thread(
|
126
|
+
session.user_pk, xmpp_thread
|
127
|
+
)
|
128
|
+
if legacy_thread_str is None:
|
129
|
+
legacy_thread = str(await recipient.create_thread(xmpp_thread))
|
130
|
+
session.xmpp.store.sent.set_thread(
|
131
|
+
session.user_pk, xmpp_thread, legacy_thread
|
132
|
+
)
|
133
|
+
return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread)
|
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
|