slidge 0.1.0b2__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.
Files changed (155) hide show
  1. slidge/__init__.py +55 -31
  2. slidge/__main__.py +118 -116
  3. slidge/command/__init__.py +28 -0
  4. slidge/command/adhoc.py +258 -0
  5. slidge/command/admin.py +193 -0
  6. slidge/command/base.py +441 -0
  7. slidge/command/categories.py +3 -0
  8. slidge/command/chat_command.py +288 -0
  9. slidge/command/register.py +179 -0
  10. slidge/command/user.py +250 -0
  11. slidge/contact/__init__.py +8 -0
  12. slidge/contact/contact.py +452 -0
  13. slidge/contact/roster.py +192 -0
  14. slidge/core/__init__.py +2 -0
  15. slidge/core/cache.py +183 -0
  16. slidge/core/config.py +216 -0
  17. slidge/core/gateway/__init__.py +3 -0
  18. slidge/core/gateway/base.py +895 -0
  19. slidge/core/gateway/caps.py +63 -0
  20. slidge/core/gateway/delivery_receipt.py +52 -0
  21. slidge/core/gateway/disco.py +80 -0
  22. slidge/core/gateway/mam.py +75 -0
  23. slidge/core/gateway/muc_admin.py +35 -0
  24. slidge/core/gateway/ping.py +66 -0
  25. slidge/core/gateway/presence.py +95 -0
  26. slidge/core/gateway/registration.py +53 -0
  27. slidge/core/gateway/search.py +102 -0
  28. slidge/core/gateway/session_dispatcher.py +789 -0
  29. slidge/core/gateway/vcard_temp.py +130 -0
  30. slidge/core/mixins/__init__.py +19 -0
  31. slidge/core/mixins/attachment.py +506 -0
  32. slidge/core/mixins/avatar.py +167 -0
  33. slidge/core/mixins/base.py +31 -0
  34. slidge/core/mixins/disco.py +130 -0
  35. slidge/core/mixins/lock.py +31 -0
  36. slidge/core/mixins/message.py +398 -0
  37. slidge/core/mixins/message_maker.py +154 -0
  38. slidge/core/mixins/presence.py +217 -0
  39. slidge/core/mixins/recipient.py +43 -0
  40. slidge/core/pubsub.py +282 -116
  41. slidge/core/session.py +595 -372
  42. slidge/group/__init__.py +10 -0
  43. slidge/group/archive.py +125 -0
  44. slidge/group/bookmarks.py +163 -0
  45. slidge/group/participant.py +458 -0
  46. slidge/group/room.py +1103 -0
  47. slidge/migration.py +18 -0
  48. slidge/slixfix/__init__.py +68 -0
  49. slidge/{util/xep_0084 → slixfix/link_preview}/__init__.py +3 -5
  50. slidge/slixfix/link_preview/link_preview.py +17 -0
  51. slidge/slixfix/link_preview/stanza.py +99 -0
  52. slidge/slixfix/roster.py +60 -0
  53. slidge/{util → slixfix}/xep_0077/register.py +14 -2
  54. slidge/slixfix/xep_0077/stanza.py +104 -0
  55. slidge/{util → slixfix}/xep_0100/gateway.py +25 -15
  56. slidge/slixfix/xep_0100/stanza.py +9 -0
  57. slidge/slixfix/xep_0153/__init__.py +10 -0
  58. slidge/slixfix/xep_0153/stanza.py +25 -0
  59. slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
  60. slidge/slixfix/xep_0264/__init__.py +5 -0
  61. slidge/slixfix/xep_0264/stanza.py +36 -0
  62. slidge/slixfix/xep_0264/thumbnail.py +23 -0
  63. slidge/slixfix/xep_0292/__init__.py +5 -0
  64. slidge/slixfix/xep_0292/vcard4.py +100 -0
  65. slidge/slixfix/xep_0313/__init__.py +12 -0
  66. slidge/slixfix/xep_0313/mam.py +262 -0
  67. slidge/slixfix/xep_0313/stanza.py +359 -0
  68. slidge/slixfix/xep_0317/__init__.py +5 -0
  69. slidge/slixfix/xep_0317/hats.py +17 -0
  70. slidge/slixfix/xep_0317/stanza.py +28 -0
  71. slidge/{util → slixfix}/xep_0356_old/privilege.py +9 -7
  72. slidge/slixfix/xep_0424/__init__.py +9 -0
  73. slidge/slixfix/xep_0424/retraction.py +77 -0
  74. slidge/slixfix/xep_0424/stanza.py +28 -0
  75. slidge/slixfix/xep_0490/__init__.py +8 -0
  76. slidge/slixfix/xep_0490/mds.py +47 -0
  77. slidge/slixfix/xep_0490/stanza.py +17 -0
  78. slidge/util/__init__.py +4 -6
  79. slidge/util/archive_msg.py +61 -0
  80. slidge/util/conf.py +206 -0
  81. slidge/util/db.py +57 -76
  82. slidge/util/schema.sql +126 -0
  83. slidge/util/sql.py +508 -0
  84. slidge/util/test.py +215 -25
  85. slidge/util/types.py +177 -4
  86. slidge/util/util.py +225 -59
  87. slidge-0.1.1.dist-info/METADATA +110 -0
  88. slidge-0.1.1.dist-info/RECORD +96 -0
  89. {slidge-0.1.0b2.dist-info → slidge-0.1.1.dist-info}/WHEEL +1 -1
  90. slidge/core/contact.py +0 -891
  91. slidge/core/gateway.py +0 -916
  92. slidge/plugins/discord/__init__.py +0 -90
  93. slidge/plugins/discord/client.py +0 -108
  94. slidge/plugins/discord/session.py +0 -162
  95. slidge/plugins/dummy.py +0 -203
  96. slidge/plugins/facebook.py +0 -493
  97. slidge/plugins/hackernews.py +0 -213
  98. slidge/plugins/mattermost/__init__.py +0 -1
  99. slidge/plugins/mattermost/api.py +0 -280
  100. slidge/plugins/mattermost/gateway.py +0 -365
  101. slidge/plugins/mattermost/websocket.py +0 -252
  102. slidge/plugins/signal/__init__.py +0 -3
  103. slidge/plugins/signal/contact.py +0 -106
  104. slidge/plugins/signal/gateway.py +0 -282
  105. slidge/plugins/signal/session.py +0 -448
  106. slidge/plugins/signal/txt.py +0 -53
  107. slidge/plugins/skype.py +0 -325
  108. slidge/plugins/steam.py +0 -310
  109. slidge/plugins/telegram/__init__.py +0 -5
  110. slidge/plugins/telegram/client.py +0 -228
  111. slidge/plugins/telegram/config.py +0 -12
  112. slidge/plugins/telegram/contact.py +0 -176
  113. slidge/plugins/telegram/gateway.py +0 -150
  114. slidge/plugins/telegram/session.py +0 -256
  115. slidge/util/xep_0030/__init__.py +0 -13
  116. slidge/util/xep_0030/disco.py +0 -811
  117. slidge/util/xep_0030/stanza/__init__.py +0 -7
  118. slidge/util/xep_0030/stanza/info.py +0 -270
  119. slidge/util/xep_0030/stanza/items.py +0 -147
  120. slidge/util/xep_0030/static.py +0 -467
  121. slidge/util/xep_0055/__init__.py +0 -5
  122. slidge/util/xep_0055/search.py +0 -75
  123. slidge/util/xep_0055/stanza.py +0 -10
  124. slidge/util/xep_0077/stanza.py +0 -71
  125. slidge/util/xep_0084/avatar.py +0 -137
  126. slidge/util/xep_0084/stanza.py +0 -104
  127. slidge/util/xep_0115/__init__.py +0 -12
  128. slidge/util/xep_0115/caps.py +0 -379
  129. slidge/util/xep_0115/stanza.py +0 -16
  130. slidge/util/xep_0115/static.py +0 -137
  131. slidge/util/xep_0292/__init__.py +0 -1
  132. slidge/util/xep_0292/stanza.py +0 -167
  133. slidge/util/xep_0292/vcard4.py +0 -75
  134. slidge/util/xep_0333/__init__.py +0 -10
  135. slidge/util/xep_0333/markers.py +0 -96
  136. slidge/util/xep_0333/stanza.py +0 -34
  137. slidge/util/xep_0356/__init__.py +0 -7
  138. slidge/util/xep_0356/permissions.py +0 -35
  139. slidge/util/xep_0356/privilege.py +0 -160
  140. slidge/util/xep_0356/stanza.py +0 -44
  141. slidge/util/xep_0363/__init__.py +0 -16
  142. slidge/util/xep_0363/http_upload.py +0 -215
  143. slidge/util/xep_0363/stanza.py +0 -46
  144. slidge/util/xep_0461/__init__.py +0 -6
  145. slidge/util/xep_0461/reply.py +0 -48
  146. slidge/util/xep_0461/stanza.py +0 -47
  147. slidge-0.1.0b2.dist-info/METADATA +0 -171
  148. slidge-0.1.0b2.dist-info/RECORD +0 -81
  149. /slidge/{plugins/__init__.py → py.typed} +0 -0
  150. /slidge/{util → slixfix}/xep_0077/__init__.py +0 -0
  151. /slidge/{util → slixfix}/xep_0100/__init__.py +0 -0
  152. /slidge/{util → slixfix}/xep_0356_old/__init__.py +0 -0
  153. /slidge/{util → slixfix}/xep_0356_old/stanza.py +0 -0
  154. {slidge-0.1.0b2.dist-info → slidge-0.1.1.dist-info}/LICENSE +0 -0
  155. {slidge-0.1.0b2.dist-info → slidge-0.1.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,61 @@
1
+ from copy import copy
2
+ from datetime import datetime, timezone
3
+ from typing import Optional, Union
4
+ from xml.etree import ElementTree as ET
5
+
6
+ from slixmpp import Message
7
+ from slixmpp.plugins.xep_0297 import Forwarded
8
+
9
+
10
+ def fix_namespaces(xml, old="{jabber:component:accept}", new="{jabber:client}"):
11
+ """
12
+ Hack to fix namespaces between jabber:component and jabber:client
13
+
14
+ Acts in-place.
15
+
16
+ :param xml:
17
+ :param old:
18
+ :param new:
19
+ """
20
+ xml.tag = xml.tag.replace(old, new)
21
+ for child in xml:
22
+ fix_namespaces(child, old, new)
23
+
24
+
25
+ class HistoryMessage:
26
+ def __init__(self, stanza: Union[Message, str], when: Optional[datetime] = None):
27
+ if isinstance(stanza, str):
28
+ from_db = True
29
+ stanza = Message(xml=ET.fromstring(stanza))
30
+ else:
31
+ from_db = False
32
+
33
+ self.id = stanza["stanza_id"]["id"]
34
+ self.when: datetime = (
35
+ when or stanza["delay"]["stamp"] or datetime.now(tz=timezone.utc)
36
+ )
37
+
38
+ if not from_db:
39
+ del stanza["delay"]
40
+ del stanza["markable"]
41
+ del stanza["hint"]
42
+ del stanza["chat_state"]
43
+ if not stanza["body"]:
44
+ del stanza["body"]
45
+ fix_namespaces(stanza.xml)
46
+
47
+ self.stanza: Message = stanza
48
+
49
+ @property
50
+ def stanza_component_ns(self):
51
+ stanza = copy(self.stanza)
52
+ fix_namespaces(
53
+ stanza.xml, old="{jabber:client}", new="{jabber:component:accept}"
54
+ )
55
+ return stanza
56
+
57
+ def forwarded(self):
58
+ forwarded = Forwarded()
59
+ forwarded["delay"]["stamp"] = self.when
60
+ forwarded.append(self.stanza)
61
+ return forwarded
slidge/util/conf.py ADDED
@@ -0,0 +1,206 @@
1
+ import logging
2
+ from functools import cached_property
3
+ from types import GenericAlias
4
+ from typing import Optional, Union, get_args, get_origin, get_type_hints
5
+
6
+ import configargparse
7
+
8
+
9
+ class Option:
10
+ DOC_SUFFIX = "__DOC"
11
+ DYNAMIC_DEFAULT_SUFFIX = "__DYNAMIC_DEFAULT"
12
+ SHORT_SUFFIX = "__SHORT"
13
+
14
+ def __init__(self, parent: "ConfigModule", name: str):
15
+ self.parent = parent
16
+ self.config_obj = parent.config_obj
17
+ self.name = name
18
+
19
+ @cached_property
20
+ def doc(self):
21
+ return getattr(self.config_obj, self.name + self.DOC_SUFFIX)
22
+
23
+ @cached_property
24
+ def required(self):
25
+ return not hasattr(
26
+ self.config_obj, self.name + self.DYNAMIC_DEFAULT_SUFFIX
27
+ ) and not hasattr(self.config_obj, self.name)
28
+
29
+ @cached_property
30
+ def default(self):
31
+ return getattr(self.config_obj, self.name, None)
32
+
33
+ @cached_property
34
+ def short(self):
35
+ return getattr(self.config_obj, self.name + self.SHORT_SUFFIX, None)
36
+
37
+ @cached_property
38
+ def nargs(self):
39
+ type_ = get_type_hints(self.config_obj).get(self.name, type(self.default))
40
+
41
+ if isinstance(type_, GenericAlias):
42
+ args = get_args(type_)
43
+ if args[1] is Ellipsis:
44
+ return "*"
45
+ else:
46
+ return len(args)
47
+
48
+ @cached_property
49
+ def type(self):
50
+ type_ = get_type_hints(self.config_obj).get(self.name, type(self.default))
51
+
52
+ if _is_optional(type_):
53
+ type_ = get_args(type_)[0]
54
+ elif isinstance(type_, GenericAlias):
55
+ args = get_args(type_)
56
+ type_ = args[0]
57
+
58
+ return type_
59
+
60
+ @cached_property
61
+ def names(self):
62
+ res = ["--" + self.name.lower().replace("_", "-")]
63
+ if s := self.short:
64
+ res.append("-" + s)
65
+ return res
66
+
67
+ @cached_property
68
+ def kwargs(self):
69
+ kwargs = dict(
70
+ required=self.required,
71
+ help=self.doc,
72
+ env_var=self.name_to_env_var(),
73
+ )
74
+ t = self.type
75
+ if t is bool:
76
+ if self.default:
77
+ kwargs["action"] = "store_false"
78
+ else:
79
+ kwargs["action"] = "store_true"
80
+ else:
81
+ kwargs["type"] = t
82
+ if self.required:
83
+ kwargs["required"] = True
84
+ else:
85
+ kwargs["default"] = self.default
86
+ if n := self.nargs:
87
+ kwargs["nargs"] = n
88
+ return kwargs
89
+
90
+ def name_to_env_var(self):
91
+ return self.parent.ENV_VAR_PREFIX + self.name
92
+
93
+
94
+ class ConfigModule:
95
+ ENV_VAR_PREFIX = "SLIDGE_"
96
+
97
+ def __init__(
98
+ self, config_obj, parser: Optional[configargparse.ArgumentParser] = None
99
+ ):
100
+ self.config_obj = config_obj
101
+ if parser is None:
102
+ parser = configargparse.ArgumentParser()
103
+ self.parser = parser
104
+
105
+ self.add_options_to_parser()
106
+
107
+ def _list_options(self):
108
+ return {
109
+ o
110
+ for o in (set(dir(self.config_obj)) | set(get_type_hints(self.config_obj)))
111
+ if o.upper() == o and not o.startswith("_") and "__" not in o
112
+ }
113
+
114
+ def set_conf(self, argv: Optional[list[str]] = None):
115
+ if argv is not None:
116
+ # this is ugly, but necessary because for plugin config, we used
117
+ # remaining argv.
118
+ # when using (a) .ini file(s), for bool options, we end-up with
119
+ # remaining pseudo-argv such as --some-bool-opt=true when we really
120
+ # should have just --some-bool-opt
121
+ # TODO: get rid of configargparse and make this cleaner
122
+ options_long = {o.name: o for o in self.options}
123
+ no_explicit_bool = []
124
+ skip_next = False
125
+ for a, aa in zip(argv, argv[1:] + [""]):
126
+ if skip_next:
127
+ skip_next = False
128
+ continue
129
+ force_keep = False
130
+ if "=" in a:
131
+ real_name, _value = a.split("=")
132
+ opt: Optional[Option] = options_long.get(
133
+ _argv_to_option_name(real_name)
134
+ )
135
+ if opt and opt.type is bool:
136
+ if opt.default:
137
+ if _value in _TRUEISH or not _value:
138
+ continue
139
+ else:
140
+ a = real_name
141
+ force_keep = True
142
+ else:
143
+ if _value in _TRUEISH:
144
+ a = real_name
145
+ force_keep = True
146
+ else:
147
+ continue
148
+ else:
149
+ upper = _argv_to_option_name(a)
150
+ opt = options_long.get(upper)
151
+ if opt and opt.type is bool:
152
+ if _argv_to_option_name(aa) not in options_long:
153
+ log.debug("Removing %s from argv", aa)
154
+ skip_next = True
155
+
156
+ if opt:
157
+ if opt.type is bool:
158
+ if force_keep or not opt.default:
159
+ no_explicit_bool.append(a)
160
+ else:
161
+ no_explicit_bool.append(a)
162
+ else:
163
+ no_explicit_bool.append(a)
164
+ log.debug("Removed boolean values from %s to %s", argv, no_explicit_bool)
165
+ argv = no_explicit_bool
166
+
167
+ args, rest = self.parser.parse_known_args(argv)
168
+ self.update_dynamic_defaults(args)
169
+ for name in self._list_options():
170
+ value = getattr(args, name.lower())
171
+ log.debug("Setting '%s' to %r", name, value)
172
+ setattr(self.config_obj, name, value)
173
+ return args, rest
174
+
175
+ @cached_property
176
+ def options(self) -> list[Option]:
177
+ res = []
178
+ for opt in self._list_options():
179
+ res.append(Option(self, opt))
180
+ return res
181
+
182
+ def add_options_to_parser(self):
183
+ p = self.parser
184
+ for o in sorted(self.options, key=lambda x: (not x.required, x.name)):
185
+ p.add_argument(*o.names, **o.kwargs)
186
+
187
+ def update_dynamic_defaults(self, args):
188
+ pass
189
+
190
+
191
+ def _is_optional(t):
192
+ if get_origin(t) is Union:
193
+ args = get_args(t)
194
+ if len(args) == 2 and isinstance(None, args[1]):
195
+ return True
196
+ return False
197
+
198
+
199
+ def _argv_to_option_name(arg: str):
200
+ return arg.upper().removeprefix("--").replace("-", "_")
201
+
202
+
203
+ _TRUEISH = {"true", "True", "1", "on", "enabled"}
204
+
205
+
206
+ log = logging.getLogger(__name__)
slidge/util/db.py CHANGED
@@ -4,6 +4,7 @@ pseudo-roster for the gateway component.
4
4
  """
5
5
 
6
6
  import dataclasses
7
+ import datetime
7
8
  import logging
8
9
  import os.path
9
10
  import shelve
@@ -14,9 +15,17 @@ from typing import Iterable, Optional, Union
14
15
  from pickle_secure import Pickler, Unpickler
15
16
  from slixmpp import JID, Iq, Message, Presence
16
17
 
18
+ from .sql import db
19
+
17
20
 
18
21
  # noinspection PyUnresolvedReferences
19
22
  class EncryptedShelf(shelve.DbfilenameShelf):
23
+ cache: dict
24
+ dict: dict
25
+ writeback: bool
26
+ keyencoding: str
27
+ _protocol: int
28
+
20
29
  def __init__(
21
30
  self, filename: PathLike, key: str, flag="c", protocol=None, writeback=False
22
31
  ):
@@ -28,7 +37,7 @@ class EncryptedShelf(shelve.DbfilenameShelf):
28
37
  value = self.cache[key]
29
38
  except KeyError:
30
39
  f = BytesIO(self.dict[key.encode(self.keyencoding)])
31
- value = Unpickler(f, key=self.secret_key).load()
40
+ value = Unpickler(f, key=self.secret_key).load() # type:ignore
32
41
  if self.writeback:
33
42
  self.cache[key] = value
34
43
  return value
@@ -37,7 +46,7 @@ class EncryptedShelf(shelve.DbfilenameShelf):
37
46
  if self.writeback:
38
47
  self.cache[key] = value
39
48
  f = BytesIO()
40
- p = Pickler(f, self._protocol, key=self.secret_key)
49
+ p = Pickler(f, self._protocol, key=self.secret_key) # type:ignore
41
50
  p.dump(value)
42
51
  self.dict[key.encode(self.keyencoding)] = f.getvalue()
43
52
 
@@ -45,13 +54,15 @@ class EncryptedShelf(shelve.DbfilenameShelf):
45
54
  @dataclasses.dataclass
46
55
  class GatewayUser:
47
56
  """
48
- A dataclass representing a gateway user
57
+ A gateway user
49
58
  """
50
59
 
51
60
  bare_jid: str
52
61
  """Bare JID of the user"""
53
62
  registration_form: dict[str, Optional[str]]
54
63
  """Content of the registration form, as a dict"""
64
+ plugin_data: Optional[dict] = None
65
+ registration_date: Optional[datetime.datetime] = None
55
66
 
56
67
  def __hash__(self):
57
68
  return hash(self.bare_jid)
@@ -59,6 +70,10 @@ class GatewayUser:
59
70
  def __repr__(self):
60
71
  return f"<User {self.bare_jid}>"
61
72
 
73
+ def __post_init__(self):
74
+ if self.registration_date is None:
75
+ self.registration_date = datetime.datetime.now()
76
+
62
77
  @property
63
78
  def jid(self) -> JID:
64
79
  """
@@ -69,16 +84,20 @@ class GatewayUser:
69
84
  return JID(self.bare_jid)
70
85
 
71
86
  def get(self, field: str, default: str = "") -> Optional[str]:
72
- """
73
- Get fields from the registration form (required to comply with slixmpp backend protocol)
74
-
75
- :param field: Name of the field
76
- :param default: Default value to return if the field is not present
77
-
78
- :return: Value of the field
79
- """
87
+ # """
88
+ # Get fields from the registration form (required to comply with slixmpp backend protocol)
89
+ #
90
+ # :param field: Name of the field
91
+ # :param default: Default value to return if the field is not present
92
+ #
93
+ # :return: Value of the field
94
+ # """
80
95
  return self.registration_form.get(field, default)
81
96
 
97
+ def commit(self):
98
+ db.user_store(self)
99
+ user_store.commit(self)
100
+
82
101
 
83
102
  class UserStore:
84
103
  """
@@ -107,6 +126,8 @@ class UserStore:
107
126
  self._users = EncryptedShelf(filename, key=secret_key)
108
127
  else:
109
128
  self._users = shelve.open(str(filename))
129
+ for user in self._users.values():
130
+ db.user_store(user)
110
131
  log.info("Registered users in the DB: %s", list(self._users.keys()))
111
132
 
112
133
  def get_all(self) -> Iterable[GatewayUser]:
@@ -128,18 +149,24 @@ class UserStore:
128
149
  :param registration_form: Content of the registration form (:xep:`0077`)
129
150
  """
130
151
  log.debug("Adding user %s", jid)
131
- self._users[jid.bare] = GatewayUser(
152
+ self._users[jid.bare] = user = GatewayUser(
132
153
  bare_jid=jid.bare,
133
154
  registration_form=registration_form,
155
+ registration_date=datetime.datetime.now(),
134
156
  )
135
157
  self._users.sync()
158
+ user.commit()
136
159
  log.debug("Store: %s", self._users)
137
160
 
161
+ def commit(self, user: GatewayUser):
162
+ self._users[user.bare_jid] = user
163
+ self._users.sync()
164
+
138
165
  def get(self, _gateway_jid, _node, ifrom: JID, iq) -> Optional[GatewayUser]:
139
166
  """
140
167
  Get a user from the store
141
168
 
142
- NB: there is no reason to call this, it is used by SliXMPP plugins
169
+ NB: there is no reason to call this, it is used by SliXMPP internal API
143
170
 
144
171
  :param _gateway_jid:
145
172
  :param _node:
@@ -156,15 +183,18 @@ class UserStore:
156
183
  """
157
184
  Remove a user from the store
158
185
 
159
- NB: there is no reason to call this, it is used by SliXMPP plugins
186
+ NB: there is no reason to call this, it is used by SliXMPP internal API
187
+ """
188
+ self.remove_by_jid(ifrom)
160
189
 
161
- :param _gateway_jid:
162
- :param _node:
163
- :param ifrom:
164
- :param _iq:
190
+ def remove_by_jid(self, jid: JID):
191
+ """
192
+ Remove a user from the store, by JID
165
193
  """
166
- log.debug("Removing user %s", ifrom.bare)
167
- del self._users[ifrom.bare]
194
+ j = jid.bare
195
+ log.debug("Removing user %s", j)
196
+ db.user_del(self._users[j])
197
+ del self._users[j]
168
198
  self._users.sync()
169
199
 
170
200
  def get_by_jid(self, jid: JID) -> Optional[GatewayUser]:
@@ -185,64 +215,15 @@ class UserStore:
185
215
  """
186
216
  return self.get_by_jid(s.get_from())
187
217
 
188
-
189
- class YesSet(set):
190
- """
191
- A pseudo-set which always test True for membership
192
- """
193
-
194
- def __contains__(self, item):
195
- log.debug("Test in")
196
- return True
197
-
198
-
199
- class RosterBackend:
200
- """
201
- A pseudo-roster for the gateway component.
202
-
203
- If a user is in the user store, this will behave as if the user is part of the
204
- roster with subscription "both", and "none" otherwise.
205
-
206
- This is rudimentary but the only sane way I could come up with so far.
207
- """
208
-
209
- @staticmethod
210
- def entries(_owner_jid, _default=None):
211
- return YesSet()
212
-
213
- @staticmethod
214
- def save(_owner_jid, _jid, _item_state, _db_state):
215
- pass
216
-
217
- @staticmethod
218
- def load(_owner_jid, jid, _db_state):
219
- log.debug("Load %s", jid)
220
- user = user_store.get_by_jid(JID(jid))
221
- log.debug("User %s", user)
222
- if user is None:
223
- return {
224
- "name": "",
225
- "groups": [],
226
- "from": False,
227
- "to": False,
228
- "pending_in": False,
229
- "pending_out": False,
230
- "whitelisted": False,
231
- "subscription": "both",
232
- }
233
- else:
234
- return {
235
- "name": "",
236
- "groups": [],
237
- "from": True,
238
- "to": True,
239
- "pending_in": False,
240
- "pending_out": False,
241
- "whitelisted": False,
242
- "subscription": "none",
243
- }
218
+ def close(self):
219
+ self._users.sync()
220
+ self._users.close()
244
221
 
245
222
 
246
223
  user_store = UserStore()
224
+ """
225
+ A persistent store for slidge users. Not public, but I didn't find how to hide
226
+ it from the docs!
227
+ """
247
228
 
248
229
  log = logging.getLogger(__name__)
slidge/util/schema.sql ADDED
@@ -0,0 +1,126 @@
1
+ CREATE TABLE user(
2
+ id INTEGER PRIMARY KEY,
3
+ jid TEXT UNIQUE
4
+ );
5
+
6
+ CREATE TABLE muc(
7
+ id INTEGER PRIMARY KEY,
8
+ jid TEXT,
9
+ user_id INTEGER,
10
+ FOREIGN KEY(user_id) REFERENCES user(id),
11
+ UNIQUE(user_id, jid)
12
+ );
13
+
14
+ CREATE TABLE mam_message(
15
+ id INTEGER PRIMARY KEY,
16
+ message_id TEXT,
17
+ sent_on INTEGER,
18
+ sender_jid TEXT,
19
+ xml TEXT,
20
+ muc_id INTEGER,
21
+ user_id INTEGER,
22
+ FOREIGN KEY(muc_id) REFERENCES muc(id),
23
+ FOREIGN KEY(user_id) REFERENCES user(id),
24
+ UNIQUE(user_id, muc_id, message_id)
25
+ );
26
+
27
+ CREATE INDEX mam_sent_on ON mam_message(sent_on);
28
+ CREATE INDEX muc_jid ON muc(jid);
29
+
30
+ CREATE TABLE session_message_sent(
31
+ id INTEGER PRIMARY KEY,
32
+ legacy_id UNIQUE,
33
+ xmpp_id TEXT,
34
+ user_id INTEGER,
35
+ FOREIGN KEY(user_id) REFERENCES user(id)
36
+ );
37
+
38
+ CREATE INDEX session_message_sent_legacy_id
39
+ ON session_message_sent(legacy_id);
40
+ CREATE INDEX session_message_sent_xmpp_id
41
+ ON session_message_sent(xmpp_id);
42
+
43
+ CREATE TABLE session_message_sent_muc(
44
+ id INTEGER PRIMARY KEY,
45
+ legacy_id UNIQUE,
46
+ xmpp_id TEXT,
47
+ user_id INTEGER,
48
+ FOREIGN KEY(user_id) REFERENCES user(id)
49
+ );
50
+
51
+ CREATE INDEX session_message_sent_muc_legacy_id
52
+ ON session_message_sent_muc(legacy_id);
53
+ CREATE INDEX session_message_sent_muc_xmpp_id
54
+ ON session_message_sent_muc(xmpp_id);
55
+
56
+ CREATE TABLE session_thread_sent_muc(
57
+ id INTEGER PRIMARY KEY,
58
+ legacy_id UNIQUE,
59
+ xmpp_id TEXT,
60
+ user_id INTEGER,
61
+ FOREIGN KEY(user_id) REFERENCES user(id)
62
+ );
63
+
64
+ CREATE INDEX session_thread_sent_muc_legacy_id
65
+ ON session_thread_sent_muc(legacy_id);
66
+ CREATE INDEX session_thread_sent_muc_xmpp_id
67
+ ON session_thread_sent_muc(xmpp_id);
68
+
69
+
70
+ CREATE TABLE attachment(
71
+ id INTEGER PRIMARY KEY,
72
+ legacy_id UNIQUE,
73
+ url TEXT UNIQUE,
74
+ sims TEXT,
75
+ sfs TEXT
76
+ );
77
+
78
+ CREATE INDEX attachment_legacy_id ON attachment(legacy_id);
79
+ CREATE INDEX attachment_url ON attachment(url);
80
+
81
+ CREATE TABLE attachment_legacy_msg_id(
82
+ id INTEGER PRIMARY KEY,
83
+ legacy_id UNIQUE
84
+ );
85
+
86
+ CREATE TABLE attachment_xmpp_ids(
87
+ id INTEGER PRIMARY KEY,
88
+ legacy_msg_id INTEGER,
89
+ xmpp_id TEXT,
90
+ FOREIGN KEY(legacy_msg_id) REFERENCES attachment_legacy_msg_id(id)
91
+ );
92
+
93
+ CREATE TABLE nick(
94
+ id INTEGER PRIMARY KEY,
95
+ jid UNIQUE,
96
+ nick TEXT,
97
+ user_id INTEGER,
98
+ FOREIGN KEY(user_id) REFERENCES user(id),
99
+ UNIQUE(jid, user_id)
100
+ );
101
+
102
+ CREATE INDEX nick_jid ON nick(jid);
103
+
104
+
105
+ CREATE TABLE avatar(
106
+ id INTEGER PRIMARY KEY,
107
+ jid TEXT UNIQUE,
108
+ cached_id TEXT
109
+ );
110
+
111
+ CREATE INDEX avatar_jid ON avatar(jid);
112
+
113
+
114
+ CREATE TABLE presence(
115
+ id INTEGER PRIMARY KEY,
116
+ jid TEXT,
117
+ last_seen INTEGER,
118
+ ptype TEXT,
119
+ pstatus TEXT,
120
+ pshow TEXT,
121
+ user_id INTEGER,
122
+ FOREIGN KEY(user_id) REFERENCES user(id),
123
+ UNIQUE(jid, user_id)
124
+ );
125
+
126
+ CREATE INDEX presence_jid ON presence(jid);