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
@@ -1,90 +0,0 @@
1
- import functools
2
- import logging
3
- from argparse import ArgumentParser
4
-
5
- import discord as di
6
- from slixmpp.exceptions import XMPPError
7
-
8
- from slidge import *
9
-
10
- from .session import Session
11
-
12
-
13
- class Gateway(BaseGateway[Session]):
14
- COMPONENT_NAME = "Discord (slidge)"
15
- COMPONENT_TYPE = "discord"
16
- REGISTRATION_INSTRUCTIONS = (
17
- "Have a look at https://discordpy-self.readthedocs.io/en/latest/token.html"
18
- )
19
- REGISTRATION_FIELDS = [FormField("token", required=True)]
20
-
21
- ROSTER_GROUP = "Discord"
22
-
23
- def config(self, argv: list[str]):
24
- parser = ArgumentParser()
25
- parser.add_argument("--discord-verbose", action="store_true")
26
- args = parser.parse_args(argv)
27
- if not args.discord_verbose:
28
- log.debug("Disabling discord info logs")
29
- logging.getLogger("discord.gateway").setLevel(logging.WARNING)
30
- logging.getLogger("discord.client").setLevel(logging.WARNING)
31
-
32
-
33
- class Contact(LegacyContact[Session]):
34
- MARKS = False
35
-
36
- @functools.cached_property
37
- def discord_user(self) -> di.User:
38
- logging.debug("Searching for user: %s", self.legacy_id)
39
- if (u := self.session.discord.get_user(self.legacy_id)) is None:
40
- raise XMPPError(
41
- "not-found", text=f"Cannot find the discord user {self.legacy_id}"
42
- )
43
- return u
44
-
45
- @functools.cached_property
46
- def direct_channel_id(self):
47
- return self.discord_user.dm_channel.id
48
-
49
- async def update_reactions(self, m: di.Message):
50
- legacy_reactions = []
51
- user = self.discord_user
52
- for r in m.reactions:
53
- async for u in r.users():
54
- if u == user:
55
- legacy_reactions.append(r.emoji)
56
- self.react(m.id, legacy_reactions)
57
-
58
- async def update_info(self):
59
- u = self.discord_user
60
- self.name = name = u.display_name
61
- self.avatar = str(u.avatar_url)
62
-
63
- try:
64
- profile = await u.profile()
65
- except di.Forbidden:
66
- log.debug("Forbidden to fetch the profile of %s", u)
67
- except di.HTTPException as e:
68
- log.debug("HTTP exception %s when fetch the profile of %s", e, u)
69
- else:
70
- self.set_vcard(full_name=name, note=profile.bio)
71
-
72
- # TODO: use the relationship here
73
- # relationship = u.relationship
74
-
75
-
76
- class Roster(LegacyRoster[Contact, "Session"]):
77
- def by_discord_user(self, u: di.User):
78
- return self.by_legacy_id(u.id)
79
-
80
- @staticmethod
81
- def jid_username_to_legacy_id(discord_id: str):
82
- try:
83
- return int(discord_id)
84
- except ValueError:
85
- raise XMPPError(
86
- "not-found", text=f"Not a valid discord user ID: {discord_id}"
87
- )
88
-
89
-
90
- log = logging.getLogger(__name__)
@@ -1,108 +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
- self.session.contacts.by_discord_user(channel.recipient).carbon(
31
- message.content
32
- )
33
- else:
34
- fut.set_result(True)
35
- else:
36
- contact = self.session.contacts.by_discord_user(author)
37
- reply_to = message.reference.message_id if message.reference else None
38
- if content := message.content:
39
- contact.send_text(
40
- content,
41
- legacy_msg_id=message.id,
42
- reply_to_msg_id=reply_to,
43
- )
44
- for attachment in message.attachments:
45
- await contact.send_file(
46
- url=attachment.url,
47
- filename=attachment.filename,
48
- content_type=attachment.content_type,
49
- reply_to_msg_id=reply_to,
50
- )
51
-
52
- async def on_typing(self, channel, user, _when):
53
- if user != self.user and isinstance(channel, di.DMChannel):
54
- self.session.contacts.by_discord_user(user).composing()
55
-
56
- async def on_message_edit(self, before: di.Message, after: di.Message):
57
- if not isinstance(after.channel, di.DMChannel):
58
- return
59
- if before.content == after.content:
60
- return
61
- if (author := after.author) == self.user:
62
- fut = self.session.edit_futures.get(after.id)
63
- if fut is None:
64
- self.session.contacts.by_discord_user(
65
- after.channel.recipient
66
- ).carbon_correct(after.id, after.content)
67
- else:
68
- fut.set_result(True)
69
- else:
70
- self.session.contacts.by_discord_user(author).correct(
71
- after.id, after.content
72
- )
73
-
74
- async def on_message_delete(self, m: di.Message):
75
- if not isinstance(m.channel, di.DMChannel):
76
- return
77
- if (author := m.author) == self.user:
78
- fut = self.session.delete_futures.get(m.id)
79
- if fut is None:
80
- self.session.contacts.by_discord_user(
81
- m.channel.recipient
82
- ).carbon_retract(m.id)
83
- else:
84
- fut.set_result(True)
85
- else:
86
- self.session.contacts.by_discord_user(author).retract(m.id)
87
-
88
- async def on_reaction_add(
89
- self, reaction: di.Reaction, user: Union[di.User, di.ClientUser]
90
- ):
91
- await self.update_reactions(reaction, user)
92
-
93
- async def on_reaction_remove(
94
- self, reaction: di.Reaction, user: Union[di.User, di.ClientUser]
95
- ):
96
- await self.update_reactions(reaction, user)
97
-
98
- async def update_reactions(
99
- self, reaction: di.Reaction, user: Union[di.User, di.ClientUser]
100
- ):
101
- message: di.Message = reaction.message
102
- if not isinstance(message.channel, di.DMChannel):
103
- return
104
-
105
- if user == self.user:
106
- self.session.update_reactions(message)
107
- else:
108
- await self.session.contacts.by_discord_user(user).update_reactions(message)
@@ -1,162 +0,0 @@
1
- import asyncio
2
- from typing import TYPE_CHECKING, Any, Union
3
-
4
- import discord as di
5
-
6
- from slidge import *
7
-
8
- if TYPE_CHECKING:
9
- from . import Contact, Gateway, Roster
10
- from .client import Discord
11
-
12
-
13
- class Session(BaseSession["Contact", "Roster", "Gateway"]):
14
- discord: "Discord"
15
- ready_future: asyncio.Future[bool]
16
- delete_futures: dict[int, asyncio.Future[bool]]
17
- edit_futures: dict[int, asyncio.Future[bool]]
18
- send_futures: dict[int, asyncio.Future[bool]]
19
- send_lock: asyncio.Lock
20
-
21
- def post_init(self):
22
- from .client import Discord
23
-
24
- self.discord = Discord(self)
25
- self.ready_future = self.xmpp.loop.create_future()
26
- self.delete_futures = {}
27
- self.edit_futures = {}
28
- self.send_futures = {}
29
- self.send_lock = asyncio.Lock()
30
-
31
- @staticmethod
32
- def xmpp_msg_id_to_legacy_msg_id(i: str) -> Union[int, str]:
33
- try:
34
- return int(i)
35
- except ValueError:
36
- return i
37
-
38
- async def login(self):
39
- self.xmpp.loop.create_task(
40
- self.discord.start(self.user.registration_form["token"])
41
- )
42
- await self.ready_future
43
- for u in self.discord.users:
44
- if not isinstance(u, di.User):
45
- self.log.debug(f"Skipping %s", u)
46
- continue
47
- if not u.is_friend():
48
- self.log.debug(f"%s is not a friend", u)
49
- continue
50
- c = self.contacts.by_legacy_id(u.id)
51
- await c.update_info()
52
- await c.add_to_roster()
53
- # TODO: contribute to discord.py-self so that the presence information
54
- # of relationships is parsed. logs show:
55
- # 'PRESENCE_UPDATE referencing an unknown guild ID: %s. Discarding.'
56
- # https://github.com/dolfies/discord.py-self/blob/master/discord/state.py#L1044
57
- c.online()
58
- return f"Logged on as {self.discord.user}"
59
-
60
- async def send_text(self, t: str, c: "Contact", *, reply_to_msg_id=None):
61
- async with self.send_lock:
62
- mid = (
63
- await c.discord_user.send(
64
- t,
65
- reference=None
66
- if reply_to_msg_id is None
67
- else di.MessageReference(
68
- message_id=reply_to_msg_id,
69
- channel_id=c.direct_channel_id,
70
- ),
71
- )
72
- ).id
73
- f = self.send_futures[mid] = self.xmpp.loop.create_future()
74
- await f
75
- return mid
76
-
77
- async def logout(self):
78
- await self.discord.close()
79
-
80
- async def send_file(self, u: str, c: "Contact", *, reply_to_msg_id=None):
81
- # discord clients inline previews of external URLs, so no need to actually send on discord servers
82
- await c.discord_user.send(u)
83
-
84
- async def active(self, c: "Contact"):
85
- pass
86
-
87
- async def inactive(self, c: "Contact"):
88
- pass
89
-
90
- async def composing(self, c: "Contact"):
91
- await c.discord_user.trigger_typing()
92
-
93
- async def paused(self, c: "Contact"):
94
- pass
95
-
96
- async def displayed(self, legacy_msg_id: str, c: "Contact"):
97
- if not isinstance(legacy_msg_id, int):
98
- self.log.debug("This is not a valid discord msg id: %s", legacy_msg_id)
99
- return
100
- u = c.discord_user
101
- channel: di.DMChannel = u.dm_channel
102
- if channel is None:
103
- return
104
- m = await channel.fetch_message(legacy_msg_id)
105
- self.log.debug("Message %s should be marked as read", m)
106
- # try:
107
- # await m.ack() # triggers 404, maybe does not work for DM?
108
- # except Exception as e:
109
- # self.log.exception("Message %s should have been marked as read but this raised %s", m, e)
110
-
111
- async def correct(self, text: str, legacy_msg_id: Any, c: "Contact"):
112
- u = c.discord_user
113
- channel: di.DMChannel = u.dm_channel
114
- if channel is None:
115
- return
116
- m = await channel.fetch_message(legacy_msg_id)
117
- self.edit_futures[legacy_msg_id] = self.xmpp.loop.create_future()
118
- await m.edit(content=text)
119
- await self.edit_futures[legacy_msg_id]
120
-
121
- async def react(self, legacy_msg_id: int, emojis: list[str], 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
-
128
- legacy_reactions = set(self.get_my_legacy_reactions(m))
129
- xmpp_reactions = set(emojis)
130
-
131
- self.log.debug("%s vs %s", legacy_reactions, xmpp_reactions)
132
- for e in xmpp_reactions - legacy_reactions:
133
- await m.add_reaction(e)
134
- for e in legacy_reactions - xmpp_reactions:
135
- await m.remove_reaction(e, self.discord.user)
136
-
137
- async def retract(self, legacy_msg_id: Any, c: "Contact"):
138
- u = c.discord_user
139
- channel: di.DMChannel = u.dm_channel
140
- if channel is None:
141
- return
142
- m = await channel.fetch_message(legacy_msg_id)
143
- self.delete_futures[legacy_msg_id] = self.xmpp.loop.create_future()
144
- await m.delete()
145
- await self.delete_futures[legacy_msg_id]
146
-
147
- def update_reactions(self, message: di.Message):
148
- self.contacts.by_discord_user(message.channel.recipient).carbon_react(
149
- message.id, self.get_my_legacy_reactions(message)
150
- )
151
-
152
- @staticmethod
153
- def get_my_legacy_reactions(message: di.Message) -> list[str]:
154
- reactions = []
155
- for r in message.reactions:
156
- if r.me and not r.custom_emoji:
157
- reactions.append(r.emoji)
158
-
159
- return reactions
160
-
161
- async def search(self, form_values: dict[str, str]) -> SearchResult:
162
- pass
slidge/plugins/dummy.py DELETED
@@ -1,203 +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 pathlib import Path
9
- from typing import Any, Optional
10
-
11
- from slixmpp import JID
12
- from slixmpp.exceptions import XMPPError
13
-
14
- from slidge import *
15
-
16
- ASSETS_DIR = Path(__file__).parent.parent.parent / "assets"
17
-
18
-
19
- class Gateway(BaseGateway):
20
- COMPONENT_NAME = "The great legacy network (slidge)"
21
- COMPONENT_AVATAR = ASSETS_DIR / "gateway.png"
22
- COMPONENT_TYPE = "aim"
23
- REGISTRATION_INSTRUCTIONS = (
24
- "Only username 'n' is accepted and only 'baba' and 'bibi' contacts exist.\n"
25
- "You can use any password you want."
26
- )
27
- REGISTRATION_FIELDS = list(BaseGateway.REGISTRATION_FIELDS) + [
28
- FormField(
29
- var="something_else",
30
- label="Some optional stuff not covered by jabber:iq:register",
31
- required=False,
32
- private=False,
33
- ),
34
- FormField(
35
- var="device",
36
- type="list-single",
37
- label="What do you want to do?",
38
- options=[
39
- {"label": "Choice #1", "value": "choice1"},
40
- {"label": "Choice #2", "value": "choice2"},
41
- ],
42
- required=True,
43
- ),
44
- ]
45
-
46
- async def validate(
47
- self, user_jid: JID, registration_form: dict[str, Optional[str]]
48
- ):
49
- if registration_form["username"] != "n":
50
- raise ValueError("Y a que N!")
51
-
52
-
53
- class Session(BaseSession[LegacyContact, LegacyRoster, Gateway]):
54
- def __init__(self, user):
55
- super(Session, self).__init__(user)
56
- self.counter = 0
57
- self.xmpp.loop.create_task(self.backfill())
58
- self.contacts.by_legacy_id("bibi").set_vcard(
59
- given="FirstBi",
60
- surname="LastBi",
61
- phone="+555",
62
- full_name="Bi bi",
63
- note="A fake friend, always there for you",
64
- url="https://example.org",
65
- email="bibi@prout.com",
66
- country="Westeros",
67
- locality="The place with the thing",
68
- )
69
-
70
- async def backfill(self):
71
- self.log.debug("CARBON")
72
- i = uuid.uuid1()
73
-
74
- self.contacts.by_legacy_id("bibi").carbon(
75
- f"Sent by the component on behalf of the user, and this should reach MAM. Msg ID: {i}",
76
- legacy_id=i,
77
- )
78
-
79
- async def paused(self, c: LegacyContact):
80
- pass
81
-
82
- async def correct(self, text: str, legacy_msg_id: Any, c: LegacyContact):
83
- pass
84
-
85
- async def login(self):
86
- log.debug("Logging in user: %s", self.user)
87
- self.send_gateway_status("Connecting...", show="dnd")
88
- await asyncio.sleep(1)
89
- self.send_gateway_status("Connected")
90
- for b, a in zip(BUDDIES, AVATARS):
91
- c = self.contacts.by_legacy_id(b.lower())
92
- c.name = b.title()
93
- c.avatar = a
94
- await c.add_to_roster()
95
- c.online("I am not a real person, so what?")
96
- return "You can talk to your fake friends now"
97
-
98
- async def logout(self):
99
- log.debug("User has disconnected")
100
-
101
- async def send_text(self, t: str, c: LegacyContact, *, reply_to_msg_id=None):
102
- i = self.counter
103
- self.counter = i + 1
104
-
105
- if t == "crash":
106
- raise RuntimeError("PANIC!!!")
107
- if t == "crash2":
108
- self.xmpp.loop.create_task(self.crash())
109
- elif t == "delete":
110
- self.xmpp.loop.create_task(self.later_carbon_delete(c, i))
111
- elif t == "nick":
112
- c.name = "NEWNAME"
113
- elif t == "avatar":
114
- c.avatar = ASSETS_DIR / "5x5.png"
115
- elif t == "nonick":
116
- c.name = None
117
- else:
118
- self.xmpp.loop.create_task(self.later(c, i))
119
-
120
- return i
121
-
122
- async def crash(self):
123
- raise RuntimeError("PANIC222!!!")
124
-
125
- async def send_file(self, u: str, c: LegacyContact, *, reply_to_msg_id=None) -> int:
126
- i = self.counter
127
- self.counter = i + 1
128
- c.send_text(u)
129
- await c.send_file(ASSETS_DIR / "buddy1.png")
130
- return i
131
-
132
- async def later(self, c: LegacyContact, trigger_msg_id: int):
133
- i = self.counter - 1
134
- await asyncio.sleep(1)
135
- c.received(i)
136
- await asyncio.sleep(1)
137
- c.active()
138
- await asyncio.sleep(1)
139
- c.displayed(i)
140
- await asyncio.sleep(1)
141
- c.ack(i)
142
- await asyncio.sleep(1)
143
- c.composing()
144
- await asyncio.sleep(1)
145
- c.paused()
146
- await asyncio.sleep(1)
147
- c.composing()
148
- await asyncio.sleep(1)
149
- c.send_text("OK", legacy_msg_id=i, reply_to_msg_id=trigger_msg_id)
150
- await asyncio.sleep(1)
151
- i = uuid.uuid1().int
152
- c.send_text("I will retract this", legacy_msg_id=i)
153
- c.retract(i)
154
- c.inactive()
155
-
156
- async def later_carbon_delete(self, c: LegacyContact, trigger_msg_id: int):
157
- await asyncio.sleep(1)
158
- c.carbon_retract(trigger_msg_id)
159
-
160
- async def active(self, c: LegacyContact):
161
- log.debug("User is active for contact %s", c)
162
-
163
- async def inactive(self, c: LegacyContact):
164
- log.debug("User is inactive for contact %s", c)
165
-
166
- async def composing(self, c: LegacyContact):
167
- log.debug("User is composing for contact %s", c)
168
-
169
- async def displayed(self, legacy_msg_id: int, c: LegacyContact):
170
- log.debug("Message #%s was read by the user", legacy_msg_id)
171
-
172
- async def search(self, form_values: dict[str, str]):
173
- if form_values["first"] == "bubu":
174
- return SearchResult(
175
- fields=[
176
- FormField("name", label="Name"),
177
- FormField("jid", type="jid-single"),
178
- ],
179
- items=[{"name": "bubu", "jid": f"bubu@{self.xmpp.boundjid.bare}"}],
180
- )
181
-
182
- async def react(self, legacy_msg_id, emojis, c):
183
- if "😈" in emojis:
184
- c.send_text("That's forbidden")
185
- c.carbon_react(legacy_msg_id, "")
186
- raise XMPPError("not-acceptable")
187
- else:
188
- c.react(legacy_msg_id, "♥")
189
-
190
- async def retract(self, legacy_msg_id, c):
191
- log.debug("User has retracted their msg: '%s' (sent to '%s')", legacy_msg_id, c)
192
-
193
-
194
- BUDDIES = ["baba", "bibi"]
195
- AVATARS = []
196
-
197
- with (ASSETS_DIR / "buddy1.png").open("rb") as fp:
198
- AVATARS.append(fp.read())
199
-
200
- with (ASSETS_DIR / "buddy2.png").open("rb") as fp:
201
- AVATARS.append(fp.read())
202
-
203
- log = logging.getLogger(__name__)