slidge 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
)
|