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,121 +0,0 @@
1
- from typing import TYPE_CHECKING, Union
2
-
3
- import discord as di
4
-
5
- if TYPE_CHECKING:
6
- from .session import Session
7
-
8
-
9
- class Discord(di.Client):
10
- def __init__(self, session: "Session"):
11
- super().__init__()
12
- self.session = session
13
- self.log = session.log
14
-
15
- async def on_ready(self):
16
- if (f := self.session.ready_future).done():
17
- return
18
- f.set_result(True)
19
- self.log.debug(f"Logged on as {self.user}")
20
-
21
- async def on_message(self, message: di.Message):
22
- channel = message.channel
23
- if not isinstance(channel, di.DMChannel):
24
- return
25
-
26
- if (author := message.author) == self.user:
27
- async with self.session.send_lock:
28
- fut = self.session.send_futures.get(message.id)
29
- if fut is None:
30
- (
31
- await self.session.contacts.by_discord_user(channel.recipient)
32
- ).send_text(message.content, carbon=True)
33
- else:
34
- fut.set_result(True)
35
- else:
36
- contact = await self.session.contacts.by_discord_user(author)
37
- reply_to = message.reference.message_id if message.reference else None
38
-
39
- text = message.content
40
- attachments = message.attachments
41
- msg_id = message.id
42
-
43
- if not attachments:
44
- contact.send_text(
45
- text,
46
- legacy_msg_id=msg_id,
47
- reply_to_msg_id=reply_to,
48
- )
49
- return
50
-
51
- last_attachment_i = len(attachments := message.attachments) - 1
52
- for i, attachment in enumerate(attachments):
53
- last = i == last_attachment_i
54
- await contact.send_file(
55
- url=attachment.url,
56
- filename=attachment.filename,
57
- content_type=attachment.content_type,
58
- reply_to_msg_id=reply_to if last else None,
59
- legacy_msg_id=msg_id if last else None,
60
- caption=text if last else None,
61
- )
62
-
63
- async def on_typing(self, channel, user, _when):
64
- if user != self.user and isinstance(channel, di.DMChannel):
65
- (await self.session.contacts.by_discord_user(user)).composing()
66
-
67
- async def on_message_edit(self, before: di.Message, after: di.Message):
68
- if not isinstance(after.channel, di.DMChannel):
69
- return
70
- if before.content == after.content:
71
- return
72
- if (author := after.author) == self.user:
73
- fut = self.session.edit_futures.get(after.id)
74
- if fut is None:
75
- (
76
- await self.session.contacts.by_discord_user(after.channel.recipient)
77
- ).correct(after.id, after.content, carbon=True)
78
- else:
79
- fut.set_result(True)
80
- else:
81
- (await self.session.contacts.by_discord_user(author)).correct(
82
- after.id, after.content
83
- )
84
-
85
- async def on_message_delete(self, m: di.Message):
86
- if not isinstance(m.channel, di.DMChannel):
87
- return
88
- if (author := m.author) == self.user:
89
- fut = self.session.delete_futures.get(m.id)
90
- if fut is None:
91
- (
92
- await self.session.contacts.by_discord_user(m.channel.recipient)
93
- ).retract(m.id, carbon=True)
94
- else:
95
- fut.set_result(True)
96
- else:
97
- (await self.session.contacts.by_discord_user(author)).retract(m.id)
98
-
99
- async def on_reaction_add(
100
- self, reaction: di.Reaction, user: Union[di.User, di.ClientUser]
101
- ):
102
- await self.update_reactions(reaction, user)
103
-
104
- async def on_reaction_remove(
105
- self, reaction: di.Reaction, user: Union[di.User, di.ClientUser]
106
- ):
107
- await self.update_reactions(reaction, user)
108
-
109
- async def update_reactions(
110
- self, reaction: di.Reaction, user: Union[di.User, di.ClientUser]
111
- ):
112
- message: di.Message = reaction.message
113
- if not isinstance(message.channel, di.DMChannel):
114
- return
115
-
116
- if user == self.user:
117
- await self.session.update_reactions(message)
118
- else:
119
- await (await self.session.contacts.by_discord_user(user)).update_reactions(
120
- message
121
- )
@@ -1,172 +0,0 @@
1
- import asyncio
2
- from typing import TYPE_CHECKING, Any, Optional, Union
3
-
4
- import discord as di
5
-
6
- from slidge import *
7
-
8
- from ...util.types import Chat
9
-
10
- if TYPE_CHECKING:
11
- from . import Contact, Gateway, Roster
12
- from .client import Discord
13
-
14
-
15
- class Session(
16
- BaseSession[
17
- "Gateway",
18
- int,
19
- "Roster",
20
- "Contact",
21
- LegacyBookmarks,
22
- LegacyMUC,
23
- LegacyParticipant,
24
- ]
25
- ):
26
- def __init__(self, user):
27
- super().__init__(user)
28
- from .client import Discord
29
-
30
- self.discord = Discord(self)
31
- self.ready_future: asyncio.Future[bool] = self.xmpp.loop.create_future()
32
- self.delete_futures = dict[int, asyncio.Future[bool]]()
33
- self.edit_futures = dict[int, asyncio.Future[bool]]()
34
- self.send_futures = dict[int, asyncio.Future[bool]]()
35
- self.send_lock = asyncio.Lock()
36
-
37
- @staticmethod
38
- def xmpp_msg_id_to_legacy_msg_id(i: str):
39
- return int(i)
40
-
41
- async def login(self):
42
- await self.discord.login(self.user.registration_form["token"])
43
- self.xmpp.loop.create_task(self.discord.connect())
44
-
45
- await self.ready_future
46
- for u in self.discord.users:
47
- if not isinstance(u, di.User):
48
- self.log.debug(f"Skipping %s", u)
49
- continue
50
- if not u.is_friend():
51
- self.log.debug(f"%s is not a friend", u)
52
- continue
53
- c = await self.contacts.by_legacy_id(u.id)
54
- await c.update_info()
55
- await c.add_to_roster()
56
- # TODO: contribute to discord.py-self so that the presence information
57
- # of relationships is parsed. logs show:
58
- # 'PRESENCE_UPDATE referencing an unknown guild ID: %s. Discarding.'
59
- # https://github.com/dolfies/discord.py-self/blob/master/discord/state.py#L1044
60
- c.online()
61
- return f"Logged on as {self.discord.user}"
62
-
63
- async def send_text(
64
- self,
65
- text: str,
66
- chat,
67
- reply_to_msg_id=None,
68
- reply_to_fallback_text: Optional[str] = None,
69
- **kwargs,
70
- ):
71
- async with self.send_lock:
72
- mid = (
73
- await chat.discord_user.send(
74
- text,
75
- reference=None
76
- if reply_to_msg_id is None
77
- else di.MessageReference(
78
- message_id=reply_to_msg_id,
79
- channel_id=chat.direct_channel_id,
80
- ),
81
- )
82
- ).id
83
- f = self.send_futures[mid] = self.xmpp.loop.create_future()
84
- await f
85
- return mid
86
-
87
- async def logout(self):
88
- await self.discord.close()
89
-
90
- async def send_file(self, url: str, chat: Chat, **kwargs):
91
- # discord clients inline previews of external URLs, so no need to actually send on discord servers
92
- await chat.discord_user.send(url)
93
-
94
- async def active(self, c: "Contact"):
95
- pass
96
-
97
- async def inactive(self, c: "Contact"):
98
- pass
99
-
100
- async def composing(self, c: "Contact"):
101
- await c.discord_user.trigger_typing()
102
-
103
- async def paused(self, c: "Contact"):
104
- pass
105
-
106
- async def displayed(self, legacy_msg_id: int, c: "Contact"):
107
- if not isinstance(legacy_msg_id, int):
108
- self.log.debug("This is not a valid discord msg id: %s", legacy_msg_id)
109
- return
110
- u = c.discord_user
111
- channel: di.DMChannel = u.dm_channel
112
- if channel is None:
113
- return
114
- m = await channel.fetch_message(legacy_msg_id)
115
- self.log.debug("Message %s should be marked as read", m)
116
- # try:
117
- # await m.ack() # triggers 404, maybe does not work for DM?
118
- # except Exception as e:
119
- # self.log.exception("Message %s should have been marked as read but this raised %s", m, e)
120
-
121
- async def correct(self, text: str, legacy_msg_id: Any, c: "Contact"):
122
- u = c.discord_user
123
- channel: di.DMChannel = u.dm_channel
124
- if channel is None:
125
- return
126
- m = await channel.fetch_message(legacy_msg_id)
127
- self.edit_futures[legacy_msg_id] = self.xmpp.loop.create_future()
128
- await m.edit(content=text)
129
- await self.edit_futures[legacy_msg_id]
130
-
131
- async def react(self, legacy_msg_id: int, emojis: list[str], c: "Contact"):
132
- u = c.discord_user
133
- channel: di.DMChannel = u.dm_channel
134
- if channel is None:
135
- return
136
- m = await channel.fetch_message(legacy_msg_id)
137
-
138
- legacy_reactions = set(self.get_my_legacy_reactions(m))
139
- xmpp_reactions = set(emojis)
140
-
141
- self.log.debug("%s vs %s", legacy_reactions, xmpp_reactions)
142
- for e in xmpp_reactions - legacy_reactions:
143
- await m.add_reaction(e)
144
- for e in legacy_reactions - xmpp_reactions:
145
- await m.remove_reaction(e, self.discord.user)
146
-
147
- async def retract(self, legacy_msg_id: Any, c: "Contact"):
148
- u = c.discord_user
149
- channel: di.DMChannel = u.dm_channel
150
- if channel is None:
151
- return
152
- m = await channel.fetch_message(legacy_msg_id)
153
- self.delete_futures[legacy_msg_id] = self.xmpp.loop.create_future()
154
- await m.delete()
155
- await self.delete_futures[legacy_msg_id]
156
-
157
- async def update_reactions(self, message: di.Message):
158
- (await self.contacts.by_discord_user(message.channel.recipient)).react(
159
- message.id, self.get_my_legacy_reactions(message), carbon=True
160
- )
161
-
162
- @staticmethod
163
- def get_my_legacy_reactions(message: di.Message) -> list[str]:
164
- reactions = []
165
- for r in message.reactions:
166
- if r.me and not r.custom_emoji:
167
- reactions.append(r.emoji)
168
-
169
- return reactions
170
-
171
- async def search(self, form_values: dict[str, str]):
172
- pass
slidge/plugins/dummy.py DELETED
@@ -1,334 +0,0 @@
1
- """
2
- A pseudo legacy network, to easily test things
3
- """
4
-
5
- import asyncio
6
- import logging
7
- import uuid
8
- from collections import defaultdict
9
- from datetime import datetime, timedelta
10
- from pathlib import Path
11
- from typing import Any, Optional, Union
12
-
13
- from slixmpp import JID
14
- from slixmpp.exceptions import XMPPError
15
-
16
- from slidge import *
17
- from slidge.core.adhoc import RegistrationType
18
-
19
- ASSETS_DIR = Path(__file__).parent.parent.parent / "assets"
20
-
21
-
22
- class Bookmarks(LegacyBookmarks):
23
- @staticmethod
24
- async def jid_local_part_to_legacy_id(local_part):
25
- if local_part not in {"prout-1", "prout2"}:
26
- raise XMPPError("not-found")
27
- return local_part
28
-
29
-
30
- class MUC(LegacyMUC["Session", str, "Participant", str]):
31
- REACTIONS_SINGLE_EMOJI = True
32
-
33
- session: "Session"
34
- msg_ids = defaultdict(int) # type: ignore
35
-
36
- async def join(self, p):
37
- self.user_nick = "SomeNick"
38
- await super().join(p)
39
-
40
- async def fill_history(
41
- self,
42
- full_jid: JID,
43
- maxchars: Optional[int] = None,
44
- maxstanzas: Optional[int] = None,
45
- seconds: Optional[int] = None,
46
- since: Optional[int] = None,
47
- ):
48
- if maxchars is not None and maxchars == 0:
49
- return
50
- part = await self.get_participant("someone")
51
- log.debug("PART")
52
- for i in range(10, 0, -1):
53
- log.debug("HISTORY")
54
- part.send_text(
55
- "history",
56
- f"-{i}",
57
- when=datetime.now() - timedelta(hours=i),
58
- full_jid=full_jid,
59
- )
60
-
61
- async def get_participants(self):
62
- if self.legacy_id == "prout-1":
63
- for nick in "anon1", "anon2":
64
- yield Participant(self, nick)
65
- break
66
- elif self.legacy_id == "prout2":
67
- for nick in "anon1", "anon2", "anon3", "anon4":
68
- yield Participant(self, nick)
69
-
70
- async def send_text(self, text: str) -> str:
71
- self.msg_ids[self.legacy_id] += 1
72
- i = self.msg_ids[self.legacy_id]
73
- self.xmpp.loop.create_task(self.session.muc_later(self, text, i))
74
- return str(self.msg_ids[self.legacy_id])
75
-
76
-
77
- class Participant(LegacyParticipant[MUC]):
78
- pass
79
-
80
-
81
- class Contact(LegacyContact):
82
- REACTIONS_SINGLE_EMOJI = True
83
-
84
- async def available_emojis(self, legacy_msg_id):
85
- return {"🦅", "🧺"}
86
-
87
-
88
- class Gateway(BaseGateway):
89
- COMPONENT_NAME = "The great legacy network (slidge)"
90
- COMPONENT_AVATAR = ASSETS_DIR / "gateway.png"
91
- COMPONENT_TYPE = "aim"
92
- GROUPS = True
93
- REGISTRATION_INSTRUCTIONS = (
94
- "Only username 'n' is accepted and only 'baba' and 'bibi' contacts exist.\n"
95
- "You can use any password you want."
96
- )
97
- REGISTRATION_TYPE = RegistrationType.QRCODE
98
- MARK_ALL_MESSAGES = True
99
-
100
- async def validate(
101
- self, user_jid: JID, registration_form: dict[str, Optional[str]]
102
- ):
103
- if registration_form["username"] != "n":
104
- raise XMPPError("bad-request", "Y a que N!")
105
-
106
- async def validate_two_factor_code(self, user, code):
107
- if code != "8":
108
- raise XMPPError("not-authorized", text="Wrong code! It's 8.")
109
-
110
- async def get_qr_text(self, user: GatewayUser) -> str:
111
- self.loop.create_task(self.later_confirm_qr(user))
112
- return "dummy:///SLIDGE-IS-GREAT-AGAIN/prout"
113
-
114
- async def later_confirm_qr(self, user: GatewayUser):
115
- await asyncio.sleep(1)
116
- exc = (
117
- XMPPError("bad-request", "Ben non")
118
- if user.registration_form["password"] == "n"
119
- else None
120
- )
121
- await self.confirm_qr(user.bare_jid, exc)
122
-
123
-
124
- class Roster(LegacyRoster):
125
- @staticmethod
126
- async def jid_username_to_legacy_id(jid_username: str):
127
- if jid_username not in BUDDIES + ["bubu"]:
128
- raise XMPPError("not-found")
129
- return jid_username
130
-
131
-
132
- class Session(
133
- BaseSession[
134
- Gateway, int, LegacyRoster, LegacyContact, LegacyBookmarks, MUC, Participant
135
- ]
136
- ):
137
- def __init__(self, user):
138
- super(Session, self).__init__(user)
139
- self.counter = 0
140
- self.xmpp.loop.create_task(self.backfill())
141
- self.xmpp.loop.create_task(
142
- self.contacts.by_legacy_id("bibi")
143
- ).add_done_callback(
144
- lambda c: c.result().set_vcard(
145
- given="FirstBi",
146
- surname="LastBi",
147
- phone="+555",
148
- full_name="Bi bi",
149
- note="A fake friend, always there for you",
150
- url="https://example.org",
151
- email="bibi@prout.com",
152
- country="Westeros",
153
- locality="The place with the thing",
154
- )
155
- )
156
- self.xmpp.loop.create_task(self.add_groups())
157
-
158
- async def add_groups(self):
159
- muc = await self.bookmarks.by_legacy_id("prout-1")
160
- muc.n_participants = 45
161
- await self.bookmarks.by_legacy_id("prout2")
162
- muc.n_participants = 885
163
-
164
- async def muc_later(self, muc: MUC, text: str, trigger_msg_id: int):
165
- replier = await muc.get_participant("anon1")
166
- log.debug("REPLIER: %s", replier)
167
- await asyncio.sleep(0.5)
168
- replier.composing()
169
- await asyncio.sleep(0.5)
170
- replier.send_text("prout", trigger_msg_id * 1000)
171
- # next(muc.participants).send_text("I agree. Ain't that great?")
172
-
173
- async def backfill(self):
174
- self.log.debug("CARBON")
175
- i = uuid.uuid1()
176
-
177
- baba = await self.contacts.by_legacy_id("baba")
178
-
179
- baba.send_text(
180
- f"You're bad!",
181
- legacy_msg_id=i,
182
- when=datetime.now() - timedelta(hours=5),
183
- carbon=True,
184
- )
185
- baba.send_text(
186
- f"You're worse",
187
- legacy_msg_id=i,
188
- when=datetime.now() - timedelta(hours=4),
189
- )
190
-
191
- async def paused(self, c: LegacyContact):
192
- pass
193
-
194
- async def correct(self, text: str, legacy_msg_id: Any, c: LegacyContact):
195
- pass
196
-
197
- async def login(self):
198
- log.debug("Logging in user: %s", self.user)
199
- self.send_gateway_status("Connecting...", show="dnd")
200
- await asyncio.sleep(1)
201
- self.send_gateway_status("Connected")
202
- for b, a in zip(BUDDIES, AVATARS):
203
- c = await self.contacts.by_legacy_id(b.lower())
204
- c.name = b.title()
205
- c.avatar = a
206
- await c.add_to_roster()
207
- c.online("I am not a real person, so what?")
208
- return "You can talk to your fake friends now"
209
-
210
- async def logout(self):
211
- log.debug("User has disconnected")
212
-
213
- async def send_text(
214
- self,
215
- text: str,
216
- chat: Union[LegacyContact, MUC],
217
- *,
218
- reply_to_msg_id=None,
219
- reply_to_fallback_text=None,
220
- reply_to=None,
221
- ):
222
- if isinstance(chat, MUC):
223
- await chat.send_text(text)
224
- return
225
-
226
- log.debug("REPLY FALLBACK: %r", reply_to_fallback_text)
227
- i = self.counter
228
- self.counter = i + 1
229
-
230
- if text == "crash":
231
- raise RuntimeError("PANIC!!!")
232
- if text == "crash2":
233
- self.xmpp.loop.create_task(self.crash())
234
- elif text == "delete":
235
- self.xmpp.loop.create_task(self.later_carbon_delete(chat, i))
236
- elif text == "nick":
237
- chat.name = "NEWNAME"
238
- elif text == "avatar":
239
- chat.avatar = ASSETS_DIR / "5x5.png"
240
- elif text == "nonick":
241
- chat.name = None
242
- else:
243
- self.xmpp.loop.create_task(self.later(chat, i, body=text))
244
-
245
- return i
246
-
247
- async def crash(self):
248
- raise RuntimeError("PANIC222!!!")
249
-
250
- async def send_file(self, url: str, chat: Union[LegacyContact, MUC], **k) -> int:
251
- i = self.counter
252
- self.counter = i + 1
253
- if isinstance(chat, MUC):
254
- replier = await chat.get_participant("uploader")
255
- else:
256
- replier = chat # type: ignore
257
- replier.send_text(url)
258
- await replier.send_file(ASSETS_DIR / "buddy1.png", caption="This is a caption")
259
- return i
260
-
261
- async def later(self, c: LegacyContact, trigger_msg_id: int, body: str):
262
- i = self.counter - 1
263
- await asyncio.sleep(1)
264
- c.received(i)
265
- await asyncio.sleep(1)
266
- c.active()
267
- await asyncio.sleep(1)
268
- c.displayed(i)
269
- await asyncio.sleep(1)
270
- c.ack(i)
271
- await asyncio.sleep(1)
272
- c.composing()
273
- await asyncio.sleep(1)
274
- c.paused()
275
- await asyncio.sleep(1)
276
- c.composing()
277
- await asyncio.sleep(1)
278
- c.send_text(
279
- "OK",
280
- legacy_msg_id=i,
281
- reply_to_msg_id=trigger_msg_id,
282
- reply_to_fallback_text=body,
283
- )
284
- await asyncio.sleep(1)
285
- i = uuid.uuid1().int
286
- c.send_text("I will retract this", legacy_msg_id=i)
287
- c.retract(i)
288
- c.inactive()
289
-
290
- async def later_carbon_delete(self, c: LegacyContact, trigger_msg_id: int):
291
- await asyncio.sleep(1)
292
- c.retract(trigger_msg_id, carbon=True)
293
-
294
- async def active(self, c: LegacyContact):
295
- log.debug("User is active for contact %s", c)
296
-
297
- async def inactive(self, c: LegacyContact):
298
- log.debug("User is inactive for contact %s", c)
299
-
300
- async def composing(self, c: LegacyContact):
301
- log.debug("User is composing for contact %s", c)
302
-
303
- async def displayed(self, legacy_msg_id: int, c: LegacyContact):
304
- log.debug("Message #%s was read by the user", legacy_msg_id)
305
-
306
- async def search(self, form_values: dict[str, str]):
307
- if form_values["first"] == "bubu":
308
- return SearchResult(
309
- fields=[
310
- FormField("name", label="Name"),
311
- FormField("jid", type="jid-single"),
312
- ],
313
- items=[{"name": "bubu", "jid": f"bubu@{self.xmpp.boundjid.bare}"}],
314
- )
315
-
316
- async def react(self, legacy_msg_id, emojis, c):
317
- if "😈" in emojis:
318
- c.send_text("That's forbidden")
319
- c.react(legacy_msg_id, "", carbon=True)
320
- raise XMPPError("not-acceptable")
321
- else:
322
- c.react(legacy_msg_id, "♥")
323
-
324
- async def retract(self, legacy_msg_id, c):
325
- log.debug("User has retracted their msg: '%s' (sent to '%s')", legacy_msg_id, c)
326
-
327
-
328
- BUDDIES = ["baba", "bibi"]
329
- AVATARS = ["https://wallpapercave.com/wp/PSksftM.jpg"]
330
-
331
- with (ASSETS_DIR / "buddy2.png").open("rb") as fp:
332
- AVATARS.append(fp.read()) # type:ignore
333
-
334
- log = logging.getLogger(__name__)