slidge 0.2.2__py3-none-any.whl → 0.2.4__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/register.py +1 -3
- slidge/contact/contact.py +1 -1
- slidge/core/dispatcher/message/message.py +4 -1
- slidge/core/dispatcher/muc/admin.py +3 -4
- slidge/core/dispatcher/presence.py +3 -3
- slidge/core/gateway.py +2 -0
- slidge/core/mixins/attachment.py +1 -1
- slidge/core/mixins/avatar.py +6 -0
- slidge/core/mixins/message.py +13 -2
- slidge/core/mixins/recipient.py +2 -2
- slidge/db/store.py +9 -3
- slidge/group/archive.py +3 -3
- slidge/group/participant.py +11 -5
- slidge/group/room.py +21 -1
- slidge/main.py +1 -1
- slidge/slixfix/__init__.py +69 -13
- slidge/slixfix/xep_0153/__init__.py +0 -1
- slidge/slixfix/xep_0153/vcard_avatar.py +1 -10
- slidge/slixfix/xep_0492/__init__.py +8 -0
- slidge/slixfix/xep_0492/notify.py +16 -0
- slidge/slixfix/xep_0492/stanza.py +102 -0
- slidge/util/test.py +9 -2
- slidge-0.2.4.dist-info/METADATA +793 -0
- {slidge-0.2.2.dist-info → slidge-0.2.4.dist-info}/RECORD +41 -54
- {slidge-0.2.2.dist-info → slidge-0.2.4.dist-info}/WHEEL +2 -1
- slidge-0.2.4.dist-info/entry_points.txt +2 -0
- slidge-0.2.4.dist-info/top_level.txt +1 -0
- slidge/slixfix/xep_0153/stanza.py +0 -25
- slidge/slixfix/xep_0264/__init__.py +0 -5
- slidge/slixfix/xep_0264/stanza.py +0 -36
- slidge/slixfix/xep_0264/thumbnail.py +0 -23
- slidge/slixfix/xep_0313/__init__.py +0 -12
- slidge/slixfix/xep_0313/mam.py +0 -262
- slidge/slixfix/xep_0313/stanza.py +0 -359
- slidge/slixfix/xep_0317/__init__.py +0 -5
- slidge/slixfix/xep_0317/hats.py +0 -17
- slidge/slixfix/xep_0317/stanza.py +0 -28
- slidge/slixfix/xep_0424/__init__.py +0 -9
- slidge/slixfix/xep_0424/retraction.py +0 -77
- slidge/slixfix/xep_0424/stanza.py +0 -28
- slidge/slixfix/xep_0490/__init__.py +0 -8
- slidge/slixfix/xep_0490/mds.py +0 -47
- slidge/slixfix/xep_0490/stanza.py +0 -17
- slidge-0.2.2.dist-info/LICENSE +0 -661
- slidge-0.2.2.dist-info/METADATA +0 -116
- slidge-0.2.2.dist-info/entry_points.txt +0 -3
slidge/__version__.py
CHANGED
slidge/command/register.py
CHANGED
@@ -119,9 +119,7 @@ class Register(Command):
|
|
119
119
|
elif self.xmpp.REGISTRATION_TYPE == RegistrationType.QRCODE:
|
120
120
|
self.xmpp.qr_pending_registrations[ # type:ignore
|
121
121
|
user.jid.bare
|
122
|
-
] = (
|
123
|
-
self.xmpp.loop.create_future()
|
124
|
-
)
|
122
|
+
] = self.xmpp.loop.create_future()
|
125
123
|
qr_text = await self.xmpp.get_qr_text(user)
|
126
124
|
qr = qrcode.make(qr_text)
|
127
125
|
with tempfile.NamedTemporaryFile(
|
slidge/contact/contact.py
CHANGED
@@ -454,7 +454,7 @@ class LegacyContact(
|
|
454
454
|
except PermissionError:
|
455
455
|
warnings.warn(
|
456
456
|
"Slidge does not have privileges to add contacts to the roster. Refer"
|
457
|
-
" to https://slidge.
|
457
|
+
" to https://slidge.codeberg.page/docs/main/admin/privilege.html for"
|
458
458
|
" more info."
|
459
459
|
)
|
460
460
|
if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
|
@@ -296,7 +296,10 @@ class MessageContentMixin(DispatcherMixin):
|
|
296
296
|
# no need to carbon for groups, we just don't echo the stanza
|
297
297
|
entity.react(legacy_id, carbon=True) # type: ignore
|
298
298
|
await session.on_react(entity, legacy_id, [], thread=thread)
|
299
|
-
raise XMPPError(
|
299
|
+
raise XMPPError(
|
300
|
+
"policy-violation", # type:ignore
|
301
|
+
text=error_msg,
|
302
|
+
)
|
300
303
|
|
301
304
|
await session.on_react(entity, legacy_id, emojis, thread=thread)
|
302
305
|
if isinstance(entity, LegacyMUC):
|
@@ -11,7 +11,7 @@ class MucAdminMixin(DispatcherMixin):
|
|
11
11
|
self.xmpp.register_handler(
|
12
12
|
CoroutineCallback(
|
13
13
|
"MUCModerate",
|
14
|
-
StanzaPath("iq/
|
14
|
+
StanzaPath("iq/moderate"),
|
15
15
|
self.on_user_moderation,
|
16
16
|
)
|
17
17
|
)
|
@@ -35,12 +35,11 @@ class MucAdminMixin(DispatcherMixin):
|
|
35
35
|
assert isinstance(iq, Iq)
|
36
36
|
muc = await self.get_muc_from_stanza(iq)
|
37
37
|
|
38
|
-
|
39
|
-
xmpp_id =
|
38
|
+
moderate = iq["moderate"]
|
39
|
+
xmpp_id = iq["moderate"]["id"]
|
40
40
|
if not xmpp_id:
|
41
41
|
raise XMPPError("bad-request", "Missing moderated message ID")
|
42
42
|
|
43
|
-
moderate = apply_to["moderate"]
|
44
43
|
if not moderate["retract"]:
|
45
44
|
raise XMPPError(
|
46
45
|
"feature-not-implemented",
|
@@ -165,9 +165,9 @@ class PresenceHandlerMixin(DispatcherMixin):
|
|
165
165
|
error_stanza["error"]["type"] = "cancel"
|
166
166
|
error_stanza["error"]["by"] = muc.jid
|
167
167
|
error_stanza["error"]["condition"] = "not-acceptable"
|
168
|
-
error_stanza["error"][
|
169
|
-
"
|
170
|
-
|
168
|
+
error_stanza["error"]["text"] = (
|
169
|
+
"Slidge does not let you change your nickname in groups."
|
170
|
+
)
|
171
171
|
error_stanza.send()
|
172
172
|
|
173
173
|
|
slidge/core/gateway.py
CHANGED
@@ -913,7 +913,9 @@ SLIXMPP_PLUGINS = [
|
|
913
913
|
"xep_0444", # Message reactions
|
914
914
|
"xep_0447", # Stateless File Sharing
|
915
915
|
"xep_0461", # Message replies
|
916
|
+
"xep_0469", # Bookmark Pinning
|
916
917
|
"xep_0490", # Message Displayed Synchronization
|
918
|
+
"xep_0492", # Chat Notification Settings
|
917
919
|
]
|
918
920
|
|
919
921
|
LOG_STRIP_ELEMENTS = ["data", "binval"]
|
slidge/core/mixins/attachment.py
CHANGED
@@ -20,11 +20,11 @@ import thumbhash
|
|
20
20
|
from PIL import Image, ImageOps
|
21
21
|
from slixmpp import JID, Message
|
22
22
|
from slixmpp.exceptions import IqError, IqTimeout
|
23
|
+
from slixmpp.plugins.xep_0264.stanza import Thumbnail
|
23
24
|
from slixmpp.plugins.xep_0363 import FileUploadError
|
24
25
|
from slixmpp.plugins.xep_0447.stanza import StatelessFileSharing
|
25
26
|
|
26
27
|
from ...db.avatar import avatar_cache
|
27
|
-
from ...slixfix.xep_0264.stanza import Thumbnail
|
28
28
|
from ...util.types import (
|
29
29
|
LegacyAttachment,
|
30
30
|
LegacyMessageType,
|
slidge/core/mixins/avatar.py
CHANGED
@@ -3,6 +3,7 @@ from hashlib import sha1
|
|
3
3
|
from pathlib import Path
|
4
4
|
from typing import TYPE_CHECKING, Optional
|
5
5
|
|
6
|
+
from PIL import UnidentifiedImageError
|
6
7
|
from slixmpp import JID
|
7
8
|
|
8
9
|
from ...db.avatar import CachedAvatar, avatar_cache
|
@@ -100,6 +101,11 @@ class AvatarMixin:
|
|
100
101
|
else:
|
101
102
|
try:
|
102
103
|
cached_avatar = await avatar_cache.convert_or_get(a)
|
104
|
+
except UnidentifiedImageError:
|
105
|
+
self.session.log.warning("%s is not a valid avatar", a)
|
106
|
+
self._avatar_pk = None
|
107
|
+
self.__avatar_unique_id = uid
|
108
|
+
return
|
103
109
|
except Exception as e:
|
104
110
|
self.session.log.error("Failed to set avatar %s", a, exc_info=e)
|
105
111
|
self._avatar_pk = None
|
slidge/core/mixins/message.py
CHANGED
@@ -4,8 +4,8 @@ import warnings
|
|
4
4
|
from typing import TYPE_CHECKING, Optional
|
5
5
|
|
6
6
|
from slixmpp import Iq, Message
|
7
|
+
from slixmpp.plugins.xep_0004 import Form
|
7
8
|
|
8
|
-
from ...slixfix.xep_0490.mds import PUBLISH_OPTIONS
|
9
9
|
from ...util.types import ChatState, LegacyMessageType, Marker
|
10
10
|
from .attachment import AttachmentMixin
|
11
11
|
from .message_maker import MessageMaker
|
@@ -14,6 +14,17 @@ from .message_text import TextMessageMixin
|
|
14
14
|
if TYPE_CHECKING:
|
15
15
|
from ...group import LegacyMUC
|
16
16
|
|
17
|
+
# this is for MDS
|
18
|
+
PUBLISH_OPTIONS = Form()
|
19
|
+
PUBLISH_OPTIONS["type"] = "submit"
|
20
|
+
PUBLISH_OPTIONS.add_field(
|
21
|
+
"FORM_TYPE", "hidden", value="http://jabber.org/protocol/pubsub#publish-options"
|
22
|
+
)
|
23
|
+
PUBLISH_OPTIONS.add_field("pubsub#persist_items", value="true")
|
24
|
+
PUBLISH_OPTIONS.add_field("pubsub#max_items", value="max")
|
25
|
+
PUBLISH_OPTIONS.add_field("pubsub#send_last_published_item", value="never")
|
26
|
+
PUBLISH_OPTIONS.add_field("pubsub#access_model", value="whitelist")
|
27
|
+
|
17
28
|
|
18
29
|
class ChatStateMixin(MessageMaker):
|
19
30
|
def __init__(self):
|
@@ -172,7 +183,7 @@ class CarbonMessageMixin(ContentMessageMixin, MarkerMixin):
|
|
172
183
|
warnings.warn(
|
173
184
|
"Slidge does not have privileges to send message on behalf of"
|
174
185
|
" user.Refer to"
|
175
|
-
" https://slidge.
|
186
|
+
" https://slidge.codeberg.page/docs/main/admin/privilege.html"
|
176
187
|
" for more info."
|
177
188
|
)
|
178
189
|
|
slidge/core/mixins/recipient.py
CHANGED
@@ -21,9 +21,9 @@ class ReactionRecipientMixin:
|
|
21
21
|
form["type"] = "result"
|
22
22
|
form.add_field("FORM_TYPE", "hidden", value="urn:xmpp:reactions:0:restrictions")
|
23
23
|
if self.REACTIONS_SINGLE_EMOJI:
|
24
|
-
form.add_field("max_reactions_per_user", value="1")
|
24
|
+
form.add_field("max_reactions_per_user", value="1", type="number")
|
25
25
|
if available:
|
26
|
-
form.add_field("allowlist", value=list(available))
|
26
|
+
form.add_field("allowlist", value=list(available), type="text-multi")
|
27
27
|
return form
|
28
28
|
|
29
29
|
async def available_emojis(
|
slidge/db/store.py
CHANGED
@@ -18,9 +18,16 @@ from sqlalchemy.sql.functions import count
|
|
18
18
|
|
19
19
|
from ..core import config
|
20
20
|
from ..util.archive_msg import HistoryMessage
|
21
|
-
from ..util.types import
|
21
|
+
from ..util.types import (
|
22
|
+
URL,
|
23
|
+
CachedPresence,
|
24
|
+
ClientType,
|
25
|
+
MamMetadata,
|
26
|
+
MucAffiliation,
|
27
|
+
MucRole,
|
28
|
+
Sticker,
|
29
|
+
)
|
22
30
|
from ..util.types import Hat as HatTuple
|
23
|
-
from ..util.types import MamMetadata, MucAffiliation, MucRole, Sticker
|
24
31
|
from .meta import Base
|
25
32
|
from .models import (
|
26
33
|
ArchivedMessage,
|
@@ -545,7 +552,6 @@ class MAMStore(EngineMixin):
|
|
545
552
|
sender: Optional[str] = None,
|
546
553
|
flip=False,
|
547
554
|
) -> Iterator[HistoryMessage]:
|
548
|
-
|
549
555
|
with self.session() as session:
|
550
556
|
q = select(ArchivedMessage).where(ArchivedMessage.room_id == room_pk)
|
551
557
|
if start_date is not None:
|
slidge/group/archive.py
CHANGED
@@ -49,9 +49,9 @@ class MessageArchive:
|
|
49
49
|
new_msg["muc"]["jid"] = participant.muc.jid
|
50
50
|
else:
|
51
51
|
log.warning("No real JID for participant in this group")
|
52
|
-
new_msg["muc"][
|
53
|
-
"
|
54
|
-
|
52
|
+
new_msg["muc"]["jid"] = (
|
53
|
+
f"{uuid.uuid4()}@{participant.xmpp.boundjid.bare}"
|
54
|
+
)
|
55
55
|
|
56
56
|
self.__store.add_message(
|
57
57
|
self.room_pk,
|
slidge/group/participant.py
CHANGED
@@ -322,7 +322,10 @@ class LegacyParticipant(
|
|
322
322
|
legacy_msg_id=None,
|
323
323
|
**send_kwargs,
|
324
324
|
) -> MessageOrPresenceTypeVar:
|
325
|
-
stanza
|
325
|
+
if stanza.get_from().resource:
|
326
|
+
stanza["occupant-id"]["id"] = self.__occupant_id
|
327
|
+
else:
|
328
|
+
stanza["occupant-id"]["id"] = "room"
|
326
329
|
self.__add_nick_element(stanza)
|
327
330
|
if not self.is_user and isinstance(stanza, Presence):
|
328
331
|
if stanza["type"] == "unavailable" and not self._presence_sent:
|
@@ -459,11 +462,14 @@ class LegacyParticipant(
|
|
459
462
|
|
460
463
|
for i in msg_ids:
|
461
464
|
m = self.muc.get_system_participant()._make_message()
|
462
|
-
m["
|
463
|
-
|
464
|
-
|
465
|
+
m["retract"]["id"] = i
|
466
|
+
if self.is_system:
|
467
|
+
m["retract"].enable("moderated")
|
468
|
+
else:
|
469
|
+
m["retract"]["moderated"]["by"] = self.jid
|
470
|
+
m["retract"]["moderated"]["occupant-id"]["id"] = self.__occupant_id
|
465
471
|
if reason:
|
466
|
-
m["
|
472
|
+
m["retract"]["reason"] = reason
|
467
473
|
self._send(m)
|
468
474
|
|
469
475
|
def set_room_subject(
|
slidge/group/room.py
CHANGED
@@ -26,6 +26,7 @@ from ..core.mixins.disco import ChatterDiscoMixin
|
|
26
26
|
from ..core.mixins.lock import NamedLockMixin
|
27
27
|
from ..core.mixins.recipient import ReactionRecipientMixin, ThreadRecipientMixin
|
28
28
|
from ..db.models import Room
|
29
|
+
from ..slixfix.xep_0492.stanza import WhenLiteral
|
29
30
|
from ..util import ABCSubclassableOnceAtMost
|
30
31
|
from ..util.types import (
|
31
32
|
HoleBound,
|
@@ -994,7 +995,14 @@ class LegacyMUC(
|
|
994
995
|
p["muc"]["status_codes"] = {110, 333}
|
995
996
|
p.send()
|
996
997
|
|
997
|
-
async def add_to_bookmarks(
|
998
|
+
async def add_to_bookmarks(
|
999
|
+
self,
|
1000
|
+
auto_join=True,
|
1001
|
+
invite=False,
|
1002
|
+
preserve=True,
|
1003
|
+
pin: bool | None = None,
|
1004
|
+
notify: WhenLiteral | None = None,
|
1005
|
+
):
|
998
1006
|
"""
|
999
1007
|
Add the MUC to the user's XMPP bookmarks (:xep:`0402')
|
1000
1008
|
|
@@ -1012,6 +1020,11 @@ class LegacyMUC(
|
|
1012
1020
|
settings.
|
1013
1021
|
:param preserve: preserve auto-join and bookmarks extensions
|
1014
1022
|
set by the user outside slidge
|
1023
|
+
:param pin: Pin the group chat bookmark :xep:`0469`. Requires privileged entity.
|
1024
|
+
If set to ``None`` (default), the bookmark pinning status will be untouched.
|
1025
|
+
:param notify: Chat notification setting: :xep:`0492`. Requires privileged entity.
|
1026
|
+
If set to ``None`` (default), the setting will be untouched. Only the "global"
|
1027
|
+
notification setting is supported (ie, per client type is not possible).
|
1015
1028
|
"""
|
1016
1029
|
item = Item()
|
1017
1030
|
item["id"] = self.jid
|
@@ -1046,6 +1059,13 @@ class LegacyMUC(
|
|
1046
1059
|
item["conference"]["autojoin"] = auto_join
|
1047
1060
|
|
1048
1061
|
item["conference"]["nick"] = self.user_nick
|
1062
|
+
|
1063
|
+
if pin is not None:
|
1064
|
+
item["conference"]["extensions"]["pinned"] = pin
|
1065
|
+
|
1066
|
+
if notify is not None:
|
1067
|
+
item["conference"]["extensions"]["notify"].configure(notify)
|
1068
|
+
|
1049
1069
|
iq = Iq(stype="set", sfrom=self.user_jid, sto=self.user_jid)
|
1050
1070
|
iq["pubsub"]["publish"]["node"] = self.xmpp["xep_0402"].stanza.NS
|
1051
1071
|
iq["pubsub"]["publish"].append(item)
|
slidge/main.py
CHANGED
@@ -10,7 +10,7 @@ Use the long version of the CLI arg without the double dash prefix inside this
|
|
10
10
|
INI file, eg ``debug=true``.
|
11
11
|
|
12
12
|
An example configuration file is available at
|
13
|
-
https://
|
13
|
+
https://codeberg.org/slidge/slidge/src/branch/main/dev/confs/slidge-example.ini
|
14
14
|
"""
|
15
15
|
|
16
16
|
import asyncio
|
slidge/slixfix/__init__.py
CHANGED
@@ -5,25 +5,30 @@
|
|
5
5
|
|
6
6
|
import slixmpp.plugins
|
7
7
|
from slixmpp import Iq, Message
|
8
|
-
from slixmpp.exceptions import XMPPError
|
8
|
+
from slixmpp.exceptions import _DEFAULT_ERROR_TYPES, XMPPError
|
9
|
+
from slixmpp.plugins.xep_0004.stanza.field import FormField
|
9
10
|
from slixmpp.plugins.xep_0050 import XEP_0050, Command
|
10
11
|
from slixmpp.plugins.xep_0231 import XEP_0231
|
12
|
+
from slixmpp.plugins.xep_0425.stanza import Moderate
|
13
|
+
from slixmpp.plugins.xep_0469.stanza import NS as PINNED_NS
|
14
|
+
from slixmpp.plugins.xep_0469.stanza import Pinned
|
11
15
|
from slixmpp.xmlstream import StanzaBase
|
12
16
|
|
13
|
-
from . import (
|
17
|
+
from . import (
|
14
18
|
link_preview,
|
15
19
|
xep_0077,
|
16
20
|
xep_0100,
|
17
21
|
xep_0153,
|
18
|
-
xep_0264,
|
19
22
|
xep_0292,
|
20
|
-
xep_0313,
|
21
|
-
xep_0317,
|
22
23
|
xep_0356_old,
|
23
|
-
|
24
|
-
xep_0490,
|
24
|
+
xep_0492,
|
25
25
|
)
|
26
26
|
|
27
|
+
# TODO: remove this when the fix is included in slixmpp
|
28
|
+
Moderate.interfaces.add("id")
|
29
|
+
|
30
|
+
_DEFAULT_ERROR_TYPES["policy-violation"] = "modify" # type:ignore
|
31
|
+
|
27
32
|
|
28
33
|
async def _handle_bob_iq(self, iq: Iq):
|
29
34
|
cid = iq["bob"]["cid"]
|
@@ -50,6 +55,17 @@ async def _handle_bob_iq(self, iq: Iq):
|
|
50
55
|
iq.send()
|
51
56
|
|
52
57
|
|
58
|
+
def set_pinned(self, val: bool):
|
59
|
+
extensions = self.parent()
|
60
|
+
if val:
|
61
|
+
extensions.enable("pinned")
|
62
|
+
else:
|
63
|
+
extensions._del_sub(f"{{{PINNED_NS}}}pinned")
|
64
|
+
|
65
|
+
|
66
|
+
Pinned.set_pinned = set_pinned
|
67
|
+
|
68
|
+
|
53
69
|
XEP_0231._handle_bob_iq = _handle_bob_iq
|
54
70
|
|
55
71
|
|
@@ -83,16 +99,56 @@ def reply(self, body=None, clear=True):
|
|
83
99
|
return new_message
|
84
100
|
|
85
101
|
|
102
|
+
Message.reply = reply # type: ignore
|
103
|
+
|
104
|
+
|
105
|
+
FormField.set_value_base = FormField.set_value # type:ignore
|
106
|
+
|
107
|
+
|
108
|
+
def set_value(self: FormField, value):
|
109
|
+
if not self._type:
|
110
|
+
if isinstance(value, bool):
|
111
|
+
self._type = "boolean"
|
112
|
+
elif isinstance(value, str):
|
113
|
+
self._type = "text-single"
|
114
|
+
elif isinstance(value, (list, tuple)):
|
115
|
+
self._type = "text-multi"
|
116
|
+
|
117
|
+
FormField.set_value_base(self, value)
|
118
|
+
|
119
|
+
|
120
|
+
def get_value(self, convert=True, convert_list=False):
|
121
|
+
valsXML = self.xml.findall("{%s}value" % self.namespace)
|
122
|
+
if len(valsXML) == 0:
|
123
|
+
return None
|
124
|
+
elif self._type == "boolean":
|
125
|
+
if convert:
|
126
|
+
return valsXML[0].text in self.true_values
|
127
|
+
return valsXML[0].text
|
128
|
+
elif self._type in self.multi_value_types or len(valsXML) > 1:
|
129
|
+
values = []
|
130
|
+
for valXML in valsXML:
|
131
|
+
if valXML.text is None:
|
132
|
+
valXML.text = ""
|
133
|
+
values.append(valXML.text)
|
134
|
+
if self._type == "text-multi" and convert_list:
|
135
|
+
values = "\n".join(values)
|
136
|
+
return values
|
137
|
+
else:
|
138
|
+
if valsXML[0].text is None:
|
139
|
+
return ""
|
140
|
+
return valsXML[0].text
|
141
|
+
|
142
|
+
|
143
|
+
FormField.set_value = set_value # type:ignore
|
144
|
+
FormField.get_value = get_value # type:ignore
|
145
|
+
|
146
|
+
|
86
147
|
slixmpp.plugins.PLUGINS.extend(
|
87
148
|
[
|
88
149
|
"link_preview",
|
89
|
-
"xep_0264",
|
90
150
|
"xep_0292_provider",
|
91
|
-
"xep_0317",
|
92
151
|
"xep_0356_old",
|
93
|
-
"
|
152
|
+
"xep_0492",
|
94
153
|
]
|
95
154
|
)
|
96
|
-
|
97
|
-
|
98
|
-
Message.reply = reply # type: ignore
|
@@ -1,17 +1,8 @@
|
|
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
1
|
from slixmpp.plugins.base import BasePlugin
|
2
|
+
from slixmpp.plugins.xep_0153 import VCardTempUpdate, stanza
|
8
3
|
from slixmpp.stanza import Presence
|
9
4
|
from slixmpp.xmlstream import register_stanza_plugin
|
10
5
|
|
11
|
-
from . import VCardTempUpdate, stanza
|
12
|
-
|
13
|
-
log = logging.getLogger(__name__)
|
14
|
-
|
15
6
|
|
16
7
|
class XEP_0153(BasePlugin):
|
17
8
|
name = "xep_0153"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from slixmpp.plugins import BasePlugin
|
2
|
+
from . import stanza
|
3
|
+
|
4
|
+
|
5
|
+
class XEP_0492(BasePlugin):
|
6
|
+
"""
|
7
|
+
XEP-0492: Chat notification settings
|
8
|
+
"""
|
9
|
+
|
10
|
+
name = "xep_0492"
|
11
|
+
description = "XEP-0492: Chat notification settings"
|
12
|
+
dependencies = {"xep_0402"}
|
13
|
+
stanza = stanza
|
14
|
+
|
15
|
+
def plugin_init(self):
|
16
|
+
stanza.register_plugin()
|
@@ -0,0 +1,102 @@
|
|
1
|
+
from typing import Literal, Optional, cast
|
2
|
+
|
3
|
+
from slixmpp import register_stanza_plugin
|
4
|
+
from slixmpp.plugins.xep_0402.stanza import Extensions
|
5
|
+
from slixmpp.xmlstream import ElementBase
|
6
|
+
|
7
|
+
from ...util.types import ClientType
|
8
|
+
|
9
|
+
NS = "urn:xmpp:notification-settings:0"
|
10
|
+
|
11
|
+
WhenLiteral = Literal["never", "always", "on-mention"]
|
12
|
+
|
13
|
+
|
14
|
+
class Notify(ElementBase):
|
15
|
+
"""
|
16
|
+
Chat notification settings element
|
17
|
+
|
18
|
+
|
19
|
+
To enable it on a Conference element, use configure() like this:
|
20
|
+
|
21
|
+
.. code-block::python
|
22
|
+
|
23
|
+
# C being a Conference element
|
24
|
+
C['extensions']["notify"].configure("always", client_type="pc")
|
25
|
+
|
26
|
+
Which will add the <notify> element to the <extensions> element.
|
27
|
+
"""
|
28
|
+
|
29
|
+
namespace = NS
|
30
|
+
name = "notify"
|
31
|
+
plugin_attrib = "notify"
|
32
|
+
interfaces = {"notify"}
|
33
|
+
|
34
|
+
def configure(self, when: WhenLiteral, client_type: Optional[ClientType] = None) -> None:
|
35
|
+
"""
|
36
|
+
Configure the chat notification settings for this bookmark.
|
37
|
+
|
38
|
+
This method ensures that there are no conflicting settings, e.g.,
|
39
|
+
both a <never /> and a <always /> element.
|
40
|
+
"""
|
41
|
+
cls = _CLASS_MAP[when]
|
42
|
+
element = cls()
|
43
|
+
if client_type is not None:
|
44
|
+
element["client-type"] = client_type
|
45
|
+
|
46
|
+
match = client_type if client_type is not None else ""
|
47
|
+
for child in self:
|
48
|
+
if isinstance(child, _Base) and child["client-type"] == match:
|
49
|
+
self.xml.remove(child.xml)
|
50
|
+
|
51
|
+
self.append(element)
|
52
|
+
|
53
|
+
def get_config(
|
54
|
+
self, client_type: Optional[ClientType] = None
|
55
|
+
) -> Optional[WhenLiteral]:
|
56
|
+
"""
|
57
|
+
Get the chat notification settings for this bookmark.
|
58
|
+
|
59
|
+
:param client_type: Optionally, get the notification for a specific client type.
|
60
|
+
If unset, returns the global notification setting.
|
61
|
+
|
62
|
+
:return: The chat notification setting as a string, or None if unset.
|
63
|
+
"""
|
64
|
+
match = client_type if client_type is not None else ""
|
65
|
+
for child in self:
|
66
|
+
if isinstance(child, _Base) and child["client-type"] == match:
|
67
|
+
return cast(WhenLiteral, child.name)
|
68
|
+
return None
|
69
|
+
|
70
|
+
|
71
|
+
class _Base(ElementBase):
|
72
|
+
namespace = NS
|
73
|
+
interfaces = {"client-type"}
|
74
|
+
|
75
|
+
|
76
|
+
class Never(_Base):
|
77
|
+
name = "never"
|
78
|
+
|
79
|
+
|
80
|
+
class Always(_Base):
|
81
|
+
name = "always"
|
82
|
+
|
83
|
+
|
84
|
+
class OnMention(_Base):
|
85
|
+
name = "on-mention"
|
86
|
+
|
87
|
+
|
88
|
+
class Advanced(ElementBase):
|
89
|
+
namespace = NS
|
90
|
+
name = plugin_attrib = "advanced"
|
91
|
+
|
92
|
+
|
93
|
+
_CLASS_MAP = {
|
94
|
+
"never": Never,
|
95
|
+
"always": Always,
|
96
|
+
"on-mention": OnMention,
|
97
|
+
}
|
98
|
+
|
99
|
+
|
100
|
+
def register_plugin():
|
101
|
+
register_stanza_plugin(Extensions, Notify)
|
102
|
+
register_stanza_plugin(Notify, Advanced)
|
slidge/util/test.py
CHANGED
@@ -5,7 +5,14 @@ from pathlib import Path
|
|
5
5
|
from typing import Optional, Union
|
6
6
|
from xml.dom.minidom import parseString
|
7
7
|
|
8
|
-
|
8
|
+
try:
|
9
|
+
import xmldiff.main
|
10
|
+
|
11
|
+
XML_DIFF_PRESENT = True
|
12
|
+
except ImportError:
|
13
|
+
xmldiff = None
|
14
|
+
XML_DIFF_PRESENT = False
|
15
|
+
|
9
16
|
from slixmpp import (
|
10
17
|
JID,
|
11
18
|
ElementBase,
|
@@ -157,7 +164,7 @@ class SlixTestPlus(SlixTest):
|
|
157
164
|
result = self.compare(xml, stanza.xml, stanza2.xml)
|
158
165
|
stanza_class.namespace = old_ns
|
159
166
|
|
160
|
-
if not result:
|
167
|
+
if XML_DIFF_PRESENT and not result:
|
161
168
|
debug += str(
|
162
169
|
xmldiff.main.diff_texts(tostring(xml), tostring(stanza.xml))
|
163
170
|
)
|