slidge 0.2.8__py3-none-any.whl → 0.2.10__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/__init__.py +9 -1
- slidge/command/admin.py +3 -3
- slidge/core/gateway.py +6 -3
- slidge/core/mixins/attachment.py +2 -2
- slidge/core/pubsub.py +6 -2
- slidge/core/session.py +2 -2
- slidge/db/store.py +1 -0
- slidge/group/participant.py +2 -0
- slidge/group/room.py +6 -3
- slidge/main.py +15 -9
- slidge/slixfix/__init__.py +69 -20
- slidge/util/conf.py +14 -4
- slidge/util/types.py +1 -7
- slidge/util/util.py +0 -13
- slidge-0.2.10.dist-info/METADATA +133 -0
- {slidge-0.2.8.dist-info → slidge-0.2.10.dist-info}/RECORD +20 -20
- {slidge-0.2.8.dist-info → slidge-0.2.10.dist-info}/WHEEL +1 -1
- slidge-0.2.10.dist-info/licenses/LICENSE +661 -0
- slidge/__version__.py +0 -5
- slidge-0.2.8.dist-info/METADATA +0 -793
- {slidge-0.2.8.dist-info → slidge-0.2.10.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.8.dist-info → slidge-0.2.10.dist-info}/top_level.txt +0 -0
slidge/__init__.py
CHANGED
@@ -6,6 +6,7 @@ Contains importable classes for a minimal function :term:`Legacy Module`.
|
|
6
6
|
|
7
7
|
import sys
|
8
8
|
import warnings
|
9
|
+
from importlib.metadata import PackageNotFoundError, version
|
9
10
|
|
10
11
|
from . import slixfix # noqa: F401
|
11
12
|
from .command import FormField, SearchResult # noqa: F401
|
@@ -28,7 +29,7 @@ def entrypoint(module_name: str) -> None:
|
|
28
29
|
:param module_name: An importable :term:`Legacy Module`.
|
29
30
|
"""
|
30
31
|
sys.argv.extend(["--legacy", module_name])
|
31
|
-
main_func()
|
32
|
+
main_func(module_name)
|
32
33
|
|
33
34
|
|
34
35
|
def formatwarning(message, category, filename, lineno, line=""):
|
@@ -37,8 +38,15 @@ def formatwarning(message, category, filename, lineno, line=""):
|
|
37
38
|
|
38
39
|
warnings.formatwarning = formatwarning
|
39
40
|
|
41
|
+
try:
|
42
|
+
__version__ = version("slidge")
|
43
|
+
except PackageNotFoundError:
|
44
|
+
# package is not installed
|
45
|
+
__version__ = "dev"
|
46
|
+
|
40
47
|
|
41
48
|
__all__ = [
|
49
|
+
"__version__",
|
42
50
|
"BaseGateway",
|
43
51
|
"BaseSession",
|
44
52
|
# For backwards compatibility, these names are still importable from the
|
slidge/command/admin.py
CHANGED
@@ -60,8 +60,6 @@ class SlidgeInfo(AdminCommand):
|
|
60
60
|
ACCESS = CommandAccess.ANY
|
61
61
|
|
62
62
|
async def run(self, _session, _ifrom, *_):
|
63
|
-
from slidge.__version__ import __version__
|
64
|
-
|
65
63
|
start = self.xmpp.datetime_started # type:ignore
|
66
64
|
uptime = datetime.now() - start
|
67
65
|
|
@@ -100,8 +98,10 @@ class SlidgeInfo(AdminCommand):
|
|
100
98
|
legacy_module = importlib.import_module(config.LEGACY_MODULE)
|
101
99
|
version = getattr(legacy_module, "__version__", "No version")
|
102
100
|
|
101
|
+
import slidge
|
102
|
+
|
103
103
|
return (
|
104
|
-
f"{self.xmpp.COMPONENT_NAME} (slidge core {__version__},"
|
104
|
+
f"{self.xmpp.COMPONENT_NAME} (slidge core {slidge.__version__},"
|
105
105
|
f" {config.LEGACY_MODULE} {version})\n"
|
106
106
|
f"Up since {start:%Y-%m-%d %H:%M} ({ago} ago)"
|
107
107
|
)
|
slidge/core/gateway.py
CHANGED
@@ -31,6 +31,7 @@ from slidge.core.pubsub import PubSubComponent
|
|
31
31
|
from slidge.core.session import BaseSession
|
32
32
|
from slidge.db import GatewayUser, SlidgeStore
|
33
33
|
from slidge.db.avatar import avatar_cache
|
34
|
+
from slidge.slixfix import PrivilegedIqError
|
34
35
|
from slidge.slixfix.delivery_receipt import DeliveryReceipt
|
35
36
|
from slidge.slixfix.roster import RosterBackend
|
36
37
|
from slidge.util import ABCSubclassableOnceAtMost
|
@@ -474,11 +475,12 @@ class BaseGateway(
|
|
474
475
|
"create the MDS node of %s",
|
475
476
|
user_jid,
|
476
477
|
)
|
477
|
-
except
|
478
|
+
except PrivilegedIqError as exc:
|
479
|
+
nested = exc.nested_error()
|
478
480
|
# conflict this means the node already exists, we can ignore that
|
479
|
-
if
|
481
|
+
if nested is not None and nested.condition != "conflict":
|
480
482
|
log.exception(
|
481
|
-
"Could not create the MDS node of %s", user_jid, exc_info=
|
483
|
+
"Could not create the MDS node of %s", user_jid, exc_info=exc
|
482
484
|
)
|
483
485
|
except Exception as e:
|
484
486
|
log.exception(
|
@@ -526,6 +528,7 @@ class BaseGateway(
|
|
526
528
|
"You are not connected to this gateway! "
|
527
529
|
f"Maybe this message will tell you why: {e}"
|
528
530
|
)
|
531
|
+
session.logged = False
|
529
532
|
return
|
530
533
|
|
531
534
|
log.info("Login success for %s", session.user_jid)
|
slidge/core/mixins/attachment.py
CHANGED
@@ -46,7 +46,7 @@ class AttachmentMixin(TextMessageMixin):
|
|
46
46
|
file_path: Path,
|
47
47
|
file_name: Optional[str] = None,
|
48
48
|
content_type: Optional[str] = None,
|
49
|
-
):
|
49
|
+
) -> str | None:
|
50
50
|
if file_name and file_path.name != file_name:
|
51
51
|
d = Path(tempfile.mkdtemp())
|
52
52
|
temp = d / file_name
|
@@ -215,7 +215,7 @@ class AttachmentMixin(TextMessageMixin):
|
|
215
215
|
else:
|
216
216
|
local_path = file_path
|
217
217
|
new_url = await self.__upload(file_path, file_name, content_type)
|
218
|
-
if legacy_file_id:
|
218
|
+
if legacy_file_id and new_url is not None:
|
219
219
|
self.__store.set_url(self.session.user_pk, str(legacy_file_id), new_url)
|
220
220
|
|
221
221
|
return is_temp, local_path, new_url
|
slidge/core/pubsub.py
CHANGED
@@ -11,7 +11,7 @@ from slixmpp import (
|
|
11
11
|
StanzaPath,
|
12
12
|
register_stanza_plugin,
|
13
13
|
)
|
14
|
-
from slixmpp.exceptions import XMPPError
|
14
|
+
from slixmpp.exceptions import IqError, IqTimeout, XMPPError
|
15
15
|
from slixmpp.plugins.base import BasePlugin, register_plugin
|
16
16
|
from slixmpp.plugins.xep_0060.stanza import Event, EventItem, EventItems, Item
|
17
17
|
from slixmpp.plugins.xep_0084 import Data as AvatarData
|
@@ -134,7 +134,11 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
134
134
|
info = None
|
135
135
|
if info is None:
|
136
136
|
async with self.lock(from_):
|
137
|
-
|
137
|
+
try:
|
138
|
+
iq = await self.xmpp.plugin["xep_0030"].get_info(from_)
|
139
|
+
except (IqError, IqTimeout):
|
140
|
+
log.debug("Could get disco#info of %s, ignoring", from_)
|
141
|
+
return []
|
138
142
|
info = iq["disco_info"]
|
139
143
|
return info["features"]
|
140
144
|
|
slidge/core/session.py
CHANGED
@@ -543,10 +543,10 @@ class BaseSession(
|
|
543
543
|
return f"<Session of {self.user_jid}>"
|
544
544
|
|
545
545
|
def shutdown(self, logout=True) -> asyncio.Task:
|
546
|
-
for c in self.contacts:
|
547
|
-
c.offline()
|
548
546
|
for m in self.bookmarks:
|
549
547
|
m.shutdown()
|
548
|
+
for c in self.contacts:
|
549
|
+
c.offline()
|
550
550
|
if logout:
|
551
551
|
return self.xmpp.loop.create_task(self.logout())
|
552
552
|
else:
|
slidge/db/store.py
CHANGED
@@ -1076,6 +1076,7 @@ class ParticipantStore(EngineMixin):
|
|
1076
1076
|
update(Participant)
|
1077
1077
|
.where(Participant.id == participant.pk)
|
1078
1078
|
.values(
|
1079
|
+
nickname=participant.nickname,
|
1079
1080
|
resource=participant.jid.resource,
|
1080
1081
|
nickname_no_illegal=participant._nickname_no_illegal,
|
1081
1082
|
affiliation=participant.affiliation,
|
slidge/group/participant.py
CHANGED
slidge/group/room.py
CHANGED
@@ -648,7 +648,7 @@ class LegacyMUC(
|
|
648
648
|
since = self.xmpp.plugin["xep_0082"].parse(history_params["since"])
|
649
649
|
except ValueError:
|
650
650
|
since = None
|
651
|
-
if seconds:
|
651
|
+
if seconds is not None:
|
652
652
|
since = datetime.now() - timedelta(seconds=seconds)
|
653
653
|
if equals_zero(maxchars) or equals_zero(maxstanzas):
|
654
654
|
log.debug("Joining client does not want any old-school MUC history-on-join")
|
@@ -1016,8 +1016,11 @@ class LegacyMUC(
|
|
1016
1016
|
item = ans["pubsub"]["items"]["item"]
|
1017
1017
|
item["id"] = self.jid
|
1018
1018
|
return item
|
1019
|
-
except
|
1020
|
-
warnings.warn(f"Cannot fetch bookmark
|
1019
|
+
except IqTimeout as exc:
|
1020
|
+
warnings.warn(f"Cannot fetch bookmark for {self.user_jid}: timeout")
|
1021
|
+
return None
|
1022
|
+
except IqError as exc:
|
1023
|
+
warnings.warn(f"Cannot fetch bookmark for {self.user_jid}: {exc}")
|
1021
1024
|
return None
|
1022
1025
|
except PermissionError:
|
1023
1026
|
warnings.warn(
|
slidge/main.py
CHANGED
@@ -24,8 +24,8 @@ from pathlib import Path
|
|
24
24
|
|
25
25
|
import configargparse
|
26
26
|
|
27
|
+
import slidge
|
27
28
|
from slidge import BaseGateway
|
28
|
-
from slidge.__version__ import __version__
|
29
29
|
from slidge.core import config
|
30
30
|
from slidge.core.pubsub import PepAvatar, PepNick
|
31
31
|
from slidge.db import SlidgeStore
|
@@ -60,7 +60,7 @@ class SigTermInterrupt(Exception):
|
|
60
60
|
pass
|
61
61
|
|
62
62
|
|
63
|
-
def get_configurator():
|
63
|
+
def get_configurator(from_entrypoint: bool = False):
|
64
64
|
p = configargparse.ArgumentParser(
|
65
65
|
default_config_files=os.getenv(
|
66
66
|
"SLIDGE_CONF_DIR", "/etc/slidge/conf.d/*.conf"
|
@@ -96,9 +96,11 @@ def get_configurator():
|
|
96
96
|
p.add_argument(
|
97
97
|
"--version",
|
98
98
|
action="version",
|
99
|
-
version=f"%(prog)s {__version__}",
|
99
|
+
version=f"%(prog)s {slidge.__version__}",
|
100
|
+
)
|
101
|
+
configurator = MainConfig(
|
102
|
+
config, p, skip_options=("legacy_module",) if from_entrypoint else ()
|
100
103
|
)
|
101
|
-
configurator = MainConfig(config, p)
|
102
104
|
return configurator
|
103
105
|
|
104
106
|
|
@@ -106,8 +108,8 @@ def get_parser():
|
|
106
108
|
return get_configurator().parser
|
107
109
|
|
108
110
|
|
109
|
-
def configure():
|
110
|
-
configurator = get_configurator()
|
111
|
+
def configure(from_entrypoint: bool):
|
112
|
+
configurator = get_configurator(from_entrypoint)
|
111
113
|
args, unknown_argv = configurator.set_conf()
|
112
114
|
|
113
115
|
if not (h := config.HOME_DIR).exists():
|
@@ -124,11 +126,15 @@ def handle_sigterm(_signum, _frame):
|
|
124
126
|
raise SigTermInterrupt
|
125
127
|
|
126
128
|
|
127
|
-
def main():
|
129
|
+
def main(module_name: str | None = None) -> None:
|
130
|
+
from_entrypoint = module_name is None
|
128
131
|
signal.signal(signal.SIGTERM, handle_sigterm)
|
129
132
|
|
130
|
-
unknown_argv = configure()
|
131
|
-
logging.info("Starting slidge version %s", __version__)
|
133
|
+
unknown_argv = configure(from_entrypoint)
|
134
|
+
logging.info("Starting slidge version %s", slidge.__version__)
|
135
|
+
|
136
|
+
if module_name is not None:
|
137
|
+
config.LEGACY_MODULE = module_name
|
132
138
|
|
133
139
|
legacy_module = importlib.import_module(config.LEGACY_MODULE)
|
134
140
|
logging.debug("Legacy module: %s", dir(legacy_module))
|
slidge/slixfix/__init__.py
CHANGED
@@ -3,12 +3,15 @@
|
|
3
3
|
|
4
4
|
# ruff: noqa: F401
|
5
5
|
|
6
|
-
import
|
6
|
+
import uuid
|
7
7
|
|
8
8
|
import slixmpp.plugins
|
9
9
|
import slixmpp.stanza.roster
|
10
|
-
from slixmpp import
|
10
|
+
from slixmpp import Message
|
11
|
+
from slixmpp.exceptions import IqError
|
11
12
|
from slixmpp.plugins.xep_0050 import XEP_0050, Command
|
13
|
+
from slixmpp.plugins.xep_0356.permissions import IqPermission
|
14
|
+
from slixmpp.plugins.xep_0356.privilege import XEP_0356
|
12
15
|
from slixmpp.plugins.xep_0469.stanza import NS as PINNED_NS
|
13
16
|
from slixmpp.plugins.xep_0469.stanza import Pinned
|
14
17
|
from slixmpp.xmlstream import StanzaBase
|
@@ -22,24 +25,6 @@ from . import (
|
|
22
25
|
)
|
23
26
|
|
24
27
|
|
25
|
-
# TODO: remove this when we pin slixmpp > 1.9.0
|
26
|
-
def get_items(self):
|
27
|
-
items = {}
|
28
|
-
for item in self["substanzas"]:
|
29
|
-
if isinstance(item, slixmpp.stanza.roster.RosterItem):
|
30
|
-
try:
|
31
|
-
items[item["jid"]] = item.values
|
32
|
-
except InvalidJID:
|
33
|
-
logging.warning("Invalid JID in roster: %s", item)
|
34
|
-
continue
|
35
|
-
del items[item["jid"]]["jid"]
|
36
|
-
del items[item["jid"]]["lang"]
|
37
|
-
return items
|
38
|
-
|
39
|
-
|
40
|
-
slixmpp.stanza.roster.Roster.get_items = get_items # type:ignore
|
41
|
-
|
42
|
-
|
43
28
|
def set_pinned(self, val: bool):
|
44
29
|
extensions = self.parent()
|
45
30
|
if val:
|
@@ -83,6 +68,70 @@ def reply(self, body=None, clear=True):
|
|
83
68
|
|
84
69
|
Message.reply = reply # type: ignore
|
85
70
|
|
71
|
+
# TODO: remove me when https://codeberg.org/poezio/slixmpp/pulls/3622 is merged
|
72
|
+
|
73
|
+
|
74
|
+
class PrivilegedIqError(IqError):
|
75
|
+
"""
|
76
|
+
Exception raised when sending a privileged IQ stanza fails.
|
77
|
+
"""
|
78
|
+
|
79
|
+
def nested_error(self) -> IqError | None:
|
80
|
+
"""
|
81
|
+
Return the IQError generated from the inner IQ stanza, if present.
|
82
|
+
"""
|
83
|
+
if "privilege" in self.iq:
|
84
|
+
if "forwarded" in self.iq["privilege"]:
|
85
|
+
if "iq" in self.iq["privilege"]["forwarded"]:
|
86
|
+
return IqError(self.iq["privilege"]["forwarded"]["iq"])
|
87
|
+
return None
|
88
|
+
|
89
|
+
|
90
|
+
async def send_privileged_iq(self, encapsulated_iq, iq_id=None):
|
91
|
+
"""
|
92
|
+
Send an IQ on behalf of a user
|
93
|
+
|
94
|
+
Caution: the IQ *must* have the jabber:client namespace
|
95
|
+
|
96
|
+
Raises :class:`PrivilegedIqError` on failure.
|
97
|
+
"""
|
98
|
+
iq_id = iq_id or str(uuid.uuid4())
|
99
|
+
encapsulated_iq["id"] = iq_id
|
100
|
+
server = encapsulated_iq.get_to().domain
|
101
|
+
perms = self.granted_privileges.get(server)
|
102
|
+
if not perms:
|
103
|
+
raise PermissionError(f"{server} has not granted us any privilege")
|
104
|
+
itype = encapsulated_iq["type"]
|
105
|
+
for ns in encapsulated_iq.plugins.values():
|
106
|
+
type_ = perms.iq[ns.namespace]
|
107
|
+
if type_ == IqPermission.NONE:
|
108
|
+
raise PermissionError(
|
109
|
+
f"{server} has not granted any IQ privilege for namespace {ns.namespace}"
|
110
|
+
)
|
111
|
+
elif type_ == IqPermission.BOTH:
|
112
|
+
pass
|
113
|
+
elif type_ != itype:
|
114
|
+
raise PermissionError(
|
115
|
+
f"{server} has not granted IQ {itype} privilege for namespace {ns.namespace}"
|
116
|
+
)
|
117
|
+
iq = self.xmpp.make_iq(
|
118
|
+
itype=itype,
|
119
|
+
ifrom=self.xmpp.boundjid.bare,
|
120
|
+
ito=encapsulated_iq.get_from(),
|
121
|
+
id=iq_id,
|
122
|
+
)
|
123
|
+
iq["privileged_iq"].append(encapsulated_iq)
|
124
|
+
|
125
|
+
try:
|
126
|
+
resp = await iq.send()
|
127
|
+
except IqError as exc:
|
128
|
+
raise PrivilegedIqError(exc.iq)
|
129
|
+
|
130
|
+
return resp["privilege"]["forwarded"]["iq"]
|
131
|
+
|
132
|
+
|
133
|
+
XEP_0356.send_privileged_iq = send_privileged_iq
|
134
|
+
|
86
135
|
|
87
136
|
slixmpp.plugins.PLUGINS.extend(
|
88
137
|
[
|
slidge/util/conf.py
CHANGED
@@ -95,20 +95,27 @@ class ConfigModule:
|
|
95
95
|
ENV_VAR_PREFIX = "SLIDGE_"
|
96
96
|
|
97
97
|
def __init__(
|
98
|
-
self,
|
98
|
+
self,
|
99
|
+
config_obj,
|
100
|
+
parser: Optional[configargparse.ArgumentParser] = None,
|
101
|
+
skip_options: tuple[str, ...] = (),
|
99
102
|
):
|
100
103
|
self.config_obj = config_obj
|
101
104
|
if parser is None:
|
102
105
|
parser = configargparse.ArgumentParser()
|
103
106
|
self.parser = parser
|
104
107
|
|
105
|
-
self.
|
108
|
+
self.skip_options = skip_options
|
109
|
+
self.add_options_to_parser(skip_options)
|
106
110
|
|
107
111
|
def _list_options(self):
|
108
112
|
return {
|
109
113
|
o
|
110
114
|
for o in (set(dir(self.config_obj)) | set(get_type_hints(self.config_obj)))
|
111
|
-
if o.upper() == o
|
115
|
+
if o.upper() == o
|
116
|
+
and not o.startswith("_")
|
117
|
+
and "__" not in o
|
118
|
+
and o.lower() not in self.skip_options
|
112
119
|
}
|
113
120
|
|
114
121
|
def set_conf(self, argv: Optional[list[str]] = None):
|
@@ -179,9 +186,12 @@ class ConfigModule:
|
|
179
186
|
res.append(Option(self, opt))
|
180
187
|
return res
|
181
188
|
|
182
|
-
def add_options_to_parser(self):
|
189
|
+
def add_options_to_parser(self, skip_options: tuple[str, ...]):
|
190
|
+
skip_options = tuple(o.lower() for o in skip_options)
|
183
191
|
p = self.parser
|
184
192
|
for o in sorted(self.options, key=lambda x: (not x.required, x.name)):
|
193
|
+
if o.name.lower() in skip_options:
|
194
|
+
continue
|
185
195
|
p.add_argument(*o.names, **o.kwargs)
|
186
196
|
|
187
197
|
def update_dynamic_defaults(self, args):
|
slidge/util/types.py
CHANGED
@@ -21,7 +21,7 @@ from typing import (
|
|
21
21
|
)
|
22
22
|
|
23
23
|
from slixmpp import Message, Presence
|
24
|
-
from slixmpp.types import PresenceShows, PresenceTypes
|
24
|
+
from slixmpp.types import PresenceShows, PresenceTypes, ResourceDict
|
25
25
|
|
26
26
|
if TYPE_CHECKING:
|
27
27
|
from ..contact import LegacyContact
|
@@ -155,12 +155,6 @@ class MucType(IntEnum):
|
|
155
155
|
PseudoPresenceShow = Union[PresenceShows, Literal[""]]
|
156
156
|
|
157
157
|
|
158
|
-
class ResourceDict(TypedDict):
|
159
|
-
show: PseudoPresenceShow
|
160
|
-
status: str
|
161
|
-
priority: int
|
162
|
-
|
163
|
-
|
164
158
|
MessageOrPresenceTypeVar = TypeVar(
|
165
159
|
"MessageOrPresenceTypeVar", bound=Union[Message, Presence]
|
166
160
|
)
|
slidge/util/util.py
CHANGED
@@ -218,19 +218,6 @@ class SlidgeLogger(logging.Logger):
|
|
218
218
|
log = logging.getLogger(__name__)
|
219
219
|
|
220
220
|
|
221
|
-
def get_version() -> str:
|
222
|
-
try:
|
223
|
-
git = subprocess.check_output(
|
224
|
-
["git", "rev-parse", "HEAD"],
|
225
|
-
stderr=subprocess.DEVNULL,
|
226
|
-
cwd=Path(__file__).parent,
|
227
|
-
).decode()
|
228
|
-
except (FileNotFoundError, subprocess.CalledProcessError):
|
229
|
-
return "NO_VERSION"
|
230
|
-
else:
|
231
|
-
return "git-" + git[:10]
|
232
|
-
|
233
|
-
|
234
221
|
def merge_resources(resources: dict[str, ResourceDict]) -> Optional[ResourceDict]:
|
235
222
|
if len(resources) == 0:
|
236
223
|
return None
|
@@ -0,0 +1,133 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: slidge
|
3
|
+
Version: 0.2.10
|
4
|
+
Summary: XMPP bridging framework
|
5
|
+
Author-email: Nicolas Cedilnik <nicoco@nicoco.fr>
|
6
|
+
License-Expression: AGPL-3.0-or-later
|
7
|
+
Project-URL: Homepage, https://codeberg.org/slidge/
|
8
|
+
Project-URL: Issues, https://codeberg.org/slidge/slidge/issues
|
9
|
+
Project-URL: Repository, https://codeberg.org/slidge/slidge/
|
10
|
+
Project-URL: Chat room, https://conference.nicoco.fr:5281/muc_log/slidge/
|
11
|
+
Project-URL: Documentation, https://slidge.im/docs/slidge/main
|
12
|
+
Project-URL: changelog, https://codeberg.org/slidge/slidge/releases
|
13
|
+
Keywords: xmpp,gateway,bridge,instant messaging
|
14
|
+
Classifier: Topic :: Internet :: XMPP
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
16
|
+
Requires-Python: >=3.11
|
17
|
+
Description-Content-Type: text/markdown
|
18
|
+
License-File: LICENSE
|
19
|
+
Requires-Dist: aiohttp[speedups]<4,>=3.11.11
|
20
|
+
Requires-Dist: alembic<2,>=1.14.0
|
21
|
+
Requires-Dist: configargparse<2,>=1.7
|
22
|
+
Requires-Dist: defusedxml>=0.7.1
|
23
|
+
Requires-Dist: pillow<12,>=11.0.0
|
24
|
+
Requires-Dist: python-magic<0.5,>=0.4.27
|
25
|
+
Requires-Dist: qrcode<9,>=8.0
|
26
|
+
Requires-Dist: slixmpp<2,>=1.10.0
|
27
|
+
Requires-Dist: sqlalchemy<3,>=2
|
28
|
+
Requires-Dist: thumbhash>=0.1.2
|
29
|
+
Dynamic: license-file
|
30
|
+
|
31
|
+

|
32
|
+
|
33
|
+
[](https://ci.codeberg.org/repos/14027)
|
34
|
+
[](https://slidge.im/coverage/slidge/main)
|
35
|
+
[](https://conference.nicoco.fr:5281/muc_log/slidge/)
|
36
|
+
|
37
|
+
Slidge is an XMPP (puppeteer) gateway library in python.
|
38
|
+
It makes
|
39
|
+
[writing gateways to other chat networks](https://slidge.im/docs/slidge/main/dev/tutorial.html)
|
40
|
+
(*legacy modules*) as frictionless as possible.
|
41
|
+
It supports fancy IM features, such as
|
42
|
+
[(emoji) reactions](https://xmpp.org/extensions/xep-0444.html),
|
43
|
+
[replies](https://xmpp.org/extensions/xep-0461.html), and
|
44
|
+
[retractions](https://xmpp.org/extensions/xep-0424.html).
|
45
|
+
The full list of supported XEPs in on [xmpp.org](https://xmpp.org/software/slidge/).
|
46
|
+
|
47
|
+
Status
|
48
|
+
------
|
49
|
+
|
50
|
+
Slidge is **beta**-grade software. It support groups and 1:1 chats.
|
51
|
+
Try slidge and give us some
|
52
|
+
feedback, through the [MUC](xmpp:slidge@conference.nicoco.fr?join) or the
|
53
|
+
[issue tracker](https://codeberg.org/slidge/slidge/issues).
|
54
|
+
Don't be shy!
|
55
|
+
|
56
|
+
Usage
|
57
|
+
-----
|
58
|
+
|
59
|
+
A minimal (and fictional!) slidge-powered "legacy module" looks like this:
|
60
|
+
|
61
|
+
```python
|
62
|
+
from cool_chat_lib import CoolClient
|
63
|
+
from slidge import BaseGateway, BaseSession
|
64
|
+
from slidge.contact import LegacyContact
|
65
|
+
from slidge.group import LegacyMUC
|
66
|
+
from slidge.db import GatewayUser
|
67
|
+
|
68
|
+
|
69
|
+
class Gateway(BaseGateway):
|
70
|
+
# Various aspects of the gateway component are configured as class
|
71
|
+
# attributes of the concrete Gateway class
|
72
|
+
COMPONENT_NAME = "Gateway to the super duper chat network"
|
73
|
+
|
74
|
+
|
75
|
+
class Session(BaseSession):
|
76
|
+
def __init__(self, user: GatewayUser):
|
77
|
+
super().__init__(user)
|
78
|
+
self.legacy_client = CoolClient(
|
79
|
+
login=user.legacy_module_data["username"],
|
80
|
+
password=user.legacy_module_data["password"],
|
81
|
+
)
|
82
|
+
|
83
|
+
async def on_text(self, chat: LegacyContact | LegacyMUC, text: str, **kwargs):
|
84
|
+
"""
|
85
|
+
Triggered when the slidge user sends an XMPP message through the gateway
|
86
|
+
"""
|
87
|
+
self.legacy_client.send_message(text=text, destination=chat.legacy_id)
|
88
|
+
```
|
89
|
+
|
90
|
+
There's more in [the tutorial](https://slidge.im/docs/slidge/main/dev/tutorial.html)!
|
91
|
+
|
92
|
+
Installation
|
93
|
+
------------
|
94
|
+
|
95
|
+
⚠️ Slidge is a lib for gateway developers, if you are an XMPP server admin and
|
96
|
+
want to install gateways on your server, you are looking for a
|
97
|
+
[slidge-based gateway](https://codeberg.org/explore/repos?q=slidge&topic=1).
|
98
|
+
or the
|
99
|
+
[slidge-debian](https://git.sr.ht/~nicoco/slidge-debian)
|
100
|
+
bundle.
|
101
|
+
|
102
|
+
[](https://pypi.org/project/slidge/)
|
103
|
+
|
104
|
+
[](https://repology.org/project/slidge/versions)
|
105
|
+
|
106
|
+
Slidge is available on
|
107
|
+
[codeberg](https://codeberg.org/slidge/slidge/releases)
|
108
|
+
and [pypi](https://pypi.org/project/slidge/).
|
109
|
+
Refer to [the docs](https://slidge.im/docs/slidge/main/admin/install.html) for details.
|
110
|
+
|
111
|
+
About privacy
|
112
|
+
-------------
|
113
|
+
|
114
|
+
Slidge (and most if not all XMPP gateway that I know of) will break
|
115
|
+
end-to-end encryption, or more precisely one of the 'ends' become the
|
116
|
+
gateway itself. If privacy is a major concern for you, my advice would
|
117
|
+
be to:
|
118
|
+
|
119
|
+
- use XMPP + OMEMO
|
120
|
+
- self-host your gateways
|
121
|
+
- have your gateways hosted by someone you know AFK and trust
|
122
|
+
|
123
|
+
Related projects
|
124
|
+
----------------
|
125
|
+
|
126
|
+
- [Spectrum](https://www.spectrum.im/)
|
127
|
+
- [telegabber](https://dev.narayana.im/narayana/telegabber)
|
128
|
+
- [biboumi](https://biboumi.louiz.org/)
|
129
|
+
- [Bifröst](https://github.com/matrix-org/matrix-bifrost)
|
130
|
+
- [Mautrix](https://github.com/mautrix)
|
131
|
+
- [matterbridge](https://github.com/42wim/matterbridge)
|
132
|
+
|
133
|
+
Thank you, [Trung](https://trung.fun/), for the slidge logo!
|
@@ -1,12 +1,11 @@
|
|
1
|
-
slidge/__init__.py,sha256=
|
1
|
+
slidge/__init__.py,sha256=OKdIR1pz7HNSokVRB4cEyistagdPM9TDmaARJdnk0kc,1804
|
2
2
|
slidge/__main__.py,sha256=ydjUklOoavS4YlGfjRX_8BQN2DaSbaXPMi47RkOgcFI,37
|
3
|
-
slidge/
|
4
|
-
slidge/main.py,sha256=vMJzhvUxbeuIXuHxXXs6lm_ShBjXiS9B5Li5Ge4vWPo,6238
|
3
|
+
slidge/main.py,sha256=NpDKks_6Ib7thhTQu8gI_g3XifX1RNV19p6-Rtz2phY,6533
|
5
4
|
slidge/migration.py,sha256=4BJmPIRB56_WIhRTqBFIIBXuvnhhBjjOMl4CE7jY6oc,1541
|
6
5
|
slidge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
6
|
slidge/command/__init__.py,sha256=UYf1mjCYbZ5G7PIgaFTWSQRAzEJkQ6dTH8Fu_e_XnO0,613
|
8
7
|
slidge/command/adhoc.py,sha256=bYLXcYJjbqPE9nBhrEQevqYFh9AS7Ei3m1vTZBIggrc,10557
|
9
|
-
slidge/command/admin.py,sha256=
|
8
|
+
slidge/command/admin.py,sha256=DYN9yADZo2hKV0t20K2qnFPJHU98eKsaZTOqi1zdMys,6165
|
10
9
|
slidge/command/base.py,sha256=EDcEl5dJcooSmLarXI2fmBq6QtU7h-7MOM3DDsxXmTU,13447
|
11
10
|
slidge/command/categories.py,sha256=vF0KGDV9sEn8TNkcMoDRw-u3gEyNHSXghOU2JRHQtKs,351
|
12
11
|
slidge/command/chat_command.py,sha256=z-4qp03rK7kCh3_kEozDViwkDg_hVjHvRCiYYJxedBQ,11153
|
@@ -17,9 +16,9 @@ slidge/contact/contact.py,sha256=CLWuNLvWuODi1mlT6MFGx9s3y8yGPM8ZEsH-Ih6QEn8,229
|
|
17
16
|
slidge/contact/roster.py,sha256=19EYyX-usuevMob9AgnQmSt6APdE5MxMroHOYmVYt-w,10205
|
18
17
|
slidge/core/__init__.py,sha256=RG7Jj5JCJERjhqJ31lOLYV-7bH_oblClQD1KF9LsTXo,68
|
19
18
|
slidge/core/config.py,sha256=OjJfpXJaDhMxRB-vYA0cqkSf0fwMt-HMThM8GS1htCg,7964
|
20
|
-
slidge/core/gateway.py,sha256=
|
21
|
-
slidge/core/pubsub.py,sha256=
|
22
|
-
slidge/core/session.py,sha256=
|
19
|
+
slidge/core/gateway.py,sha256=Ghe2-bZ2XSeWC34uXO0D31yn04WbcwKH5pnDpPL4cVA,37606
|
20
|
+
slidge/core/pubsub.py,sha256=IAu-MV_ncrUk3SiVVWyzmKtv9I9yXBNAOE78GaowyYE,12156
|
21
|
+
slidge/core/session.py,sha256=Ie1a6bpUCC_HFvm98S-t03WG5JlOFG0SwbmGB2l9ZTc,28439
|
23
22
|
slidge/core/dispatcher/__init__.py,sha256=1EXcjXietUKlxEqdrCWCV3xZ3q_DSsjHoqWrPMbtYao,84
|
24
23
|
slidge/core/dispatcher/caps.py,sha256=vzCAXo_bhALuLEpJWtyJTzVfWx96g1AsWD8_wkoDl0Y,2028
|
25
24
|
slidge/core/dispatcher/disco.py,sha256=j56VY9NIFzwPEWFKQQZ7YIqS9GdD-ZaF_K8a2L-JvRk,2006
|
@@ -40,7 +39,7 @@ slidge/core/dispatcher/muc/misc.py,sha256=bHBjMC-Pu3jR5hAPGMzXf-C05UbACIwg38YbJU
|
|
40
39
|
slidge/core/dispatcher/muc/owner.py,sha256=1a6YV7b_mmi1jC6q1ko8weeL8imQA-s-hYGPLIHd10I,3308
|
41
40
|
slidge/core/dispatcher/muc/ping.py,sha256=lb1VQPhiUPZ19KhbofRXMVCcY6wwQ2w-asnqtANaAwA,1660
|
42
41
|
slidge/core/mixins/__init__.py,sha256=muReAzgvENgMvlfm0Fpe6BQFfm2EMjoDe9ZhGgo6Vig,627
|
43
|
-
slidge/core/mixins/attachment.py,sha256=
|
42
|
+
slidge/core/mixins/attachment.py,sha256=diEmZAkc_fv9UEHzDXVHtRbE9-vhK8yN9ZtI9gL0JAY,20212
|
44
43
|
slidge/core/mixins/avatar.py,sha256=BVOeH5j5tsPJ0IHUcB5ozpyh02TBPKiZMd9MYizDokA,8136
|
45
44
|
slidge/core/mixins/base.py,sha256=MOd-pas38_52VawQVlxWtBtmTKC6My9G0ZaCeQxOJbs,748
|
46
45
|
slidge/core/mixins/db.py,sha256=5Qpegd7D8e5TLXLLINYcf_DuVdN-7wNmsfztUuFYPcU,442
|
@@ -55,7 +54,7 @@ slidge/db/__init__.py,sha256=EBDH1JSEhgqYcli2Bw11CRC749wJk8AOucgBzmhDSvU,105
|
|
55
54
|
slidge/db/avatar.py,sha256=z5e72STv8PdN6zkNyKlLqF7NFxHwCa6IjwgFpzu5ghE,8033
|
56
55
|
slidge/db/meta.py,sha256=v1Jf-npZ28QwdGpsLQWLBHEbEP3-jnPrygRg05tJ_Iw,1831
|
57
56
|
slidge/db/models.py,sha256=MSVNW04x05qfxahvjCYRDFjfFP-XXp-lOHnK5IqFCXw,14046
|
58
|
-
slidge/db/store.py,sha256=
|
57
|
+
slidge/db/store.py,sha256=P9gprCsRsbJjdzvyG2UKV5IHL6R0tWu-Q7t6mivqJVI,47206
|
59
58
|
slidge/db/alembic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
59
|
slidge/db/alembic/env.py,sha256=hsBlRNs0zF5diSHGRSa8Fi3qRVQDA2rJdR41AEIdvxc,1642
|
61
60
|
slidge/db/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
@@ -80,9 +79,9 @@ slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py,sha2
|
|
80
79
|
slidge/group/__init__.py,sha256=yFt7cHqeaKIMN6f9ZyhhspOcJJvBtLedGv-iICG7lto,258
|
81
80
|
slidge/group/archive.py,sha256=AUzVtXlHiCreyY3jp1XMt0G7LDNm-qOU-4CEPQ89ics,5445
|
82
81
|
slidge/group/bookmarks.py,sha256=uGw_XtF0nloZ7rhNLdKM0nNZZb5C6SBfTsLyZryxjxY,6592
|
83
|
-
slidge/group/participant.py,sha256=
|
84
|
-
slidge/group/room.py,sha256=
|
85
|
-
slidge/slixfix/__init__.py,sha256=
|
82
|
+
slidge/group/participant.py,sha256=YExXhm7FWK_G-_H6VJKFhJhA07l1KB7pZvD1xAYibj8,17730
|
83
|
+
slidge/group/room.py,sha256=c1SVpdrI7-xTr0UsF2n539R3iV8C4P5KZw6HhK5cPuU,49176
|
84
|
+
slidge/slixfix/__init__.py,sha256=su7mDAHgP3uU7bSxjK8dwW79k7KBLevJHdMQV8_33WA,4130
|
86
85
|
slidge/slixfix/delivery_receipt.py,sha256=3bWdZH3-X3CZJXmnI_TpjkTUUK-EY4Ktm78lW0-40fc,1366
|
87
86
|
slidge/slixfix/roster.py,sha256=KvDjh9q7pqaZf69H93okfib13cc95uVZUJ6rzpqmDaU,1704
|
88
87
|
slidge/slixfix/link_preview/__init__.py,sha256=TDPTSEH5FQxgGpQpQIde-D72AHg-6YVWG-tOj4KpKmU,290
|
@@ -100,13 +99,14 @@ slidge/slixfix/xep_0292/__init__.py,sha256=_MvS9wGra6ig3P_dPAVlCPDJkiOFvUWGjaRsH
|
|
100
99
|
slidge/slixfix/xep_0292/vcard4.py,sha256=jL-TOW3eG2QXLduSLNq03L8HoUNmvy8kTZI5ojvo6GE,358
|
101
100
|
slidge/util/__init__.py,sha256=BELovoTMPcPPGz3D48esBr8A4BRRHXTvavfgnArBgEc,301
|
102
101
|
slidge/util/archive_msg.py,sha256=xXAR0BI5r3d6KKWjae9594izCOv6iI03z2WLuTecNw8,1724
|
103
|
-
slidge/util/conf.py,sha256=
|
102
|
+
slidge/util/conf.py,sha256=yT_9Wino0xfhTvOjmmokt2PIQBTwBKVuVPWp7_7aoKU,6967
|
104
103
|
slidge/util/jid_escaping.py,sha256=pWJTrB6-M923a_rEE-nxPmiDTOx9UCMa8UgE7JbLC0c,1066
|
105
104
|
slidge/util/test.py,sha256=l1VHBsw5Uzk2t7wtkfb9kWvtehcYhw1t_d567JAJFKA,14135
|
106
|
-
slidge/util/types.py,sha256=
|
107
|
-
slidge/util/util.py,sha256=
|
108
|
-
slidge-0.2.
|
109
|
-
slidge-0.2.
|
110
|
-
slidge-0.2.
|
111
|
-
slidge-0.2.
|
112
|
-
slidge-0.2.
|
105
|
+
slidge/util/types.py,sha256=5j0G22hhsh3PNhtpJaz2DVt5XINJznZjz0hqewPZoFo,5407
|
106
|
+
slidge/util/util.py,sha256=ZkQltO6JmfqZc9T-86XI6gAsZx9BLdF0CCurnxPRNRc,9329
|
107
|
+
slidge-0.2.10.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
108
|
+
slidge-0.2.10.dist-info/METADATA,sha256=OFlStm_YVTPwHPhpu8z60NtKdi00uMU2Tp8crY5-6F8,5055
|
109
|
+
slidge-0.2.10.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
110
|
+
slidge-0.2.10.dist-info/entry_points.txt,sha256=py3_x834fFJ2TEzPd18Wt2DnysdAfuVqJ5zzBrXbAZs,44
|
111
|
+
slidge-0.2.10.dist-info/top_level.txt,sha256=2LRjDYHaGZ5ieCMF8xy58JIiabRMzX-MGMbCZwfE17c,7
|
112
|
+
slidge-0.2.10.dist-info/RECORD,,
|