Created
March 14, 2019 10:45
-
-
Save jimsug/e4c65248cb770fab515e7142114a5dea to your computer and use it in GitHub Desktop.
Hangoutsbot discord sync
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import plugins | |
import discord | |
import asyncio | |
import logging | |
import aiohttp | |
import io, re, requests | |
import copy | |
from pprint import pprint | |
from event import ConversationEvent | |
logger = logging.getLogger(__name__) | |
client = discord.Client() | |
_bot = None | |
sending = {} | |
currentKnownBots = [] | |
already_seen_discord_messages = [] | |
@client.event | |
@asyncio.coroutine | |
def on_ready(): | |
logger.debug('Logged into discord as {} {}'.format(client.user.name, client.user.id)) | |
@client.event | |
@asyncio.coroutine | |
def on_message(message): | |
if message.author == client.user: | |
return | |
try: | |
if message.webhook_id: | |
sendmsg = False | |
return | |
except AttributeError: | |
pass | |
if message.author.bot: | |
return | |
global already_seen_discord_messages | |
if message.id in already_seen_discord_messages: | |
return | |
print("{0.channel.id}: {0.content}".format(message)) | |
already_seen_discord_messages.append(message.id) | |
global sending | |
if 'whereami' in message.content: | |
yield from message.channel.send(message.channel.id) | |
conv_config = _bot.config.get_by_path(["conversations"]) | |
for conv_id, config in conv_config.items(): | |
try: | |
config_sync_channel = config['discord_sync'] | |
except KeyError: | |
continue | |
except TypeError: | |
continue | |
if str(message.channel.id) == config_sync_channel: | |
fixed_message = re.sub(r"(@.*?)#0000", r"\1", message.clean_content) | |
fixed_message = re.sub(r"<(:.*?:).*?>", r"\1", fixed_message) | |
msg = "<b>{}</b>: {}".format(message.author.display_name, fixed_message) | |
if conv_id not in sending: | |
sending[conv_id] = 0 | |
sending[conv_id] += 1 | |
yield from _bot.coro_send_message(conv_id, msg, context={'discord': True}) | |
mentioning_user = _bot._user_list._self_user | |
fake_event = ConversationEvent | |
fake_event.conv_event = 0 | |
fake_event.timestamp = 0 | |
fake_event.conv_id = conv_id | |
try: | |
fake_event.conv = _bot._conv_list.get(conv_id) | |
except KeyError: | |
fake_event.conv = None | |
fake_event.user_id = mentioning_user.id_ | |
mentioning_user.full_name = "@" + message.author.display_name | |
fake_event.user = mentioning_user | |
fake_event.text = fixed_message | |
occurrences = [word for word in set(fake_event.text.split()) if word.startswith('@')] | |
if len(occurrences) > 0: | |
for word in occurrences: | |
cleaned_name = ''.join(e for e in word if e.isalnum() or e in ["-"]) | |
yield from plugins.mentions.mention( _bot, fake_event, cleaned_name) | |
for attachment in message.attachments: | |
imgurl = str(attachment.url) | |
image_id = yield from _bot.call_shared("image_validate_and_upload_single", imgurl) | |
yield from _bot.coro_send_message(conv_id, None, image_id=image_id, context={'discord': True}) | |
def _initialise(bot): | |
global _bot, client | |
_bot = bot | |
token = bot.get_config_option('discord_token') | |
if not token: | |
logger.error("discord_token not set") | |
return | |
plugins.register_handler(_handle_hangout_message, type="allmessages") | |
plugins.register_user_command(["dusers"]) | |
plugins.register_admin_command(['dsync', 'discordfwd','discordfwdfilter', 'setbot']) | |
currentKnownBots = bot.get_config_option('known_bot_users') or [] | |
logger.info(currentKnownBots) | |
try: | |
client.run(token) | |
except RuntimeError: | |
# client.run will try start an event loop, however this will fail as hangoutsbot will have already started one | |
# this isn't anything to worry about | |
pass | |
@asyncio.coroutine | |
def dusers(bot, event, *text): | |
"""List users in the synced Discord channel""" | |
print(", ".join([server for server in client.guilds])) | |
try: | |
conv_config = bot.config.get_by_path(["conversations", event.conv_id]) | |
discord_channelid = conv_config["discord_sync"] | |
print("Synced to {}".format(discord_channelid)) | |
except: | |
msg = "This chat isn't synced to any Discord channel" | |
if discord_channelid: | |
channel = client.get_channel(str(discord_channelid)) | |
print("Channel name: {}".format(channel.name)) | |
members = [] | |
for member in channel.server.members: | |
if channel.permissions_for(member).read_messages: | |
members.append(member.nickname or member.name) | |
print(members) | |
def get_discord_channel(bot, hangoutid): | |
try: | |
channelconfig = bot.config.get_by_path(['conversations', hangoutid]) | |
discord_channel_id = channelconfig['discord_sync'] | |
if discord_channel_id: | |
return client.get_channel(int(discord_channel_id)) | |
except Exception as e: | |
return False | |
def _handle_hangout_message(bot, event, command): | |
try: | |
channelconfig = bot.config.get_by_path(['conversations', event.conv_id]) | |
except Exception as e: | |
return | |
discord_channel = get_discord_channel(bot, event.conv_id) | |
if not discord_channel: | |
syncouts = bot.get_config_option("sync_rooms") or [] | |
syncout = False | |
for sync_room_list in syncouts: | |
if event.conv_id in sync_room_list: | |
syncout = sync_room_list | |
break | |
if not syncout: | |
pass | |
else: | |
for sync_room in syncout: | |
discord_channel = get_discord_channel(bot, sync_room) | |
if discord_channel: | |
break | |
try: | |
if event.user.is_self and event._slackrtm_no_repeat: | |
return | |
except AttributeError: | |
if event.user.is_self: | |
return | |
discord_webhook = bot.get_config_suboption(event.conv_id, 'discord_webhook') | |
if discord_webhook: | |
if (bot.memory.exists(['user_data', event.user_id.chat_id, 'nickname']) and len(bot.get_memory_suboption(event.user_id.chat_id, 'nickname').strip()) > 0): | |
username = bot.get_memory_suboption(event.user_id.chat_id, 'nickname').strip() | |
else: | |
username = event.user.full_name | |
try: | |
if event.user.photo_url: | |
avatar = "https:{}".format(event.user.photo_url) # use avatar from event.user if possible | |
elif bot._user_list.get_user(event.user_id).photo_url: | |
avatar = "https:{}".format(bot._user_list.get_user(event.user_id)) #user avatar from memory if not | |
else: | |
avatar = "" | |
except Exception as e: | |
avatar = "" | |
if channelconfig['discord_forward']: | |
try: | |
assert not isinstance(discord_webhook, str) | |
discord_url = discord_webhook.pop() | |
discord_webhook.insert(0, discord_url) | |
discord_url = discord_url + "?wait=true" | |
bot.config.set_by_path(["conversations", event.conv_id, 'discord_webhook'], discord_webhook) | |
except AssertionError: | |
discord_url = discord_webhook | |
try: | |
permitteduser = channelconfig['discord_forward_filter'] | |
except KeyError: | |
permitteduser = False | |
if permitteduser and not event.user.id_.chat_id == permitteduser: | |
logger.info("not authorised to be forwarded ({}), {}".format(event.user.id_.chat_id, event.user.id_.chat_id == permitteduser)) | |
return | |
elif channelconfig['discord_channel']: | |
channel = channelconfig['discord_channel'] | |
discord_url = "https://discordapp.com/api/channels/{}/messages".format(channel) | |
html_message = "" | |
try: | |
for segment in event.conv_event.segments: | |
if (not segment) or (segment.type_ == 1): | |
html_message += "\n" | |
continue | |
else: | |
html_message += markdownify(segment) | |
except AttributeError: | |
for segment in event.conv_event.ChatMessageSegment: | |
if segment.type_ == hangups.schemas.SegmentType.TEXT: | |
html_message = event.text | |
except TypeError: | |
try: | |
for segment in event.conv_event.segments: | |
if not segment: | |
html_message += "<br />" | |
continue | |
elif segment.type_ == hangups.schemas.SegmentType.TEXT: | |
html_message = event.text | |
else: | |
html_message += markdownify(segment) | |
except TypeError: | |
for segment in event.conv_event.text: | |
if not segment: | |
html_message += "<br />" | |
continue | |
else: | |
html_message = event.text | |
for a in event.conv_event.attachments: | |
html_message += "\n" + a | |
if discord_channel: | |
members = False | |
for word in html_message.split(): | |
if word == "@everyone": | |
html_message = html_message.replace(word, "@-everyone") | |
elif word == "@here": | |
html_message = html_message.replace(word, "@-here") | |
elif word.startswith("@"): | |
if not members: | |
members = discord_channel.guild.members | |
member = False | |
mentioned_name = str(word[1:]).strip().lower() | |
for user in members: | |
try: | |
if mentioned_name in [str(user.nickname).strip().lower(), str(user.name).strip().lower()]: | |
member = user | |
break | |
except AttributeError: | |
if mentioned_name == user.name.strip().lower(): | |
member = user | |
break | |
if member: | |
html_message = html_message.replace(word, "<@!{}>".format(member.id)) | |
logger.info(currentKnownBots) | |
logger.info(event.user_id.chat_id) | |
if event.user_id.chat_id in currentKnownBots: | |
splitMessage = html_message.split("**:") | |
username = splitMessage[0][2:] | |
html_message = "**:".join(splitMessage[1:]) | |
if channelconfig['discord_forward']: | |
body = {u"content":u"{}".format(html_message), u"username":u"{}".format(username), u"avatar_url":u"{}".format(avatar)} | |
logger.info(body) | |
r = requests.post(discord_url, data=body) | |
elif channelconfig['discord_channel']: | |
body = {u"content":u"{}".format(html_message)} | |
headers = {"Authorization":"Bot {}".format(bot.memory.get_by_path(['discord_token']))} | |
r = requests.post(discord_url, data=body, headers=headers) | |
def markdownify(segment): | |
text = segment.text | |
prefix = "" | |
if segment.is_bold: | |
prefix += "**" | |
if segment.is_italic: | |
prefix += "_" | |
if segment.is_underline: | |
prefix =+ "__" | |
if prefix: | |
suffix = prefix[::-1] | |
text = prefix + text + suffix | |
return text | |
def setbot(bot, event, bot_user_id, remove=False): | |
logger.info(currentKnownBots) | |
if not remove: | |
currentKnownBots.append(bot_user_id) | |
else: | |
currentKnownBots.remove(bot_user_id) | |
bot.config.set_by_path(['known_bot_users'], list(set(currentKnownBots))) | |
bot.config.save() | |
yield from bot.coro_send_message(event.conv_id, | |
"Current known bots:<br/>{}".format("</br>".join(list(set(currentKnownBots))) if currentKnownBots else "None") | |
) | |
def dsync(bot, event, discord_channel=None, convid=False, mode="forwarding"): | |
conv_id = convid or event.conv_id | |
if discord_channel == None: | |
bot.config.set_by_path(["conversations", conv_id, "discord_forward"], False) | |
bot.config.set_by_path(["conversations", conv_id, "discord_webhook"], []) | |
bot.config.set_by_path(["conversations", conv_id, "discord_sync"], None) | |
msg = "Hangout disconnected from Discord" | |
yield from bot.coro_send_message(event.conv_id, msg) | |
return | |
''' Sync a hangout to a discord channel. Usage - "/bot dsync 123456789" Say "whereami" in the channel once the bot has been added to get the channel id" ''' | |
allchannels = client.get_all_channels() | |
channel = False | |
for thisguild in client.guilds: | |
for thischannel in thisguild.text_channels: | |
if thischannel.id == int(discord_channel): | |
channel = thischannel | |
break | |
if not channel: | |
msg = "channel not found" | |
yield from bot.coro_send_message(event.conv_id, msg) | |
return | |
webhooks = yield from channel.webhooks() | |
existing_webhooks = [] | |
for webhook in webhooks: | |
print(webhook.name) | |
if webhook.name.startswith(str(conv_id)[:30]): | |
existing_webhooks.append(webhook) | |
if len(existing_webhooks) >= 2: | |
break | |
while len(existing_webhooks) < 2: | |
new_webhook_name = "{}-{}".format(str(conv_id)[:30], str(len(existing_webhooks))) | |
try: | |
new_webhook = yield from channel.create_webhook(name=new_webhook_name) | |
existing_webhooks.append(new_webhook) | |
except HTTPException: | |
logger.error("Something went wrong while creating webhooks for forwarding") | |
try: | |
bot.config.set_by_path(["conversations", conv_id, "discord_sync"], discord_channel) | |
except KeyError: | |
bot.config.set_by_path(["conversations", conv_id], {"discord_sync": discord_channel}) | |
# conv_id = convid or event.conv_id | |
try: | |
bot.config.get_by_path(["conversations", conv_id]) | |
except: | |
bot.config.set_by_path(["conversations", conv_id], {}) | |
if len(existing_webhooks) >= 2: | |
bot.config.set_by_path(["conversations", conv_id, "discord_forward"], True) | |
bot.config.set_by_path(["conversations", conv_id, "discord_webhook"], [webhook.url for webhook in existing_webhooks]) | |
# if mode == "forwarding": | |
# bot.config.set_by_path(["conversations", event.conv_id, "discord_forward"],) | |
bot.config.save() | |
msg = "Synced Hangout **{}** to Discord channel **#{}** in **{}**".format(bot.conversations.get_name(conv_id), channel.name, channel.guild.name) | |
yield from bot.coro_send_message(event.conv_id, msg) | |
def discordfwd(bot, event, url1="", url2="", convid=""): | |
'''/bot discordfwd <url1> <url2> [convid] | |
Enable forwarding of messages to the specified urls. The bot will alternate between the two webhooks to ensure correct authorship in Discord. If less than two urls are specified, the bot will disable forwarding and remove any stored webhooks. | |
If alternate Discord forwarding is enabled, it will be disabled. | |
A Hangout conversation id may be optionally provided as a third parameter, in which case these changes will be applied to that Hangout. Otherwise, the changes will be applied to the present Hangout. | |
If disabling forwarding the command must be run in the required Hangout''' | |
discordurl = re.compile("https://(canary\.)?discordapp\.com\/api\/webhooks\/\d{18}\/.+") | |
if not discordurl.match(url1) or not discordurl.match(url2): | |
msg = "Webhooks deleted. Forwarding disabled." | |
try: | |
bot.config.get_by_path(["conversations", event.conv_id]) | |
bot.config.get_by_path(["conversations", event.conv_id, "discord_forward"]) | |
except: | |
bot.config.set_by_path(["conversations", event.conv_id], {}) | |
bot.config.set_by_path(["conversations", event.conv_id, "discord_webhook"], []) | |
bot.config.set_by_path(["conversations", event.conv_id, "discord_forward"], False) | |
elif url1 == url2: | |
msg = "Those two webhooks are the same." | |
elif discordurl.match(url1) and discordurl.match(url2): | |
conv_id = convid or event.conv_id | |
try: | |
bot.config.get_by_path(["conversations", conv_id]) | |
except: | |
bot.config.set_by_path(["conversations", conv_id], {}) | |
bot.config.set_by_path(["conversations", conv_id, "discord_forward"], True) | |
bot.config.set_by_path(["conversations", conv_id, "discord_webhook"], [url1, url2]) | |
msg = "Forwarding to discord enabled for {}.".format(conv_id) | |
yield from bot.coro_send_message(event.conv, "{}: {}".format(event.user.full_name, msg)) | |
def discordfwdfilter(bot, event, convid, chatid): | |
'''/bot discordfwdfilter <convid> <chatid> | |
Limit forwarding to a single user only. If chatid is not specified, then all messages will be forwarded. convid must be specified | |
''' | |
if not chatid: | |
msg = "Filtering disabled, all users will be forwarded." | |
try: | |
bot.config.get_by_path(["conversations", convid]) | |
bot.config.get_by_path(["conversations", convid, "discord_forward_filter"]) | |
except: | |
bot.config.set_by_path(["conversations", convid], {}) | |
bot.config.set_by_path(["conversations", convid, "discord_forward_filter"], True) | |
else: | |
try: | |
bot.config.get_by_path(["conversations", convid]) | |
except: | |
bot.config.set_by_path(["conversations", convid], {}) | |
bot.config.set_by_path(["conversations", convid, "discord_forward_filter"], chatid) | |
msg = "Filtering to discord enabled for {}.".format(convid) | |
yield from bot.coro_send_message(event.conv, "{}: {}".format(event.user.full_name, msg)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment