slidge 0.1.0rc1__py3-none-any.whl → 0.1.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- slidge/__init__.py +54 -31
- slidge/__main__.py +51 -5
- slidge/command/__init__.py +28 -0
- slidge/command/adhoc.py +258 -0
- slidge/command/admin.py +193 -0
- slidge/command/base.py +441 -0
- slidge/command/categories.py +3 -0
- slidge/command/chat_command.py +288 -0
- slidge/command/register.py +179 -0
- slidge/command/user.py +250 -0
- slidge/contact/__init__.py +8 -0
- slidge/contact/contact.py +452 -0
- slidge/contact/roster.py +192 -0
- slidge/core/__init__.py +2 -0
- slidge/core/cache.py +121 -39
- slidge/core/config.py +116 -11
- slidge/core/gateway/__init__.py +3 -0
- slidge/core/gateway/base.py +895 -0
- slidge/core/gateway/caps.py +63 -0
- slidge/core/gateway/delivery_receipt.py +52 -0
- slidge/core/gateway/disco.py +80 -0
- slidge/core/gateway/mam.py +75 -0
- slidge/core/gateway/muc_admin.py +35 -0
- slidge/core/gateway/ping.py +66 -0
- slidge/core/gateway/presence.py +95 -0
- slidge/core/gateway/registration.py +53 -0
- slidge/core/gateway/search.py +102 -0
- slidge/core/gateway/session_dispatcher.py +789 -0
- slidge/core/gateway/vcard_temp.py +130 -0
- slidge/core/mixins/__init__.py +9 -1
- slidge/core/mixins/attachment.py +506 -0
- slidge/core/mixins/avatar.py +167 -0
- slidge/core/mixins/base.py +6 -19
- slidge/core/mixins/disco.py +66 -15
- slidge/core/mixins/lock.py +31 -0
- slidge/core/mixins/message.py +254 -252
- slidge/core/mixins/message_maker.py +154 -0
- slidge/core/mixins/presence.py +128 -31
- slidge/core/mixins/recipient.py +43 -0
- slidge/core/pubsub.py +275 -116
- slidge/core/session.py +586 -518
- slidge/group/__init__.py +10 -0
- slidge/group/archive.py +125 -0
- slidge/group/bookmarks.py +163 -0
- slidge/group/participant.py +458 -0
- slidge/group/room.py +1103 -0
- slidge/migration.py +18 -0
- slidge/slixfix/__init__.py +68 -0
- slidge/{util/xep_0050 → slixfix/link_preview}/__init__.py +4 -5
- slidge/slixfix/link_preview/link_preview.py +17 -0
- slidge/slixfix/link_preview/stanza.py +99 -0
- slidge/slixfix/roster.py +60 -0
- slidge/{util → slixfix}/xep_0077/register.py +1 -2
- slidge/slixfix/xep_0077/stanza.py +104 -0
- slidge/{util → slixfix}/xep_0100/gateway.py +17 -12
- slidge/slixfix/xep_0153/__init__.py +10 -0
- slidge/slixfix/xep_0153/stanza.py +25 -0
- slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
- slidge/slixfix/xep_0264/__init__.py +5 -0
- slidge/slixfix/xep_0264/stanza.py +36 -0
- slidge/slixfix/xep_0264/thumbnail.py +23 -0
- slidge/slixfix/xep_0292/__init__.py +5 -0
- slidge/slixfix/xep_0292/vcard4.py +100 -0
- slidge/slixfix/xep_0313/__init__.py +12 -0
- slidge/slixfix/xep_0313/mam.py +262 -0
- slidge/slixfix/xep_0313/stanza.py +359 -0
- slidge/slixfix/xep_0317/__init__.py +5 -0
- slidge/slixfix/xep_0317/hats.py +17 -0
- slidge/slixfix/xep_0317/stanza.py +28 -0
- slidge/{util → slixfix}/xep_0356_old/privilege.py +9 -7
- slidge/slixfix/xep_0424/__init__.py +9 -0
- slidge/slixfix/xep_0424/retraction.py +77 -0
- slidge/slixfix/xep_0424/stanza.py +28 -0
- slidge/slixfix/xep_0490/__init__.py +8 -0
- slidge/slixfix/xep_0490/mds.py +47 -0
- slidge/slixfix/xep_0490/stanza.py +17 -0
- slidge/util/__init__.py +4 -6
- slidge/util/archive_msg.py +61 -0
- slidge/util/conf.py +25 -4
- slidge/util/db.py +23 -69
- slidge/util/schema.sql +126 -0
- slidge/util/sql.py +508 -0
- slidge/util/test.py +136 -86
- slidge/util/types.py +155 -14
- slidge/util/util.py +225 -51
- slidge-0.1.1.dist-info/METADATA +110 -0
- slidge-0.1.1.dist-info/RECORD +96 -0
- {slidge-0.1.0rc1.dist-info → slidge-0.1.1.dist-info}/WHEEL +1 -1
- slidge/core/adhoc.py +0 -492
- slidge/core/chat_command.py +0 -197
- slidge/core/contact.py +0 -441
- slidge/core/disco.py +0 -59
- slidge/core/gateway.py +0 -899
- slidge/core/muc/__init__.py +0 -3
- slidge/core/muc/bookmarks.py +0 -74
- slidge/core/muc/participant.py +0 -152
- slidge/core/muc/room.py +0 -348
- slidge/plugins/discord/__init__.py +0 -121
- slidge/plugins/discord/client.py +0 -121
- slidge/plugins/discord/session.py +0 -172
- slidge/plugins/dummy.py +0 -334
- slidge/plugins/facebook.py +0 -591
- slidge/plugins/hackernews.py +0 -209
- slidge/plugins/mattermost/__init__.py +0 -1
- slidge/plugins/mattermost/api.py +0 -288
- slidge/plugins/mattermost/gateway.py +0 -417
- slidge/plugins/mattermost/websocket.py +0 -248
- slidge/plugins/signal/__init__.py +0 -4
- slidge/plugins/signal/config.py +0 -4
- slidge/plugins/signal/contact.py +0 -104
- slidge/plugins/signal/gateway.py +0 -379
- slidge/plugins/signal/group.py +0 -76
- slidge/plugins/signal/session.py +0 -515
- slidge/plugins/signal/txt.py +0 -13
- slidge/plugins/signal/util.py +0 -32
- slidge/plugins/skype.py +0 -310
- slidge/plugins/steam.py +0 -400
- slidge/plugins/telegram/__init__.py +0 -6
- slidge/plugins/telegram/client.py +0 -325
- slidge/plugins/telegram/config.py +0 -21
- slidge/plugins/telegram/contact.py +0 -154
- slidge/plugins/telegram/gateway.py +0 -182
- slidge/plugins/telegram/group.py +0 -184
- slidge/plugins/telegram/session.py +0 -275
- slidge/plugins/telegram/util.py +0 -153
- slidge/plugins/whatsapp/__init__.py +0 -6
- slidge/plugins/whatsapp/config.py +0 -17
- slidge/plugins/whatsapp/contact.py +0 -33
- slidge/plugins/whatsapp/event.go +0 -455
- slidge/plugins/whatsapp/gateway.go +0 -156
- slidge/plugins/whatsapp/gateway.py +0 -69
- slidge/plugins/whatsapp/go.mod +0 -17
- slidge/plugins/whatsapp/go.sum +0 -22
- slidge/plugins/whatsapp/session.go +0 -371
- slidge/plugins/whatsapp/session.py +0 -370
- slidge/util/xep_0030/__init__.py +0 -13
- slidge/util/xep_0030/disco.py +0 -811
- slidge/util/xep_0030/stanza/__init__.py +0 -7
- slidge/util/xep_0030/stanza/info.py +0 -270
- slidge/util/xep_0030/stanza/items.py +0 -147
- slidge/util/xep_0030/static.py +0 -467
- slidge/util/xep_0050/adhoc.py +0 -631
- slidge/util/xep_0050/stanza.py +0 -180
- slidge/util/xep_0077/stanza.py +0 -71
- slidge/util/xep_0292/__init__.py +0 -1
- slidge/util/xep_0292/stanza.py +0 -167
- slidge/util/xep_0292/vcard4.py +0 -74
- slidge/util/xep_0356/__init__.py +0 -7
- slidge/util/xep_0356/permissions.py +0 -35
- slidge/util/xep_0356/privilege.py +0 -160
- slidge/util/xep_0356/stanza.py +0 -44
- slidge/util/xep_0461/__init__.py +0 -6
- slidge/util/xep_0461/reply.py +0 -48
- slidge/util/xep_0461/stanza.py +0 -80
- slidge-0.1.0rc1.dist-info/METADATA +0 -171
- slidge-0.1.0rc1.dist-info/RECORD +0 -99
- /slidge/{plugins/__init__.py → py.typed} +0 -0
- /slidge/{util → slixfix}/xep_0077/__init__.py +0 -0
- /slidge/{util → slixfix}/xep_0100/__init__.py +0 -0
- /slidge/{util → slixfix}/xep_0100/stanza.py +0 -0
- /slidge/{util → slixfix}/xep_0356_old/__init__.py +0 -0
- /slidge/{util → slixfix}/xep_0356_old/stanza.py +0 -0
- {slidge-0.1.0rc1.dist-info → slidge-0.1.1.dist-info}/LICENSE +0 -0
- {slidge-0.1.0rc1.dist-info → slidge-0.1.1.dist-info}/entry_points.txt +0 -0
slidge/migration.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
import logging
|
2
|
+
import shutil
|
3
|
+
|
4
|
+
from .core import config
|
5
|
+
|
6
|
+
|
7
|
+
def remove_avatar_cache_v1():
|
8
|
+
old_dir = config.HOME_DIR / "slidge_avatars"
|
9
|
+
if old_dir.exists():
|
10
|
+
log.info("Avatar cache dir v1 found, clearing it.")
|
11
|
+
shutil.rmtree(old_dir)
|
12
|
+
|
13
|
+
|
14
|
+
def migrate():
|
15
|
+
remove_avatar_cache_v1()
|
16
|
+
|
17
|
+
|
18
|
+
log = logging.getLogger(__name__)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# This module contains patches for slixmpp; some have pending requests upstream
|
2
|
+
# and should be removed on the next slixmpp release.
|
3
|
+
|
4
|
+
# ruff: noqa: F401
|
5
|
+
|
6
|
+
import slixmpp.plugins
|
7
|
+
from slixmpp import Message
|
8
|
+
from slixmpp.plugins.xep_0050 import XEP_0050, Command
|
9
|
+
from slixmpp.xmlstream import StanzaBase
|
10
|
+
|
11
|
+
from . import ( # xep_0356,
|
12
|
+
link_preview,
|
13
|
+
xep_0077,
|
14
|
+
xep_0100,
|
15
|
+
xep_0153,
|
16
|
+
xep_0264,
|
17
|
+
xep_0292,
|
18
|
+
xep_0313,
|
19
|
+
xep_0317,
|
20
|
+
xep_0356_old,
|
21
|
+
xep_0424,
|
22
|
+
xep_0490,
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
def session_bind(self, jid):
|
27
|
+
self.xmpp["xep_0030"].add_feature(Command.namespace)
|
28
|
+
# awful hack to for the disco items: we need to comment this line
|
29
|
+
# related issue: https://todo.sr.ht/~nicoco/slidge/131
|
30
|
+
# self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
|
31
|
+
|
32
|
+
|
33
|
+
XEP_0050.session_bind = session_bind # type:ignore
|
34
|
+
|
35
|
+
|
36
|
+
def reply(self, body=None, clear=True):
|
37
|
+
"""
|
38
|
+
Overrides slixmpp's Message.reply(), since it strips to sender's resource
|
39
|
+
for mtype=groupchat, and we do not want that, because when we raise an XMPPError,
|
40
|
+
we actually want to preserve the resource.
|
41
|
+
(this is called in RootStanza.exception() to handle XMPPErrors)
|
42
|
+
"""
|
43
|
+
new_message = StanzaBase.reply(self, clear)
|
44
|
+
new_message["thread"] = self["thread"]
|
45
|
+
new_message["parent_thread"] = self["parent_thread"]
|
46
|
+
|
47
|
+
del new_message["id"]
|
48
|
+
if self.stream is not None and self.stream.use_message_ids:
|
49
|
+
new_message["id"] = self.stream.new_id()
|
50
|
+
|
51
|
+
if body is not None:
|
52
|
+
new_message["body"] = body
|
53
|
+
return new_message
|
54
|
+
|
55
|
+
|
56
|
+
slixmpp.plugins.PLUGINS.extend(
|
57
|
+
[
|
58
|
+
"link_preview",
|
59
|
+
"xep_0264",
|
60
|
+
"xep_0292_provider",
|
61
|
+
"xep_0317",
|
62
|
+
"xep_0356_old",
|
63
|
+
"xep_0490",
|
64
|
+
]
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
Message.reply = reply # type: ignore
|
@@ -1,11 +1,10 @@
|
|
1
|
-
|
2
1
|
# Slixmpp: The Slick XMPP Library
|
3
|
-
# Copyright (C)
|
2
|
+
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
4
3
|
# This file is part of Slixmpp.
|
5
4
|
# See the file LICENSE for copying permission.
|
5
|
+
|
6
6
|
from slixmpp.plugins.base import register_plugin
|
7
7
|
|
8
|
-
from .
|
9
|
-
from .stanza import Command
|
8
|
+
from .link_preview import LinkPreview
|
10
9
|
|
11
|
-
register_plugin(
|
10
|
+
register_plugin(LinkPreview)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Slixmpp: The Slick XMPP Library
|
2
|
+
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
3
|
+
# This file is part of Slixmpp.
|
4
|
+
# See the file LICENSE for copying permission.
|
5
|
+
from slixmpp.plugins import BasePlugin
|
6
|
+
|
7
|
+
from . import stanza
|
8
|
+
|
9
|
+
|
10
|
+
class LinkPreview(BasePlugin):
|
11
|
+
name = "link_preview"
|
12
|
+
description = "Sender-generated link previews"
|
13
|
+
dependencies = set()
|
14
|
+
stanza = stanza
|
15
|
+
|
16
|
+
def plugin_init(self):
|
17
|
+
stanza.register_plugin()
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Slixmpp: The Slick XMPP Library
|
2
|
+
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
3
|
+
# This file is part of Slixmpp.
|
4
|
+
# See the file LICENSE for copying permission.
|
5
|
+
from typing import Optional, Type
|
6
|
+
|
7
|
+
from slixmpp.stanza.message import Message
|
8
|
+
from slixmpp.xmlstream import ElementBase, register_stanza_plugin
|
9
|
+
|
10
|
+
|
11
|
+
class LinkPreview(ElementBase):
|
12
|
+
name = "Description"
|
13
|
+
namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
14
|
+
plugin_attrib = "link_preview"
|
15
|
+
plugin_multi_attrib = "link_previews"
|
16
|
+
interfaces = {"about", "title", "description", "url", "image", "type", "site_name"}
|
17
|
+
|
18
|
+
def _set_og(self, el: ElementBase, value: str) -> None:
|
19
|
+
el.xml.text = value
|
20
|
+
self.xml.append(el.xml)
|
21
|
+
|
22
|
+
def _get_og(self, el: Type[ElementBase]) -> Optional[str]:
|
23
|
+
child = self.xml.find(f"{{{el.namespace}}}{el.name}")
|
24
|
+
if child is None:
|
25
|
+
return None
|
26
|
+
return child.text
|
27
|
+
|
28
|
+
def set_title(self, v: str) -> None:
|
29
|
+
self._set_og(Title(), v)
|
30
|
+
|
31
|
+
def get_title(self) -> Optional[str]:
|
32
|
+
return self._get_og(Title)
|
33
|
+
|
34
|
+
def set_description(self, v: str) -> None:
|
35
|
+
self._set_og(Description(), v)
|
36
|
+
|
37
|
+
def get_description(self) -> Optional[str]:
|
38
|
+
return self._get_og(Description)
|
39
|
+
|
40
|
+
def set_url(self, v: str) -> None:
|
41
|
+
self._set_og(Url(), v)
|
42
|
+
|
43
|
+
def get_url(self) -> Optional[str]:
|
44
|
+
return self._get_og(Url)
|
45
|
+
|
46
|
+
def set_image(self, v: str) -> None:
|
47
|
+
self._set_og(Image(), v)
|
48
|
+
|
49
|
+
def get_image(self) -> Optional[str]:
|
50
|
+
return self._get_og(Image)
|
51
|
+
|
52
|
+
def set_type(self, v: str) -> None:
|
53
|
+
self._set_og(Type_(), v)
|
54
|
+
|
55
|
+
def get_type(self) -> Optional[str]:
|
56
|
+
return self._get_og(Type_)
|
57
|
+
|
58
|
+
def set_site_name(self, v: str) -> None:
|
59
|
+
self._set_og(SiteName(), v)
|
60
|
+
|
61
|
+
def get_site_name(self) -> Optional[str]:
|
62
|
+
return self._get_og(SiteName)
|
63
|
+
|
64
|
+
def get_about(self) -> Optional[str]:
|
65
|
+
return self.xml.attrib.get(f"{{{self.namespace}}}about")
|
66
|
+
|
67
|
+
|
68
|
+
class OpenGraphMixin(ElementBase):
|
69
|
+
namespace = "https://ogp.me/ns#"
|
70
|
+
|
71
|
+
|
72
|
+
class Title(OpenGraphMixin):
|
73
|
+
name = plugin_attrib = "title"
|
74
|
+
|
75
|
+
|
76
|
+
class Description(OpenGraphMixin):
|
77
|
+
name = plugin_attrib = "description"
|
78
|
+
|
79
|
+
|
80
|
+
class Url(OpenGraphMixin):
|
81
|
+
name = plugin_attrib = "url"
|
82
|
+
|
83
|
+
|
84
|
+
class Image(OpenGraphMixin):
|
85
|
+
name = plugin_attrib = "image"
|
86
|
+
|
87
|
+
|
88
|
+
class Type_(OpenGraphMixin):
|
89
|
+
name = plugin_attrib = "type"
|
90
|
+
|
91
|
+
|
92
|
+
class SiteName(OpenGraphMixin):
|
93
|
+
name = plugin_attrib = "site_name"
|
94
|
+
|
95
|
+
|
96
|
+
def register_plugin():
|
97
|
+
for plugin in Title, Description, Url, Image, Type_, SiteName:
|
98
|
+
register_stanza_plugin(plugin, Title)
|
99
|
+
register_stanza_plugin(Message, LinkPreview, iterable=True)
|
slidge/slixfix/roster.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
from slixmpp import JID
|
2
|
+
|
3
|
+
from ..util.db import log, user_store
|
4
|
+
|
5
|
+
|
6
|
+
class YesSet(set):
|
7
|
+
"""
|
8
|
+
A pseudo-set which always test True for membership
|
9
|
+
"""
|
10
|
+
|
11
|
+
def __contains__(self, item):
|
12
|
+
log.debug("Test in")
|
13
|
+
return True
|
14
|
+
|
15
|
+
|
16
|
+
class RosterBackend:
|
17
|
+
"""
|
18
|
+
A pseudo-roster for the gateway component.
|
19
|
+
|
20
|
+
If a user is in the user store, this will behave as if the user is part of the
|
21
|
+
roster with subscription "both", and "none" otherwise.
|
22
|
+
|
23
|
+
This is rudimentary but the only sane way I could come up with so far.
|
24
|
+
"""
|
25
|
+
|
26
|
+
@staticmethod
|
27
|
+
def entries(_owner_jid, _default=None):
|
28
|
+
return YesSet()
|
29
|
+
|
30
|
+
@staticmethod
|
31
|
+
def save(_owner_jid, _jid, _item_state, _db_state):
|
32
|
+
pass
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
def load(_owner_jid, jid, _db_state):
|
36
|
+
log.debug("Load %s", jid)
|
37
|
+
user = user_store.get_by_jid(JID(jid))
|
38
|
+
log.debug("User %s", user)
|
39
|
+
if user is None:
|
40
|
+
return {
|
41
|
+
"name": "",
|
42
|
+
"groups": [],
|
43
|
+
"from": False,
|
44
|
+
"to": False,
|
45
|
+
"pending_in": False,
|
46
|
+
"pending_out": False,
|
47
|
+
"whitelisted": False,
|
48
|
+
"subscription": "both",
|
49
|
+
}
|
50
|
+
else:
|
51
|
+
return {
|
52
|
+
"name": "",
|
53
|
+
"groups": [],
|
54
|
+
"from": True,
|
55
|
+
"to": True,
|
56
|
+
"pending_in": False,
|
57
|
+
"pending_out": False,
|
58
|
+
"whitelisted": False,
|
59
|
+
"subscription": "none",
|
60
|
+
}
|
@@ -20,7 +20,6 @@ log = logging.getLogger(__name__)
|
|
20
20
|
|
21
21
|
# noinspection PyPep8Naming
|
22
22
|
class XEP_0077(BasePlugin):
|
23
|
-
|
24
23
|
"""
|
25
24
|
XEP-0077: In-Band Registration
|
26
25
|
|
@@ -81,7 +80,7 @@ class XEP_0077(BasePlugin):
|
|
81
80
|
self.xmpp.register_handler(
|
82
81
|
CoroutineCallback(
|
83
82
|
"registration",
|
84
|
-
StanzaPath("/iq/register"),
|
83
|
+
StanzaPath(f"/iq@to={self.xmpp.boundjid.bare}/register"),
|
85
84
|
self._handle_registration,
|
86
85
|
)
|
87
86
|
)
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Slixmpp: The Slick XMPP Library
|
2
|
+
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
3
|
+
# This file is part of Slixmpp.
|
4
|
+
# See the file LICENSE for copying permission.
|
5
|
+
from __future__ import unicode_literals
|
6
|
+
|
7
|
+
from typing import ClassVar, Set
|
8
|
+
|
9
|
+
from slixmpp.xmlstream import ElementBase
|
10
|
+
|
11
|
+
|
12
|
+
class Register(ElementBase):
|
13
|
+
namespace = "jabber:iq:register"
|
14
|
+
name = "query"
|
15
|
+
plugin_attrib = "register"
|
16
|
+
interfaces = {
|
17
|
+
"username",
|
18
|
+
"password",
|
19
|
+
"email",
|
20
|
+
"nick",
|
21
|
+
"name",
|
22
|
+
"first",
|
23
|
+
"last",
|
24
|
+
"address",
|
25
|
+
"city",
|
26
|
+
"state",
|
27
|
+
"zip",
|
28
|
+
"phone",
|
29
|
+
"url",
|
30
|
+
"date",
|
31
|
+
"misc",
|
32
|
+
"text",
|
33
|
+
"key",
|
34
|
+
"registered",
|
35
|
+
"remove",
|
36
|
+
"instructions",
|
37
|
+
"fields",
|
38
|
+
}
|
39
|
+
sub_interfaces = interfaces
|
40
|
+
form_fields = {
|
41
|
+
"username",
|
42
|
+
"password",
|
43
|
+
"email",
|
44
|
+
"nick",
|
45
|
+
"name",
|
46
|
+
"first",
|
47
|
+
"last",
|
48
|
+
"address",
|
49
|
+
"city",
|
50
|
+
"state",
|
51
|
+
"zip",
|
52
|
+
"phone",
|
53
|
+
"url",
|
54
|
+
"date",
|
55
|
+
"misc",
|
56
|
+
"text",
|
57
|
+
"key",
|
58
|
+
}
|
59
|
+
|
60
|
+
def get_registered(self):
|
61
|
+
present = self.xml.find("{%s}registered" % self.namespace)
|
62
|
+
return present is not None
|
63
|
+
|
64
|
+
def get_remove(self):
|
65
|
+
present = self.xml.find("{%s}remove" % self.namespace)
|
66
|
+
return present is not None
|
67
|
+
|
68
|
+
def set_registered(self, value):
|
69
|
+
if value:
|
70
|
+
self.add_field("registered")
|
71
|
+
else:
|
72
|
+
del self["registered"]
|
73
|
+
|
74
|
+
def set_remove(self, value):
|
75
|
+
if value:
|
76
|
+
self.add_field("remove")
|
77
|
+
else:
|
78
|
+
del self["remove"]
|
79
|
+
|
80
|
+
def add_field(self, value):
|
81
|
+
self._set_sub_text(value, "", keep=True)
|
82
|
+
|
83
|
+
def get_fields(self):
|
84
|
+
fields = set()
|
85
|
+
for field in self.form_fields:
|
86
|
+
if self.xml.find("{%s}%s" % (self.namespace, field)) is not None:
|
87
|
+
fields.add(field)
|
88
|
+
return fields
|
89
|
+
|
90
|
+
def set_fields(self, fields):
|
91
|
+
del self["fields"]
|
92
|
+
for field in fields:
|
93
|
+
self._set_sub_text(field, "", keep=True)
|
94
|
+
|
95
|
+
def del_fields(self):
|
96
|
+
for field in self.form_fields:
|
97
|
+
self._del_sub(field)
|
98
|
+
|
99
|
+
|
100
|
+
class RegisterFeature(ElementBase):
|
101
|
+
name = "register"
|
102
|
+
namespace = "http://jabber.org/features/iq-register"
|
103
|
+
plugin_attrib = name
|
104
|
+
interfaces: ClassVar[Set[str]] = set()
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import logging
|
2
|
+
import warnings
|
2
3
|
|
3
4
|
from slixmpp import JID, Iq, Message, Presence, register_stanza_plugin
|
5
|
+
from slixmpp.exceptions import XMPPError
|
4
6
|
from slixmpp.plugins.base import BasePlugin
|
5
7
|
|
6
8
|
from slidge.core import config
|
@@ -80,20 +82,21 @@ class XEP_0100(BasePlugin):
|
|
80
82
|
"groups": ["Slidge"],
|
81
83
|
}
|
82
84
|
}
|
85
|
+
try:
|
86
|
+
await self._set_roster(jid, items)
|
87
|
+
except PermissionError:
|
88
|
+
warnings.warn(
|
89
|
+
"Slidge does not have the privilege to manage users' rosters. "
|
90
|
+
"Users should add the slidge component to their rosters manually."
|
91
|
+
)
|
92
|
+
if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
|
93
|
+
self.xmpp.send_presence(ptype="subscribe", pto=jid.bare)
|
94
|
+
|
95
|
+
async def _set_roster(self, jid, items):
|
83
96
|
try:
|
84
97
|
await self.xmpp["xep_0356"].set_roster(jid=jid.bare, roster_items=items)
|
85
98
|
except PermissionError:
|
86
|
-
|
87
|
-
await self.xmpp["xep_0356_old"].set_roster(
|
88
|
-
jid=jid.bare, roster_items=items
|
89
|
-
)
|
90
|
-
except PermissionError:
|
91
|
-
log.warning(
|
92
|
-
"Slidge does not have the privilege to manage users' rosters. "
|
93
|
-
"Users should add the slidge component to their rosters manually."
|
94
|
-
)
|
95
|
-
if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
|
96
|
-
self.xmpp.send_presence(ptype="subscribe", pto=jid.bare)
|
99
|
+
await self.xmpp["xep_0356_old"].set_roster(jid=jid.bare, roster_items=items)
|
97
100
|
|
98
101
|
def on_presence_unsubscribe(self, p: Presence):
|
99
102
|
if p.get_to() == self.xmpp.boundjid.bare:
|
@@ -111,6 +114,8 @@ class XEP_0100(BasePlugin):
|
|
111
114
|
return
|
112
115
|
|
113
116
|
if self.needs_registration and await self.get_user(msg) is None:
|
114
|
-
|
117
|
+
raise XMPPError(
|
118
|
+
"registration-required", text="You are not registered to this gateway"
|
119
|
+
)
|
115
120
|
|
116
121
|
self.xmpp.event("legacy_message", msg)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Slixmpp: The Slick XMPP Library
|
2
|
+
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
3
|
+
# This file is part of Slixmpp.
|
4
|
+
# See the file LICENSE for copying permission.
|
5
|
+
from slixmpp.plugins.base import register_plugin
|
6
|
+
|
7
|
+
from .stanza import VCardTempUpdate
|
8
|
+
from .vcard_avatar import XEP_0153
|
9
|
+
|
10
|
+
register_plugin(XEP_0153)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Slixmpp: The Slick XMPP Library
|
2
|
+
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
3
|
+
# This file is part of Slixmpp.
|
4
|
+
# See the file LICENSE for copying permission.
|
5
|
+
from slixmpp.xmlstream import ElementBase
|
6
|
+
|
7
|
+
|
8
|
+
class VCardTempUpdate(ElementBase):
|
9
|
+
name = "x"
|
10
|
+
namespace = "vcard-temp:x:update"
|
11
|
+
plugin_attrib = "vcard_temp_update"
|
12
|
+
interfaces = {"photo"}
|
13
|
+
sub_interfaces = interfaces
|
14
|
+
|
15
|
+
def set_photo(self, value):
|
16
|
+
if value is not None:
|
17
|
+
self._set_sub_text("photo", value, keep=True)
|
18
|
+
else:
|
19
|
+
self._del_sub("photo")
|
20
|
+
|
21
|
+
def get_photo(self):
|
22
|
+
photo = self.xml.find("{%s}photo" % self.namespace)
|
23
|
+
if photo is None:
|
24
|
+
return None
|
25
|
+
return photo.text
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Slixmpp: The Slick XMPP Library
|
2
|
+
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
3
|
+
# This file is part of Slixmpp.
|
4
|
+
# See the file LICENSE for copying permission.
|
5
|
+
import logging
|
6
|
+
|
7
|
+
from slixmpp.plugins.base import BasePlugin
|
8
|
+
from slixmpp.stanza import Presence
|
9
|
+
from slixmpp.xmlstream import register_stanza_plugin
|
10
|
+
|
11
|
+
from . import VCardTempUpdate, stanza
|
12
|
+
|
13
|
+
log = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class XEP_0153(BasePlugin):
|
17
|
+
name = "xep_0153"
|
18
|
+
description = "XEP-0153: vCard-Based Avatars (slidge, just for MUCs)"
|
19
|
+
dependencies = {"xep_0054"}
|
20
|
+
stanza = stanza
|
21
|
+
|
22
|
+
def plugin_init(self):
|
23
|
+
register_stanza_plugin(Presence, VCardTempUpdate)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from slixmpp import register_stanza_plugin
|
4
|
+
from slixmpp.plugins.xep_0234.stanza import File
|
5
|
+
from slixmpp.xmlstream import ElementBase
|
6
|
+
|
7
|
+
NS = "urn:xmpp:thumbs:1"
|
8
|
+
|
9
|
+
|
10
|
+
class Thumbnail(ElementBase):
|
11
|
+
name = plugin_attrib = "thumbnail"
|
12
|
+
namespace = NS
|
13
|
+
interfaces = {"uri", "media-type", "width", "height"}
|
14
|
+
|
15
|
+
def get_width(self) -> float:
|
16
|
+
return _int_or_none(self._get_attr("width"))
|
17
|
+
|
18
|
+
def get_height(self) -> float:
|
19
|
+
return _int_or_none(self._get_attr("height"))
|
20
|
+
|
21
|
+
def set_width(self, v: int) -> None:
|
22
|
+
self._set_attr("width", str(v))
|
23
|
+
|
24
|
+
def set_height(self, v: int) -> None:
|
25
|
+
self._set_attr("height", str(v))
|
26
|
+
|
27
|
+
|
28
|
+
def _int_or_none(v) -> Optional[int]:
|
29
|
+
try:
|
30
|
+
return int(v)
|
31
|
+
except ValueError:
|
32
|
+
return None
|
33
|
+
|
34
|
+
|
35
|
+
def register_plugin():
|
36
|
+
register_stanza_plugin(File, Thumbnail)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from slixmpp.plugins import BasePlugin
|
4
|
+
|
5
|
+
from . import stanza
|
6
|
+
|
7
|
+
log = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
|
10
|
+
class XEP_0264(BasePlugin):
|
11
|
+
"""
|
12
|
+
XEP-0264: Jingle Content Thumbnails
|
13
|
+
|
14
|
+
Minimum needed for xep 0385 (Stateless inline media sharing)
|
15
|
+
"""
|
16
|
+
|
17
|
+
name = "xep_0264"
|
18
|
+
description = "XEP-0264: Jingle Content Thumbnails"
|
19
|
+
dependencies = {"xep_0234"}
|
20
|
+
stanza = stanza
|
21
|
+
|
22
|
+
def plugin_init(self):
|
23
|
+
stanza.register_plugin()
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import TYPE_CHECKING, NamedTuple, Optional
|
3
|
+
|
4
|
+
from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
|
5
|
+
from slixmpp.plugins.base import BasePlugin, register_plugin
|
6
|
+
from slixmpp.plugins.xep_0292.stanza import NS, VCard4
|
7
|
+
from slixmpp.types import JidStr
|
8
|
+
|
9
|
+
from slidge.contact import LegacyContact
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from slidge.core.gateway import BaseGateway
|
13
|
+
|
14
|
+
|
15
|
+
class StoredVCard(NamedTuple):
|
16
|
+
content: VCard4
|
17
|
+
authorized_jids: set[JidStr]
|
18
|
+
|
19
|
+
|
20
|
+
class VCard4Provider(BasePlugin):
|
21
|
+
xmpp: "BaseGateway"
|
22
|
+
|
23
|
+
name = "xep_0292_provider"
|
24
|
+
description = "VCard4 Provider"
|
25
|
+
dependencies = {"xep_0030"}
|
26
|
+
|
27
|
+
def __init__(self, *a, **k):
|
28
|
+
super(VCard4Provider, self).__init__(*a, **k)
|
29
|
+
self._vcards = dict[JidStr, StoredVCard]()
|
30
|
+
|
31
|
+
def plugin_init(self):
|
32
|
+
self.xmpp.register_handler(
|
33
|
+
CoroutineCallback(
|
34
|
+
"get_vcard",
|
35
|
+
StanzaPath(f"iq@type=get/vcard"),
|
36
|
+
self.handle_vcard_get, # type:ignore
|
37
|
+
)
|
38
|
+
)
|
39
|
+
|
40
|
+
self.xmpp.plugin["xep_0030"].add_feature(NS)
|
41
|
+
|
42
|
+
def _get_cached_vcard(self, jid: JidStr, requested_by: JidStr) -> Optional[VCard4]:
|
43
|
+
vcard = self._vcards.get(JID(jid).bare)
|
44
|
+
if vcard:
|
45
|
+
if auth := vcard.authorized_jids:
|
46
|
+
if JID(requested_by).bare in auth:
|
47
|
+
return vcard.content
|
48
|
+
else:
|
49
|
+
return vcard.content
|
50
|
+
return None
|
51
|
+
|
52
|
+
async def get_vcard(self, jid: JidStr, requested_by: JidStr) -> Optional[VCard4]:
|
53
|
+
if vcard := self._get_cached_vcard(jid, requested_by):
|
54
|
+
log.debug("Found a cached vcard")
|
55
|
+
return vcard
|
56
|
+
if not hasattr(self.xmpp, "get_session_from_jid"):
|
57
|
+
return None
|
58
|
+
jid = JID(jid)
|
59
|
+
requested_by = JID(requested_by)
|
60
|
+
session = self.xmpp.get_session_from_jid(requested_by)
|
61
|
+
if session is None:
|
62
|
+
return
|
63
|
+
entity = await session.get_contact_or_group_or_participant(jid)
|
64
|
+
if isinstance(entity, LegacyContact):
|
65
|
+
log.debug("Fetching vcard")
|
66
|
+
await entity.fetch_vcard()
|
67
|
+
return self._get_cached_vcard(jid, requested_by)
|
68
|
+
return None
|
69
|
+
|
70
|
+
async def handle_vcard_get(self, iq: Iq):
|
71
|
+
r = iq.reply()
|
72
|
+
if vcard := await self.get_vcard(iq.get_to().bare, iq.get_from().bare):
|
73
|
+
r.append(vcard)
|
74
|
+
else:
|
75
|
+
r.enable("vcard")
|
76
|
+
r.send()
|
77
|
+
|
78
|
+
def set_vcard(
|
79
|
+
self,
|
80
|
+
jid: JidStr,
|
81
|
+
vcard: VCard4,
|
82
|
+
/,
|
83
|
+
authorized_jids: Optional[set[JidStr]] = None,
|
84
|
+
):
|
85
|
+
cache = self._vcards.get(jid)
|
86
|
+
new = StoredVCard(
|
87
|
+
vcard, authorized_jids if authorized_jids is not None else set()
|
88
|
+
)
|
89
|
+
self._vcards[jid] = new
|
90
|
+
if cache == new:
|
91
|
+
return
|
92
|
+
if self.xmpp["pubsub"] and authorized_jids:
|
93
|
+
for to in authorized_jids:
|
94
|
+
self.xmpp.loop.create_task(
|
95
|
+
self.xmpp["pubsub"].broadcast_vcard_event(jid, to)
|
96
|
+
)
|
97
|
+
|
98
|
+
|
99
|
+
register_plugin(VCard4Provider)
|
100
|
+
log = logging.getLogger(__name__)
|