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.
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