slidge 0.2.0a9__py3-none-any.whl → 0.2.0a10__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. slidge/__version__.py +1 -1
  2. slidge/command/adhoc.py +1 -1
  3. slidge/command/base.py +4 -4
  4. slidge/contact/roster.py +7 -0
  5. slidge/core/dispatcher/__init__.py +3 -0
  6. slidge/core/{gateway → dispatcher}/caps.py +6 -4
  7. slidge/core/{gateway → dispatcher}/disco.py +11 -17
  8. slidge/core/dispatcher/message/__init__.py +10 -0
  9. slidge/core/dispatcher/message/chat_state.py +40 -0
  10. slidge/core/dispatcher/message/marker.py +67 -0
  11. slidge/core/dispatcher/message/message.py +397 -0
  12. slidge/core/dispatcher/muc/__init__.py +12 -0
  13. slidge/core/dispatcher/muc/admin.py +98 -0
  14. slidge/core/{gateway → dispatcher/muc}/mam.py +26 -15
  15. slidge/core/dispatcher/muc/misc.py +118 -0
  16. slidge/core/dispatcher/muc/owner.py +96 -0
  17. slidge/core/{gateway → dispatcher/muc}/ping.py +10 -15
  18. slidge/core/dispatcher/presence.py +177 -0
  19. slidge/core/{gateway → dispatcher}/registration.py +23 -2
  20. slidge/core/{gateway → dispatcher}/search.py +9 -14
  21. slidge/core/dispatcher/session_dispatcher.py +84 -0
  22. slidge/core/dispatcher/util.py +174 -0
  23. slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +26 -12
  24. slidge/core/{gateway/base.py → gateway.py} +42 -137
  25. slidge/core/mixins/base.py +2 -2
  26. slidge/core/mixins/message.py +10 -4
  27. slidge/core/pubsub.py +2 -1
  28. slidge/core/session.py +28 -2
  29. slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
  30. slidge/db/models.py +13 -0
  31. slidge/db/store.py +128 -2
  32. slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
  33. slidge/util/test.py +5 -1
  34. slidge/util/types.py +6 -0
  35. slidge/util/util.py +5 -2
  36. {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/METADATA +2 -1
  37. {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/RECORD +40 -31
  38. slidge/core/gateway/__init__.py +0 -3
  39. slidge/core/gateway/muc_admin.py +0 -35
  40. slidge/core/gateway/presence.py +0 -95
  41. slidge/core/gateway/session_dispatcher.py +0 -895
  42. {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/LICENSE +0 -0
  43. {slidge-0.2.0a9.dist-info → slidge-0.2.0a10.dist-info}/WHEEL +0 -0
  44. {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 .base import BaseGateway
9
+ from slidge.core.gateway import BaseGateway
8
10
 
9
11
 
10
- class Search:
12
+ class SearchMixin(DispatcherMixin):
11
13
  def __init__(self, xmpp: "BaseGateway"):
12
- self.xmpp = xmpp
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
- user = self.xmpp.store.users.get(ifrom)
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 self.xmpp.get_session_from_stanza(iq).on_search(
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
- class VCardTemp:
18
- def __init__(self, xmpp: "BaseGateway"):
19
- self.xmpp = xmpp
20
- # remove slixmpp's default handler to replace with our own
21
- self.xmpp.remove_handler("VCardTemp")
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.__handler, # type:ignore
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
- async def __handler(self, iq: Iq):
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.xmpp.get_muc_from_stanza(iq)
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 ... import command # noqa: F401
31
- from ...command.adhoc import AdhocProvider
32
- from ...command.admin import Exec
33
- from ...command.base import Command, FormField
34
- from ...command.chat_command import ChatCommandProvider
35
- from ...command.register import RegistrationType
36
- from ...db import GatewayUser, SlidgeStore
37
- from ...db.avatar import avatar_cache
38
- from ...slixfix.roster import RosterBackend
39
- from ...util import ABCSubclassableOnceAtMost
40
- from ...util.types import AvatarType, MessageOrPresenceTypeVar
41
- from ...util.util import timeit
42
- from .. import config
43
- from ..mixins import MessageMixin
44
- from ..pubsub import PubSubComponent
45
- from ..session import BaseSession
46
- from .caps import Caps
47
- from .delivery_receipt import DeliveryReceipt
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
- from ...group.room import LegacyMUC
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: Collection[FormField] = [
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 ...group.room import LegacyMUC
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
- self.__disco_handler = Disco(self)
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
- self.add_event_handler("user_register", self._on_user_register)
427
- self.add_event_handler("user_unregister", self._on_user_unregister)
428
- self.add_event_handler("groupchat_message_error", self.__on_group_chat_error)
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.__login_wrap(session))
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 __login_wrap(self, session: "BaseSession"):
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.__login_wrap(session)
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