slidge 0.1.0rc1__py3-none-any.whl → 0.1.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. slidge/__init__.py +54 -31
  2. slidge/__main__.py +51 -5
  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 +121 -39
  16. slidge/core/config.py +116 -11
  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 +795 -0
  29. slidge/core/gateway/vcard_temp.py +130 -0
  30. slidge/core/mixins/__init__.py +9 -1
  31. slidge/core/mixins/attachment.py +506 -0
  32. slidge/core/mixins/avatar.py +167 -0
  33. slidge/core/mixins/base.py +6 -19
  34. slidge/core/mixins/disco.py +66 -15
  35. slidge/core/mixins/lock.py +31 -0
  36. slidge/core/mixins/message.py +254 -252
  37. slidge/core/mixins/message_maker.py +154 -0
  38. slidge/core/mixins/presence.py +128 -31
  39. slidge/core/mixins/recipient.py +43 -0
  40. slidge/core/pubsub.py +275 -116
  41. slidge/core/session.py +586 -518
  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_0050 → slixfix/link_preview}/__init__.py +4 -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 +1 -2
  54. slidge/slixfix/xep_0077/stanza.py +104 -0
  55. slidge/{util → slixfix}/xep_0100/gateway.py +17 -12
  56. slidge/slixfix/xep_0153/__init__.py +10 -0
  57. slidge/slixfix/xep_0153/stanza.py +25 -0
  58. slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
  59. slidge/slixfix/xep_0264/__init__.py +5 -0
  60. slidge/slixfix/xep_0264/stanza.py +36 -0
  61. slidge/slixfix/xep_0264/thumbnail.py +23 -0
  62. slidge/slixfix/xep_0292/__init__.py +5 -0
  63. slidge/slixfix/xep_0292/vcard4.py +100 -0
  64. slidge/slixfix/xep_0313/__init__.py +12 -0
  65. slidge/slixfix/xep_0313/mam.py +262 -0
  66. slidge/slixfix/xep_0313/stanza.py +359 -0
  67. slidge/slixfix/xep_0317/__init__.py +5 -0
  68. slidge/slixfix/xep_0317/hats.py +17 -0
  69. slidge/slixfix/xep_0317/stanza.py +28 -0
  70. slidge/{util → slixfix}/xep_0356_old/privilege.py +9 -7
  71. slidge/slixfix/xep_0424/__init__.py +9 -0
  72. slidge/slixfix/xep_0424/retraction.py +77 -0
  73. slidge/slixfix/xep_0424/stanza.py +28 -0
  74. slidge/slixfix/xep_0490/__init__.py +8 -0
  75. slidge/slixfix/xep_0490/mds.py +47 -0
  76. slidge/slixfix/xep_0490/stanza.py +17 -0
  77. slidge/util/__init__.py +4 -6
  78. slidge/util/archive_msg.py +61 -0
  79. slidge/util/conf.py +25 -4
  80. slidge/util/db.py +23 -69
  81. slidge/util/schema.sql +126 -0
  82. slidge/util/sql.py +508 -0
  83. slidge/util/test.py +136 -86
  84. slidge/util/types.py +155 -14
  85. slidge/util/util.py +225 -51
  86. slidge-0.1.2.dist-info/METADATA +111 -0
  87. slidge-0.1.2.dist-info/RECORD +96 -0
  88. {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/WHEEL +1 -1
  89. slidge/core/adhoc.py +0 -492
  90. slidge/core/chat_command.py +0 -197
  91. slidge/core/contact.py +0 -441
  92. slidge/core/disco.py +0 -59
  93. slidge/core/gateway.py +0 -899
  94. slidge/core/muc/__init__.py +0 -3
  95. slidge/core/muc/bookmarks.py +0 -74
  96. slidge/core/muc/participant.py +0 -152
  97. slidge/core/muc/room.py +0 -348
  98. slidge/plugins/discord/__init__.py +0 -121
  99. slidge/plugins/discord/client.py +0 -121
  100. slidge/plugins/discord/session.py +0 -172
  101. slidge/plugins/dummy.py +0 -334
  102. slidge/plugins/facebook.py +0 -591
  103. slidge/plugins/hackernews.py +0 -209
  104. slidge/plugins/mattermost/__init__.py +0 -1
  105. slidge/plugins/mattermost/api.py +0 -288
  106. slidge/plugins/mattermost/gateway.py +0 -417
  107. slidge/plugins/mattermost/websocket.py +0 -248
  108. slidge/plugins/signal/__init__.py +0 -4
  109. slidge/plugins/signal/config.py +0 -4
  110. slidge/plugins/signal/contact.py +0 -104
  111. slidge/plugins/signal/gateway.py +0 -379
  112. slidge/plugins/signal/group.py +0 -76
  113. slidge/plugins/signal/session.py +0 -515
  114. slidge/plugins/signal/txt.py +0 -13
  115. slidge/plugins/signal/util.py +0 -32
  116. slidge/plugins/skype.py +0 -310
  117. slidge/plugins/steam.py +0 -400
  118. slidge/plugins/telegram/__init__.py +0 -6
  119. slidge/plugins/telegram/client.py +0 -325
  120. slidge/plugins/telegram/config.py +0 -21
  121. slidge/plugins/telegram/contact.py +0 -154
  122. slidge/plugins/telegram/gateway.py +0 -182
  123. slidge/plugins/telegram/group.py +0 -184
  124. slidge/plugins/telegram/session.py +0 -275
  125. slidge/plugins/telegram/util.py +0 -153
  126. slidge/plugins/whatsapp/__init__.py +0 -6
  127. slidge/plugins/whatsapp/config.py +0 -17
  128. slidge/plugins/whatsapp/contact.py +0 -33
  129. slidge/plugins/whatsapp/event.go +0 -455
  130. slidge/plugins/whatsapp/gateway.go +0 -156
  131. slidge/plugins/whatsapp/gateway.py +0 -69
  132. slidge/plugins/whatsapp/go.mod +0 -17
  133. slidge/plugins/whatsapp/go.sum +0 -22
  134. slidge/plugins/whatsapp/session.go +0 -371
  135. slidge/plugins/whatsapp/session.py +0 -370
  136. slidge/util/xep_0030/__init__.py +0 -13
  137. slidge/util/xep_0030/disco.py +0 -811
  138. slidge/util/xep_0030/stanza/__init__.py +0 -7
  139. slidge/util/xep_0030/stanza/info.py +0 -270
  140. slidge/util/xep_0030/stanza/items.py +0 -147
  141. slidge/util/xep_0030/static.py +0 -467
  142. slidge/util/xep_0050/adhoc.py +0 -631
  143. slidge/util/xep_0050/stanza.py +0 -180
  144. slidge/util/xep_0077/stanza.py +0 -71
  145. slidge/util/xep_0292/__init__.py +0 -1
  146. slidge/util/xep_0292/stanza.py +0 -167
  147. slidge/util/xep_0292/vcard4.py +0 -74
  148. slidge/util/xep_0356/__init__.py +0 -7
  149. slidge/util/xep_0356/permissions.py +0 -35
  150. slidge/util/xep_0356/privilege.py +0 -160
  151. slidge/util/xep_0356/stanza.py +0 -44
  152. slidge/util/xep_0461/__init__.py +0 -6
  153. slidge/util/xep_0461/reply.py +0 -48
  154. slidge/util/xep_0461/stanza.py +0 -80
  155. slidge-0.1.0rc1.dist-info/METADATA +0 -171
  156. slidge-0.1.0rc1.dist-info/RECORD +0 -99
  157. /slidge/{plugins/__init__.py → py.typed} +0 -0
  158. /slidge/{util → slixfix}/xep_0077/__init__.py +0 -0
  159. /slidge/{util → slixfix}/xep_0100/__init__.py +0 -0
  160. /slidge/{util → slixfix}/xep_0100/stanza.py +0 -0
  161. /slidge/{util → slixfix}/xep_0356_old/__init__.py +0 -0
  162. /slidge/{util → slixfix}/xep_0356_old/stanza.py +0 -0
  163. {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/LICENSE +0 -0
  164. {slidge-0.1.0rc1.dist-info → slidge-0.1.2.dist-info}/entry_points.txt +0 -0
@@ -1,104 +0,0 @@
1
- import asyncio
2
- import functools
3
- import logging
4
- from pathlib import Path
5
- from typing import TYPE_CHECKING, Optional
6
-
7
- import aiosignald.exc as sigexc
8
- import aiosignald.generated as sigapi
9
- from slixmpp.exceptions import XMPPError
10
-
11
- from slidge import *
12
-
13
- from .util import AttachmentSenderMixin
14
-
15
- if TYPE_CHECKING:
16
- from .group import Participant
17
- from .session import Session
18
-
19
-
20
- class Contact(AttachmentSenderMixin, LegacyContact["Session", str]):
21
- CORRECTION = False
22
- REACTIONS_SINGLE_EMOJI = True
23
-
24
- def __init__(self, *a, **k):
25
- super().__init__(*a, **k)
26
- # keys = msg timestamp; vals = single character emoji
27
- self.user_reactions = dict[int, str]()
28
- self.xmpp.loop.create_task(self._update_info())
29
-
30
- @functools.cached_property
31
- def signal_address(self):
32
- return sigapi.JsonAddressv1(uuid=self.legacy_id)
33
-
34
- async def get_identities(self):
35
- s = await self.session.signal
36
- log.debug("%s, %s", type(self.session.phone), type(self.signal_address))
37
- try:
38
- r = await s.get_identities(
39
- account=self.session.phone,
40
- address=self.signal_address,
41
- )
42
- except sigexc.UnregisteredUserError:
43
- raise XMPPError("not-found")
44
- identities = r.identities
45
- self.session.send_gateway_message(str(identities))
46
-
47
- async def get_profile(self, max_attempts=10, sleep=1, exp=2):
48
- attempts = 0
49
- while attempts < max_attempts:
50
- try:
51
- profile = await (await self.session.signal).get_profile(
52
- account=self.session.phone, address=self.signal_address
53
- )
54
- except sigexc.ProfileUnavailableError as e:
55
- log.debug(
56
- "Could not fetch the profile of a contact: %s, retrying later...",
57
- e.message,
58
- )
59
- else:
60
- if profile.profile_name or profile.name or profile.contact_name:
61
- return profile
62
- attempts += 1
63
- await asyncio.sleep(sleep * attempts**exp)
64
-
65
- async def _update_info(self, profile: Optional[sigapi.Profilev1] = None):
66
- if profile is None:
67
- profile = await self.get_profile()
68
- if profile is None:
69
- log.warning(
70
- "Could not update avatar, nickname, and phone of %s",
71
- self.signal_address,
72
- )
73
- return
74
-
75
- nick = profile.name or profile.profile_name
76
- if nick is not None:
77
- nick = nick.replace("\u0000", " ")
78
- self.name = nick
79
- if profile.avatar is not None:
80
- self.avatar = Path(profile.avatar)
81
-
82
- address = await (await self.session.signal).resolve_address(
83
- account=self.session.phone,
84
- partial=sigapi.JsonAddressv1(uuid=self.legacy_id),
85
- )
86
-
87
- self.set_vcard(full_name=nick, phone=address.number, note=profile.about)
88
- await self.add_to_roster()
89
- self.online()
90
-
91
-
92
- class Roster(LegacyRoster["Session", Contact, str]):
93
- async def by_uuid(self, uuid: str):
94
- return await self.by_json_address(sigapi.JsonAddressv1(uuid=uuid))
95
-
96
- async def by_json_address(self, address: sigapi.JsonAddressv1):
97
- return await self.by_legacy_id(address.uuid)
98
-
99
- async def jid_username_to_legacy_id(self, jid_username: str):
100
- if jid_username in self.session.bookmarks.known_groups:
101
- raise XMPPError("bad-request", "This is a group ID, not a contact ID")
102
-
103
-
104
- log = logging.getLogger(__name__)
@@ -1,379 +0,0 @@
1
- import asyncio
2
- import functools
3
- import logging
4
- import tempfile
5
- from datetime import datetime
6
- from pathlib import Path
7
- from typing import TYPE_CHECKING, Any, Optional
8
-
9
- import aiosignald.exc as sigexc
10
- import aiosignald.generated as sigapi
11
- import qrcode
12
- from aiosignald import SignaldAPI
13
- from slixmpp import JID, Iq, Message
14
- from slixmpp.exceptions import XMPPError
15
- from slixmpp.plugins.xep_0004 import Form
16
- from slixmpp.plugins.xep_0004 import FormField as AdhocFormField
17
-
18
- from slidge import *
19
- from slidge.util import is_valid_phone_number
20
-
21
- from ...core.adhoc import RegistrationType
22
-
23
- if TYPE_CHECKING:
24
- from .session import Session
25
-
26
- from . import config, txt
27
-
28
-
29
- class Gateway(BaseGateway):
30
- COMPONENT_NAME = "Signal (slidge)"
31
- COMPONENT_TYPE = "signal"
32
- COMPONENT_AVATAR = (
33
- "https://upload.wikimedia.org/wikipedia/commons/5/56/Logo_Signal..png"
34
- )
35
- REGISTRATION_INSTRUCTIONS = txt.REGISTRATION_INSTRUCTIONS
36
- REGISTRATION_FIELDS = txt.REGISTRATION_FIELDS
37
- REGISTRATION_TYPE = RegistrationType.TWO_FACTOR_CODE
38
-
39
- ROSTER_GROUP = "Signal"
40
-
41
- SEARCH_FIELDS = [
42
- FormField(var="phone", label="Phone number", required=True),
43
- ]
44
-
45
- signal: asyncio.Future["Signal"]
46
- sessions_by_phone: dict[str, "Session"] = {}
47
-
48
- CHAT_COMMANDS = {
49
- "add_device": "_chat_command_add_device",
50
- "get_identities": "_chat_command_get_identities",
51
- }
52
-
53
- GROUPS = True
54
-
55
- def __init__(self):
56
- super().__init__()
57
- self.signal: asyncio.Future[Signal] = self.loop.create_future()
58
- self.loop.create_task(self.connect_signal(config.SIGNALD_SOCKET))
59
-
60
- async def connect_signal(self, socket: Path):
61
- """
62
- Establish connection to the signald socker
63
- """
64
- log.debug("Connecting to signald...")
65
- _, signal = await self.loop.create_unix_connection(
66
- functools.partial(Signal, self), str(socket)
67
- )
68
- self.signal.set_result(signal)
69
- await signal.on_con_lost
70
- log.error("Signald UNIX socket connection lost!")
71
- raise RuntimeError("Signald socket connection lost")
72
-
73
- def add_adhoc_commands(self):
74
- self.adhoc.add_command(
75
- node="link",
76
- name="Link slidge to your signal account",
77
- handler=self._handle_link_slidge,
78
- only_nonusers=True,
79
- )
80
- self.adhoc.add_command(
81
- node="linked_devices",
82
- name="Get linked devices",
83
- handler=self._handle_linked_devices,
84
- only_users=True,
85
- )
86
- self.adhoc.add_command(
87
- node="add_device",
88
- name="Link a new device",
89
- handler=self._handle_add_device1,
90
- only_users=True,
91
- )
92
-
93
- async def _handle_link_slidge(self, iq: Iq, adhoc_session: dict[str, Any]):
94
- user = user_store.get_by_stanza(iq)
95
- if user is not None:
96
- raise XMPPError(
97
- "bad-request", text="You are already registered to this gateway."
98
- )
99
-
100
- form = self["xep_0004"].make_form(
101
- "form",
102
- "Enter a device name to identify your slidge session in the official signal app. "
103
- "Prepare to scan the QR code that you will see on the next step.",
104
- )
105
- form.add_field(
106
- var="phone",
107
- ftype="text-single",
108
- label="Your phone number in international format",
109
- required=True,
110
- )
111
- form.add_field(
112
- var="device",
113
- ftype="text-single",
114
- label="Name of this device",
115
- value="slidge",
116
- required=True,
117
- )
118
-
119
- adhoc_session["payload"] = form
120
- adhoc_session["has_next"] = True
121
- adhoc_session["next"] = self._handle_link_slidge2
122
-
123
- return adhoc_session
124
-
125
- async def _handle_link_slidge2(self, form: Form, adhoc_session: dict[str, Any]):
126
- resp = await (await self.signal).generate_linking_uri()
127
- qr_text = resp.uri
128
-
129
- qr = qrcode.make(qr_text)
130
- with tempfile.NamedTemporaryFile(suffix=".png") as f:
131
- qr.save(f.name)
132
- img_url = await self.plugin["xep_0363"].upload_file(
133
- filename=Path(f.name), ifrom=global_config.UPLOAD_REQUESTER
134
- )
135
-
136
- msg = self.make_message(mto=adhoc_session["from"])
137
- msg.set_from(self.boundjid.bare)
138
- msg["oob"]["url"] = img_url
139
- msg["body"] = img_url
140
- msg.send()
141
-
142
- msg = self.make_message(mto=adhoc_session["from"])
143
- msg.set_from(self.boundjid.bare)
144
- msg["body"] = qr_text
145
- msg.send()
146
- form_values = form.get_values()
147
-
148
- form = self.plugin["xep_0004"].make_form(
149
- title="Flash this",
150
- instructions="Flash this QR in the official signal app",
151
- )
152
- img = AdhocFormField()
153
- img["media"]["height"] = "200"
154
- img["media"]["width"] = "200"
155
- img["media"]["alt"] = "The thing to flash"
156
- img["media"].add_uri(img_url, itype="image/png")
157
- form.append(img)
158
-
159
- adhoc_session["payload"] = form
160
- adhoc_session["has_next"] = True
161
- adhoc_session["next"] = self._handle_link_slidge3
162
- adhoc_session["device"] = form_values["device"]
163
- adhoc_session["phone"] = form_values["phone"]
164
- adhoc_session["signal_session_id"] = resp.session_id
165
- return adhoc_session
166
-
167
- async def _handle_link_slidge3(self, _payload, adhoc_session: dict[str, Any]):
168
- try:
169
- await (await self.signal).finish_link(
170
- device_name=adhoc_session["device"],
171
- session_id=adhoc_session["signal_session_id"],
172
- )
173
- except sigexc.ScanTimeoutError:
174
- raise XMPPError(
175
- "not-authorized", "You took too much time to scan. Please retry."
176
- )
177
- except sigexc.SignaldException as e:
178
- raise XMPPError("not-authorized", f"Something went wrong: {e}.")
179
- user_store.add(adhoc_session["from"], {"phone": adhoc_session["phone"]})
180
-
181
- adhoc_session["has_next"] = False
182
- adhoc_session["next"] = None
183
- adhoc_session["payload"] = None
184
- adhoc_session["notes"] = [("info", "Success!")]
185
- return adhoc_session
186
-
187
- async def _handle_linked_devices(self, iq: Iq, adhoc_session: dict[str, Any]):
188
- user = user_store.get_by_stanza(iq)
189
- if user is None:
190
- raise XMPPError("subscription-required")
191
-
192
- devices = await (await self.signal).get_linked_devices(
193
- account=user.registration_form["phone"]
194
- )
195
-
196
- # does not work in gajim https://dev.gajim.org/gajim/gajim/-/issues/10857 is fixed
197
- form = self["xep_0004"].make_form("result", "Linked devices")
198
- form.add_reported("id", label="ID", type="fixed")
199
- form.add_reported("name", label="Name", type="fixed")
200
- form.add_reported("created", label="Created", type="fixed")
201
- form.add_reported("last_seen", label="Last seen", type="fixed")
202
- for d in devices.devices:
203
- form.add_item(
204
- {
205
- "name": d.name,
206
- "id": str(d.id),
207
- "created": datetime.fromtimestamp(d.created / 1000).isoformat(),
208
- "last_seen": datetime.fromtimestamp(d.lastSeen / 1000).isoformat(),
209
- }
210
- )
211
-
212
- adhoc_session["payload"] = form
213
- adhoc_session["has_next"] = False
214
-
215
- return adhoc_session
216
-
217
- async def _handle_add_device1(self, iq: Iq, adhoc_session: dict[str, Any]):
218
- user = user_store.get_by_stanza(iq)
219
- if user is None:
220
- raise XMPPError("subscription-required")
221
-
222
- form = self["xep_0004"].make_form(
223
- "form", "Link a new device to your signal account"
224
- )
225
- form.add_field(
226
- var="uri",
227
- ftype="text-single",
228
- label="Linking URI. Use a QR code reader app to get it from official signal clients.",
229
- required=True,
230
- )
231
-
232
- adhoc_session["payload"] = form
233
- adhoc_session["has_next"] = True
234
- adhoc_session["next"] = self._handle_add_device2
235
-
236
- return adhoc_session
237
-
238
- async def _handle_add_device2(self, stanza: Form, adhoc_session: dict[str, Any]):
239
- user = user_store.get_by_jid(adhoc_session["from"])
240
- if user is None:
241
- raise XMPPError("subscription-required")
242
-
243
- values = stanza.get_values()
244
- uri = values.get("uri")
245
-
246
- await (await self.signal).add_device(
247
- account=user.registration_form["phone"], uri=uri
248
- )
249
-
250
- adhoc_session["notes"] = [
251
- (
252
- "info",
253
- "Your new device is now correctly linked to your signal account",
254
- )
255
- ]
256
- adhoc_session["has_next"] = False
257
-
258
- return adhoc_session
259
-
260
- @staticmethod
261
- async def _chat_command_add_device(
262
- *args, msg: Message, session: Optional["Session"] = None
263
- ):
264
- if session is None:
265
- msg.reply("I don't know you, so don't talk to me").send()
266
- return
267
- if len(args) == 0:
268
- uri = await session.input("URI?")
269
- elif len(args) > 1:
270
- msg.reply("Syntax error! Use 'add_device [LINKING_URI]'").send()
271
- return
272
- else:
273
- uri = args[0]
274
- await session.add_device(uri)
275
-
276
- @staticmethod
277
- async def _chat_command_get_identities(
278
- *args, msg: Message, session: Optional["Session"] = None
279
- ):
280
- if session is None:
281
- msg.reply("I don't know you, so don't talk to me").send()
282
- return
283
- if len(args) == 0:
284
- uuid = await session.input("UUID?")
285
- elif len(args) > 1:
286
- msg.reply("Syntax error! Use 'get_identities [UUID]'").send()
287
- return
288
- else:
289
- uuid = args[0]
290
- await (await session.contacts.by_legacy_id(uuid)).get_identities()
291
-
292
- async def validate(
293
- self, user_jid: JID, registration_form: dict[str, Optional[str]]
294
- ):
295
- phone = registration_form.get("phone")
296
- if not is_valid_phone_number(phone):
297
- raise ValueError("Not a valid phone number")
298
- for u in user_store.get_all():
299
- if u.registration_form.get("phone") == phone:
300
- raise XMPPError(
301
- "not-allowed",
302
- text="Someone is already using this phone number on this server.\n",
303
- )
304
- signal = await self.signal
305
- try:
306
- await signal.register(phone, captcha=registration_form.get("captcha"))
307
- except sigexc.CaptchaRequiredError:
308
- raise XMPPError(
309
- "not-acceptable",
310
- "Please fill the captcha to register your phone number.",
311
- etype="modify",
312
- )
313
-
314
- async def validate_two_factor_code(self, user: GatewayUser, code: str):
315
- signal = await self.signal
316
- phone = user.registration_form.get("phone")
317
- await signal.verify(phone, code)
318
- await signal.set_profile(account=phone, name=user.registration_form.get("name"))
319
-
320
- async def unregister(self, user: GatewayUser):
321
- try:
322
- await (await self.signal).delete_account(
323
- account=user.registration_form.get("phone"), server=False
324
- )
325
- except sigexc.NoSuchAccountError:
326
- # if user unregisters before completing the registration process,
327
- # NoSuchAccountError is raised by signald
328
- pass
329
-
330
- log.info("Removed user: %s", user)
331
-
332
-
333
- # noinspection PyPep8Naming,GrazieInspection
334
- class Signal(SignaldAPI):
335
- """
336
- Extends :class:`.SignaldAPI` with handlers for events we are interested in.
337
- """
338
-
339
- def __init__(self, xmpp: Gateway):
340
- super().__init__()
341
- self.sessions_by_phone = xmpp.sessions_by_phone
342
-
343
- async def handle_WebSocketConnectionState(
344
- self, state: sigapi.WebSocketConnectionStatev1, payload
345
- ):
346
- """
347
- We should not care much about this since
348
-
349
- :param state:
350
- :param payload:
351
- """
352
- session = self.sessions_by_phone[payload["account"]]
353
- await session.on_websocket_connection_state(state)
354
-
355
- async def handle_ListenerState(self, state: sigapi.ListenerStatev1, payload):
356
- """
357
- Deprecated in signald and replaced by WebSocketConnectionState
358
- Just here to avoid cluttering logs with unhandled events warnings
359
-
360
- :param state:
361
- :param payload:
362
- """
363
- pass
364
-
365
- async def handle_IncomingMessage(self, msg: sigapi.IncomingMessagev1, _payload):
366
- """
367
- Dispatch a signald message to the proper session.
368
-
369
- Can be a lot of other things than an actual message, still need to figure
370
- things out to cover all cases.
371
-
372
- :param msg: the data!
373
- :param _payload:
374
- """
375
- session = self.sessions_by_phone[msg.account]
376
- await session.on_signal_message(msg)
377
-
378
-
379
- log = logging.getLogger(__name__)
@@ -1,76 +0,0 @@
1
- from typing import TYPE_CHECKING, Optional, Union
2
-
3
- import aiosignald.generated as sigapi
4
- from slixmpp.exceptions import XMPPError
5
- from slixmpp.jid import _unescape_node
6
-
7
- from slidge import *
8
- from slidge.core.contact import ESCAPE_TABLE
9
-
10
- from .util import AttachmentSenderMixin
11
-
12
- if TYPE_CHECKING:
13
- from .contact import Contact
14
- from .session import Session
15
-
16
-
17
- class Participant(AttachmentSenderMixin, LegacyParticipant):
18
- contact: "Contact"
19
- muc: "MUC"
20
- signal_address: sigapi.JsonAddressv1
21
-
22
- def send_text(self, body: str, legacy_msg_id=None, **k):
23
- if legacy_msg_id:
24
- self.muc.sent[legacy_msg_id] = self.signal_address
25
- super().send_text(body, legacy_msg_id, **k)
26
-
27
-
28
- class MUC(LegacyMUC["Session", str, Participant, int]):
29
- REACTIONS_SINGLE_EMOJI = True
30
-
31
- session: "Session"
32
-
33
- type = MucType.GROUP
34
-
35
- def __init__(self, *a, **k):
36
- super().__init__(*a, **k)
37
- self.sent = dict[int, sigapi.JsonAddressv1]()
38
-
39
- async def get_participants(self):
40
- group = await (await self.session.signal).get_group(
41
- account=self.session.phone, groupID=self.legacy_id
42
- )
43
- for m in group.members:
44
- if m.uuid == self.session.user_uuid:
45
- continue
46
- contact = await self.session.contacts.by_uuid(m.uuid)
47
- participant = await self.get_participant_by_contact(contact)
48
- yield participant
49
-
50
- async def get_participant_by_contact(self, contact):
51
- p = await self.get_participant(contact.name)
52
- p.contact = contact
53
- p.signal_address = contact.signal_address
54
- return p
55
-
56
- async def fill_history(self, *_, **__):
57
- pass
58
-
59
-
60
- class Bookmarks(LegacyBookmarks["Session", MUC, str]):
61
- def __init__(self, session: "Session"):
62
- super().__init__(session)
63
-
64
- # maps case-insensitive JID local parts to case-sensitive signal group IDs
65
- self.known_groups = dict[str, str]()
66
-
67
- async def jid_local_part_to_legacy_id(self, local_part: str):
68
- try:
69
- return self.known_groups[_unescape_node(local_part.lower())]
70
- except KeyError:
71
- raise XMPPError("item-not-found", "I don't know this group")
72
-
73
- async def legacy_id_to_jid_local_part(self, legacy_id: str):
74
- local_part = legacy_id.lower().translate(ESCAPE_TABLE)
75
- self.known_groups[local_part] = legacy_id
76
- return local_part