tangled
alpha
login
or
join now
zenfyr.dev
/
xmpp-discord-bridge
0
fork
atom
fork of https://f-hub.org/XMPP/xmpp-discord-bridge
0
fork
atom
overview
issues
pulls
pipelines
misc: Fix general things
Alexander PapaTutuWawa
4 years ago
27d1529e
6afffe04
+48
-23
4 changed files
expand all
collapse all
unified
split
setup.py
xmpp_discord_bridge
avatar.py
helpers.py
main.py
+1
-1
setup.py
···
22
22
zip_safe = True,
23
23
entry_points = {
24
24
"console_scripts": [
25
25
-
"discord-xmpp-bridge = discord_xmpp_bridge.main:main"
25
25
+
"xmpp-discord-bridge = xmpp_discord_bridge.main:main"
26
26
]
27
27
}
28
28
)
+19
-4
xmpp_discord_bridge/avatar.py
···
1
1
import logging
2
2
+
import hashlib
3
3
+
import os
2
4
3
5
from slixmpp.exceptions import IqError
4
6
5
7
class AvatarManager:
8
8
+
"""
9
9
+
The AvatarManager class is responsible for aquiring the XMPP avatars
10
10
+
using the means of either XEP-0153 (XEP-0054), XEP-0084 or (in the future)
11
11
+
XEP-0292, storing and providing a publicly available URL to those avatars.
12
12
+
"""
6
13
def __init__(self, xmpp, config):
7
14
self._xmpp = xmpp
8
15
self._path = config["avatars"]["path"]
···
12
19
13
20
self._logger = logging.getLogger("xmpp.avatar")
14
21
15
15
-
def save_avatar(self, jid, data, type_):
22
22
+
def _save_avatar(self, jid, data, type_):
16
23
filename = hashlib.sha1(data).hexdigest() + "." + type_.split("/")[1]
17
24
path = os.path.join(self._path, filename)
18
25
···
31
38
ifrom=self._xmpp._bot_jid_full)
32
39
type_ = iq["vcard_temp"]["PHOTO"]["TYPE"]
33
40
data = iq["vcard_temp"]["PHOTO"]["BINVAL"]
34
34
-
self.save_avatar(jid, data, type_)
41
41
+
self._save_avatar(jid, data, type_)
35
42
return True
36
43
except IqError:
37
44
self._logger.debug("Avatar retrieval via XEP-0054/XEP-0153 failed. Probably no vCard for XEP-0054 published")
···
43
50
node="urn:xmpp:avatar:data",
44
51
max_items=1,
45
52
ifrom=self._xmpp._bot_jid_full)
53
53
+
data = iq["pubsub"]["items"]["substanzas"][0]["data"]
54
54
+
self._save_avatar(jid, data, "image/png")
55
55
+
return True
46
56
except IqError:
47
57
self._logger.debug("Avatar retrieval via XEP-0084 failed. Probably no avatar published or subscription model not fulfilled.")
48
58
return False
49
59
50
60
async def aquire_avatar(self, jid):
51
51
-
# First try vCard via 0054/0153
61
61
+
"""
62
62
+
Attempt to request the avatar of @jid
63
63
+
"""
52
64
for f in [self.try_xep_0153, self.try_xep_0084]:
53
65
if await f(jid):
54
66
self._logger.debug("Avatar retrieval successful for %s",
···
58
70
self._logger.debug("Avatar retrieval failed for %s. Giving up.",
59
71
jid)
60
72
61
61
-
def get_avatar(self, jid):
73
73
+
def get_avatar_url(self, jid):
74
74
+
"""
75
75
+
Returns either the URL to the avatar of @jid or None.
76
76
+
"""
62
77
return self._avatars.get(jid, None)
+8
-6
xmpp_discord_bridge/helpers.py
···
1
1
+
from discord import Status
2
2
+
1
3
def discord_status_to_xmpp_show(status):
2
4
return {
3
3
-
discord.Status.online: "available",
4
4
-
discord.Status.idle: "away",
5
5
-
discord.Status.dnd: "dnd",
6
6
-
discord.Status.do_not_disturb: "dnd",
7
7
-
discord.Status.invisible: "xa", # TODO: Kinda
8
8
-
discord.Status.offline: "unavailable"
5
5
+
Status.online: "available",
6
6
+
Status.idle: "away",
7
7
+
Status.dnd: "dnd",
8
8
+
Status.do_not_disturb: "dnd",
9
9
+
Status.invisible: "xa", # TODO: Kinda
10
10
+
Status.offline: "unavailable"
9
11
}.get(status, "available")
+20
-12
xmpp_discord_bridge/main.py
···
16
16
from slixmpp.exceptions import XMPPError, IqError
17
17
from slixmpp.xmlstream import register_stanza_plugin
18
18
from slixmpp.jid import JID
19
19
+
from discord import Status
19
20
import requests
20
21
21
22
from xmpp_discord_bridge.slixmpp.oob import OOBData
···
124
125
self._virtual_muc_users[muc] = []
125
126
self._virtual_muc_nicks[muc] = {}
126
127
for member in dchannel.members:
127
127
-
if member.status == discord.Status.offline:
128
128
+
if member.status == Status.offline and not self._dont_ignore_offline:
128
129
continue
129
130
130
131
self._virtual_muc_users[muc].append(member.display_name)
···
208
209
"username": message["from"].resource
209
210
}
210
211
211
211
-
if self._relay_xmpp_avatars and self._avatars.get_avatar(message["from"]):
212
212
-
webhook["avatar_url"] = self._avatar.get_avatar(message["from"])
212
212
+
if self._relay_xmpp_avatars and self._avatars.get_avatar_url(message["from"]):
213
213
+
webhook["avatar_url"] = self._avatar.get_avatar_url(message["from"])
213
214
214
215
# Look for mentions and replace them
215
216
guild, channel = self._muc_map[muc]
···
230
231
requests.post(self._webhooks[muc],
231
232
data=webhook)
232
233
233
233
-
def virtual_user_update_presence(self, muc, uid, pshow):
234
234
+
def virtual_user_update_presence(self, muc, uid, pshow, pstatus=None):
234
235
"""
235
236
Change the status of a virtual MUC member.
236
237
NOTE: This assumes that the user is in the MUC
237
238
"""
238
239
self.send_presence(pshow=pshow,
240
240
+
pstatus=pstatus,
239
241
pto="%s/%s" % (muc, self._virtual_muc_nicks[muc][uid]),
240
242
pfrom=self.spoof_member_jid(uid))
241
243
···
246
248
247
249
If update_state_tracking is True, then _virtual_muc_... gets updates.
248
250
"""
249
249
-
if member.status == discord.Status.offline and not self._dont_ignore_offline:
251
251
+
if member.status == Status.offline and not self._dont_ignore_offline:
250
252
return
251
253
252
254
if update_state_tracking:
···
256
258
# Prevent offline users from getting an unavailable presence by
257
259
# accident
258
260
pshow = discord_status_to_xmpp_show(member.status)
259
259
-
if member.status == discord.Status.offline:
261
261
+
pstatus = ""
262
262
+
if member.status == Status.offline:
260
263
pshow = "xa"
261
264
265
265
+
if self._dont_ignore_offline:
266
266
+
pstatus = "Offline"
267
267
+
262
268
self.plugin["xep_0045"].join_muc(muc,
263
269
nick=member.display_name,
264
270
pfrom=self.spoof_member_jid(member.id),
265
265
-
pshow=pshow)
271
271
+
pshow=pshow,
272
272
+
pstatus=pstatus)
266
273
267
274
async def on_discord_member_join(self, member):
268
275
guild = member.guild.id
···
300
307
# Handle a status change
301
308
for channel in self._guild_map[guild]:
302
309
muc = self._guild_map[guild][channel]
303
303
-
if after.status == discord.Status.offline:
310
310
+
if after.status == Status.offline:
304
311
if self._dont_ignore_offline:
305
312
self.virtual_user_update_presence(muc,
306
313
after.id,
307
307
-
"xa")
314
314
+
"xa",
315
315
+
"Offline")
308
316
else:
309
317
self._logger.debug("%s went offline. Removing from state tracking.",
310
318
after.display_name)
···
315
323
# Remove from all state tracking
316
324
self._virtual_muc_users[muc].remove(after.display_name)
317
325
del self._virtual_muc_nicks[muc][after.id]
318
318
-
elif before.status == discord.Status.offline and after.status != discord.Status.offline:
326
326
+
elif before.status == Status.offline and after.status != Status.offline and not self._dont_ignore_offline:
319
327
self.virtual_user_join_muc(muc, after, update_state_tracking=True)
320
328
else:
321
329
self.virtual_user_update_presence(muc,
···
425
433
self._real_muc_users[muc].append(resource)
426
434
427
435
async def on_session_start(self, event):
428
428
-
self._discord = DiscordClient(self, config)
436
436
+
self._discord = DiscordClient(self, self._config)
429
437
asyncio.ensure_future(self._discord.start(self._token))
430
438
431
439
def main():
···
441
449
"-d", "--debug", dest="debug", help="Enable debug logging", action="store_true"
442
450
)
443
451
444
444
-
(option, args) = parser.parse_args()
452
452
+
(options, args) = parser.parse_args()
445
453
verbosity = logging.DEBUG if options.debug else logging.INFO
446
454
447
455
general = config["general"]