Bridge discord emotes to matrix (#2)

This commit is contained in:
git-bruh 2021-01-10 23:10:11 +05:30 committed by GitHub
parent 5096505308
commit 7a31decfe4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 16 deletions

View file

@ -26,3 +26,4 @@ NOTE: [Privileged Intents](https://discordpy.readthedocs.io/en/latest/intents.ht
- [x] Editing messages
- [x] Replies
- [x] Bridging multiple channels/rooms
- [x] Discord emotes bridged as inline images (Works on Element Web, Fluffychat)

104
main.py
View file

@ -1,10 +1,14 @@
import discord.ext.commands
import aiofiles
import aiofiles.os
import aiohttp
import discord
import discord.ext.commands
import json
import logging
import nio
import os
import re
import uuid
def config_gen(config_file):
@ -14,7 +18,7 @@ def config_gen(config_file):
"password": "my-secret-password",
"token": "my-secret-token",
"discord_prefix": "my-command-prefix",
"bridge": {"channel_id": "room_id", }
"bridge": {"channel_id": "room_id"}
}
if not os.path.exists(config_file):
@ -35,9 +39,13 @@ message_store, channel_store = {}, {}
class MatrixClient(nio.AsyncClient):
async def start(self, discord_client):
self.logger = logging.getLogger("matrix_logger")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger = logging.getLogger("matrix_logger")
self.uploaded_emotes = {}
async def start(self, discord_client):
password = config["password"]
timeout = 30000
@ -70,15 +78,71 @@ class MatrixClient(nio.AsyncClient):
await self.close()
async def message_send(self, message, channel_id,
async def process_emotes(self, message, emotes):
formatted_body = message
async def upload_emote(emote_name, emote_id):
if emote_id in self.uploaded_emotes.keys():
return self.uploaded_emotes[emote_id]
emote_url = f"https://cdn.discordapp.com/emojis/{emote_id}"
emote_file = f"/tmp/{str(uuid.uuid4())}"
try:
async with aiohttp.ClientSession() as session:
async with session.get(emote_url) as resp:
emote = await resp.read()
content_type = resp.content_type
except Exception as e:
self.logger.warning(
f"Failed to download emote {emote}: {e}"
)
return
async with aiofiles.open(emote_file, "wb") as f:
await f.write(emote)
try:
async with aiofiles.open(emote_file, "rb") as f:
resp, maybe_keys = await self.upload(
f, content_type=content_type
)
except Exception as e:
self.logger.warning(
f"Failed to upload emote {emote}: {e}"
)
return
await aiofiles.os.remove(emote_file)
self.uploaded_emotes[emote_id] = resp.content_uri
return resp.content_uri
for emote in emotes.keys():
emote_ = await upload_emote(emote, emotes[emote])
if emote_:
emote = f":{emote}:"
formatted_body = formatted_body.replace(
emote, f"""<img alt=\"{emote}\" title=\"{emote}\"
height=\"32\" src=\"{emote_}\" data-mx-emoticon />"""
)
return formatted_body
async def message_send(self, message, channel_id, emotes,
reply_id=None, edit_id=None):
room_id = config["bridge"][str(channel_id)]
content = {
"msgtype": "m.text",
"body": message,
"format": "org.matrix.custom.html",
"msgtype": "m.text"
}
content["body"], content["formatted_body"] = message, await \
self.process_emotes(message, emotes)
if reply_id:
reply_event = await self.room_get_event(
room_id, reply_id
@ -91,16 +155,18 @@ class MatrixClient(nio.AsyncClient):
content["format"] = "org.matrix.custom.html"
content["formatted_body"] = f"""<mx-reply><blockquote>
<a href="https://matrix.to/#/{room_id}/{reply_id}">In reply to</a>
<a href="https://matrix.to/#/{reply_event.sender}">{reply_event.sender}</a><br>
{reply_event.body}</blockquote></mx-reply>{message}"""
content["formatted_body"] = f"""<mx-reply><blockquote>\
<a href="https://matrix.to/#/{room_id}/{reply_id}">In reply to</a>\
<a href="https://matrix.to/#/{reply_event.sender}">{reply_event.sender}</a>\
<br>{reply_event.body}</blockquote></mx-reply>{content["formatted_body"]}"""
if edit_id:
content["body"] = f" * {message}"
content["m.new_content"] = {
"body": message,
"formatted_body": content["formatted_body"],
"format": "org.matrix.custom.html",
"msgtype": "m.text"
}
@ -183,7 +249,8 @@ class DiscordClient(discord.ext.commands.Bot):
content = await self.process_message(message)
matrix_message = await self.matrix_client.message_send(
content[0], message.channel.id, reply_id=content[1]
content[0], message.channel.id,
reply_id=content[1], emotes=content[2]
)
message_store[message.id] = matrix_message
@ -196,7 +263,8 @@ class DiscordClient(discord.ext.commands.Bot):
if before.id in message_store:
await self.matrix_client.message_send(
content[0], after.channel.id, edit_id=message_store[before.id]
content[0], after.channel.id,
edit_id=message_store[before.id], emotes=content[2]
)
async def on_message_delete(self, message):
@ -217,6 +285,12 @@ class DiscordClient(discord.ext.commands.Bot):
async def process_message(self, message):
content = message.clean_content
regex = r"<a?:(\w+):(\d+)>"
emotes = {}
for emote in re.findall(regex, content):
emotes[emote[0]] = emote[1]
replied_event = None
if message.reference:
replied_message = await message.channel.fetch_message(
@ -228,7 +302,7 @@ class DiscordClient(discord.ext.commands.Bot):
pass
# Replace emote IDs with names
content = re.sub(r"<a?(:\w+:)\d*>", r"\g<1>", content)
content = re.sub(regex, r":\g<1>:", content)
# Append attachments to message
for attachment in message.attachments:
@ -236,7 +310,7 @@ class DiscordClient(discord.ext.commands.Bot):
content = f"[{message.author.name}] {content}"
return content, replied_event
return content, replied_event, emotes
class Callbacks(object):

View file

@ -1,2 +1,2 @@
git+git://github.com/rapptz/discord.py@c793737
discord.py==1.6.0
matrix-nio==0.15.2