From c20ebb6a4fed512b90a09c99f5a2caa4ce6b1137 Mon Sep 17 00:00:00 2001 From: Marc Debureaux Date: Thu, 13 Apr 2023 23:53:05 +0200 Subject: [PATCH] Cleaning / Logging / Enable mentions on Discord --- config/config.json.dist | 3 +- discord-irc-sync.py | 2 +- src/discordclient.py | 220 +++++++++++++++++++--------------------- src/formatting.py | 122 +++++++++++----------- src/ircclient.py | 131 +++++++++++++----------- src/notification.py | 57 ++++++----- src/utils.py | 22 +++- 7 files changed, 292 insertions(+), 265 deletions(-) diff --git a/config/config.json.dist b/config/config.json.dist index 88c9ce6..9e87dde 100644 --- a/config/config.json.dist +++ b/config/config.json.dist @@ -19,7 +19,8 @@ "cmd_prefix": "!", "output_msg": "<:username:> :message:", "output_cmd": "CMD by :username:", - "log_events": true + "log_events": true, + "mentions": false }, "formatting": { "irc_to_discord": false, diff --git a/discord-irc-sync.py b/discord-irc-sync.py index ceff413..9ebba9c 100644 --- a/discord-irc-sync.py +++ b/discord-irc-sync.py @@ -9,7 +9,7 @@ config_file = sys.argv[1] if len(sys.argv) == 2 else None settings = utils.read_config(config_file) -settings['irc']['master_bot'] = True +settings["irc"]["master_bot"] = True discord_client = DiscordClient(settings) irc_client = IRCClient(settings) diff --git a/src/discordclient.py b/src/discordclient.py index 4e1a106..165d561 100644 --- a/src/discordclient.py +++ b/src/discordclient.py @@ -4,57 +4,60 @@ import re import discord +import discord.utils +from . import utils from .formatting import D2IFormatter from .notification import Notification +logger = utils.get_logger(__name__) + class DiscordClient(discord.Client): def __init__(self, configuration): - self.h_token = configuration['discord']['token'] - self.h_server_id = configuration['discord']['server'] - self.h_channel_id = configuration['discord']['channel'] - self.h_owner = configuration['discord']["owner"] - self.h_cmd_prefix = configuration['discord']["cmd_prefix"] - self.h_output_msg = configuration['discord']["output_msg"] - self.h_output_cmd = configuration['discord']["output_cmd"] - self.h_log_events = configuration['discord']["log_events"] - self.h_formatter = D2IFormatter(configuration) - self.h_channel = None - self.h_irc = None - self.users_bots = {} - self.callback = { - 'notification' : { - 'nick_in_use' :self.unimplemented, - 'bot_created' :self.on_bot_created, - 'bot_ch_nick' :self.on_bot_change_nick, - 'bot_killed' :self.on_bot_killed}, - 'message' : { - 'default' :self.h_send_message, - 'command' :self.h_send_command, - 'raw' :self.h_send_raw_message}, - 'quit' : { - 'default' :self.on_irc_quit}, - 'kick' : { - 'default' :self.on_irc_kick}, - 'part' : { - 'default' :self.on_irc_part}, - 'join' : { - 'default' :self.on_irc_join}, - + self.h_token = configuration["discord"]["token"] + self.h_server_id = configuration["discord"]["server"] + self.h_channel_id = configuration["discord"]["channel"] + self.h_owner = configuration["discord"]["owner"] + self.h_cmd_prefix = configuration["discord"]["cmd_prefix"] + self.h_output_msg = configuration["discord"]["output_msg"] + self.h_output_cmd = configuration["discord"]["output_cmd"] + self.h_log_events = configuration["discord"]["log_events"] + self.h_mentions = configuration["discord"]["mentions"] + self.h_formatter = D2IFormatter(configuration) + self.h_channel = None + self.h_irc = None + self.users_bots = {} + self.callback = { + "notification": { + "nick_in_use": self.unimplemented, + "bot_created": self.on_bot_created, + "bot_ch_nick": self.on_bot_change_nick, + "bot_killed": self.on_bot_killed, + }, + "message": { + "default": self.h_send_message, + "command": self.h_send_command, + "raw": self.h_send_raw_message, + }, + "quit": {"default": self.on_irc_quit}, + "kick": {"default": self.on_irc_kick}, + "part": {"default": self.on_irc_part}, + "join": {"default": self.on_irc_join}, } intents = discord.Intents.default() intents.typing = False intents.presences = False intents.message_content = True + intents.members = self.h_mentions super().__init__(intents=intents) def unimplemented(self, notif): - print(notif) - print('This feature is not yet implemented') + logger.info(notif) + logger.info("This feature is not yet implemented") def on_irc_alreadyinuse(self, notif): - del(self.users_bots[notif.src_user]) + del self.users_bots[notif.src_user] def on_irc_quit(self, notif): pass @@ -81,61 +84,61 @@ def set_irc(self, irc): self.h_irc = irc async def on_ready(self): - print("[Discord] Logged in as:") - print("[Discord] " + self.user.name) + logger.info("[Discord] Logged in as:") + logger.info("[Discord] " + self.user.name) if len(self.guilds) == 0: - print("[Discord] Bot is not yet in any server.") + logger.info("[Discord] Bot is not yet in any server.") await self.close() return - + if self.h_server_id == "": - print("[Discord] You have not configured a server to use in settings.json") - print("[Discord] Please put one of the server IDs listed below in settings.json") - + logger.info("[Discord] You have not configured a server to use in settings.json") + logger.info("[Discord] Please put one of the server IDs listed below in settings.json") + for server in self.guilds: - print("[Discord] %s: %s" % (server.name, server.id)) - + logger.info("[Discord] %s: %s" % (server.name, server.id)) + await self.close() return - + findServer = [x for x in self.guilds if x.id == self.h_server_id] if not len(findServer): - print("[Discord] No server could be found with the specified id: " + self.h_server_id) - print("[Discord] Available servers:") - + logger.info("[Discord] No server could be found with the specified id: " + self.h_server_id) + logger.info("[Discord] Available servers:") + for server in self.guilds: - print("[Discord] %s: %s" % (server.name, server.id)) - + logger.info("[Discord] %s: %s" % (server.name, server.id)) + await self.close() return - + server = findServer[0] - + if self.h_channel_id == "": - print("[Discord] You have not configured a channel to use in settings.json") - print("[Discord] Please put one of the channel IDs listed below in settings.json") - + logger.info("[Discord] You have not configured a channel to use in settings.json") + logger.info("[Discord] Please put one of the channel IDs listed below in settings.json") + for channel in server.channels: if channel.type == discord.ChannelType.text: - print("[Discord] %s: %s" % (channel.name, channel.id)) - + logger.info("[Discord] %s: %s" % (channel.name, channel.id)) + await self.close() return - + find_channel = [x for x in server.text_channels if x.id == self.h_channel_id] if not len(find_channel): - print("[Discord] No channel could be found with the specified id: " + self.h_server_id) - print("[Discord] Note that you can only use text channels.") - print("[Discord] Available channels:") - + logger.info("[Discord] No channel could be found with the specified id: " + self.h_server_id) + logger.info("[Discord] Note that you can only use text channels.") + logger.info("[Discord] Available channels:") + for channel in server.channels: if channel.type == discord.ChannelType.text: - print("[Discord] %s: %s" % (channel.name, channel.id)) - + logger.info("[Discord] %s: %s" % (channel.name, channel.id)) + await self.close() return - + self.h_channel = find_channel[0] async def on_message(self, message): @@ -152,10 +155,8 @@ async def on_message(self, message): return username = self.get_nick(message.author) - content = message.clean_content - """ Admin commands """ @@ -173,8 +174,8 @@ async def on_message(self, message): """ Send to IRC """ - for c in content.split('\n'): - print("[Discord] <%s> %s" % (username, c)) + for c in content.split("\n"): + logger.info("[Discord] <%s> %s" % (username, c)) self.h_send_to_irc(username, self.h_format_text(c.strip())) async def on_member_join(self, member): @@ -191,11 +192,8 @@ async def on_member_join(self, member): return username = self.get_nick(member) - message = self.h_format_text("*%s* has joined the server" % username) - - self.h_send_notification('message', 'raw', message, username) - + self.h_send_notification("message", "raw", message, username) async def on_member_remove(self, member): """ @@ -211,10 +209,8 @@ async def on_member_remove(self, member): return username = self.get_nick(member) - message = self.h_format_text("*%s* has quit the server" % username) - - self.h_send_notification('message', 'raw', message, username) + self.h_send_notification("message", "raw", message, username) async def on_member_update(self, member_before, member_after): """ @@ -232,10 +228,9 @@ async def on_member_update(self, member_before, member_after): if username_a != username_b: if not self.h_log_events: message = self.h_format_text("*%s* is now known as *%s*" % (username_b, username_a)) - self.h_send_notification('message', 'raw', message, username_b) - self.h_send_notification('user', 'change_nick', None, username_b, username_a) + self.h_send_notification("message", "raw", message, username_b) + self.h_send_notification("user", "change_nick", None, username_b, username_a) - username = username_b """ @@ -244,19 +239,18 @@ async def on_member_update(self, member_before, member_after): if member_before.status == discord.Status.offline and member_after.status != discord.Status.offline: if not self.h_log_events: message = self.h_format_text("*%s* has joined" % (username,)) - self.h_send_notification('message', 'raw', message, None) + self.h_send_notification("message", "raw", message, None) # spawn a new IRC user bot - self.h_send_notification('user', 'join', None, username) - + self.h_send_notification("user", "join", None, username) if member_before.status != discord.Status.offline and member_after.status == discord.Status.offline: if not self.h_log_events: message = self.h_format_text("*%s* has quit" % (username,)) - self.h_send_notification('message', 'raw', message, None) + self.h_send_notification("message", "raw", message, None) # kill the IRC user bot - self.h_send_notification('user', 'quit', None, username) + self.h_send_notification("user", "quit", None, username) def get_nick(self, member): if member.nick is not None: @@ -265,52 +259,51 @@ def get_nick(self, member): def hl_nicks(self, message): for client in self.get_all_members(): - nick = ''.join("[" + c.replace("\\","\\\\") + "]" for c in self.get_nick(client)) - message = re.sub(r'\b(' + nick + r')\b', client.mention, message, flags=re.IGNORECASE) + nick = "".join("[" + c.replace("\\", "\\\\") + "]" for c in self.get_nick(client)) + message = re.sub(r"\b(" + nick + r")\b", client.mention, message, flags=re.IGNORECASE) return message def de_hl_nick(self, nick): special_chars = { - 'a':'а', - 'A':'А', - 'B':'В', - 'S':'Ѕ', - 'M':'М', - 'O':'О', - 'o':'о', - 'p':'р', - 'P':'Р', - 'c':'с', - 'y':'у', - 'x':'х', - 's':'ѕ', - 'i':'і', - 'j':'ј', - 'e':'е', - '0':'ѳ', - 'h':'Һ' + "a": "а", + "A": "А", + "B": "В", + "S": "Ѕ", + "M": "М", + "O": "О", + "o": "о", + "p": "р", + "P": "Р", + "c": "с", + "y": "у", + "x": "х", + "s": "ѕ", + "i": "і", + "j": "ј", + "e": "е", + "0": "ѳ", + "h": "Һ", } output = [c for c in nick] for key, letter in enumerate(nick): if letter in special_chars: output[key] = special_chars[letter] - return ''.join(output) + return "".join(output) return nick[0] + "'" + nick[1:] - def h_raw_send_to_irc(self, message): - print("[Discord] %s" % message) - self.h_send_notification('message', 'raw', message, None) + logger.info("[Discord] %s" % message) + self.h_send_notification("message", "raw", message, None) def h_send_to_irc(self, username, content): if username not in self.users_bots.keys(): content = self.h_output_msg.replace(":username:", username).replace(":message:", content) - + if content.startswith(self.h_cmd_prefix): - self.h_send_notification('message', 'raw', self.h_output_cmd.replace(":username:", username)) - self.h_send_notification('message', 'raw', content, username) + self.h_send_notification("message", "raw", self.h_output_cmd.replace(":username:", username)) + self.h_send_notification("message", "raw", content, username) else: - self.h_send_notification('message', None, content, username) + self.h_send_notification("message", None, content, username) # notification system @@ -326,14 +319,13 @@ def h_send_notification(self, n_type, subtype=None, content=None, username=None, def get_notification(self, notif): self.callback[notif.n_type][notif.subtype](notif) - # discord sending def h_send_message(self, notif): - user = '' if notif.src_user is None else '<'+notif.src_user+'> ' + user = "" if notif.src_user is None else "<" + notif.src_user + "> " if notif.src_user == self.user or notif.src_user in self.users_bots.keys(): return - asyncio.run_coroutine_threadsafe(self.h_send_message_async(user+notif.message), self.loop) + asyncio.run_coroutine_threadsafe(self.h_send_message_async(user + notif.message), self.loop) def h_send_raw_message(self, notif): asyncio.run_coroutine_threadsafe(self.h_send_message_async(notif.message), self.loop) diff --git a/src/formatting.py b/src/formatting.py index 0003d44..d939528 100644 --- a/src/formatting.py +++ b/src/formatting.py @@ -1,54 +1,49 @@ import re from src.utils import replace_all, is_included -IRC_BOLD, IRC_ITALIC, IRC_UNDERLINE, IRC_RESET = ("\x02","\x1d", "\x1f", "\x0f") -DSC_BOLD, DSC_ITALIC, DSC_UNDERLINE = ("**","*","__") +IRC_BOLD, IRC_ITALIC, IRC_UNDERLINE, IRC_RESET = ("\x02", "\x1d", "\x1f", "\x0f") +DSC_BOLD, DSC_ITALIC, DSC_UNDERLINE = ("**", "*", "__") -class D2IFormatter(): - +class D2IFormatter: syntax = { - 'double_emphasis': { - 're': re.compile(r'(\*{2})([\s\S]+?)(\*{2})(?!\*)'), - 'irc': IRC_BOLD, - 'discord': DSC_BOLD + "double_emphasis": { + "re": re.compile(r"(\*{2})([\s\S]+?)(\*{2})(?!\*)"), + "irc": IRC_BOLD, + "discord": DSC_BOLD, + }, + "emphasis": { + "re": re.compile(r"\b(_)((?:__|[^_])+?)(_)\b" r"|" r"(\*)((?:\*\*|[^\*])+?)(\*)(?!\*)"), # _word_ # *word* + "irc": IRC_ITALIC, + "discord": DSC_ITALIC, }, - 'emphasis': { - 're': re.compile( - r'\b(_)((?:__|[^_])+?)(_)\b' # _word_ - r'|' - r'(\*)((?:\*\*|[^\*])+?)(\*)(?!\*)' # *word* - ), - 'irc': IRC_ITALIC, - 'discord': DSC_ITALIC + "underline": { + "re": re.compile(r"(_{2})([\s\S]+?)(_{2})(?!_)"), + "irc": IRC_UNDERLINE, + "discord": DSC_UNDERLINE, }, - 'underline': { - 're': re.compile(r'(_{2})([\s\S]+?)(_{2})(?!_)'), - 'irc': IRC_UNDERLINE, - 'discord': DSC_UNDERLINE - } } - rules = ['double_emphasis', 'emphasis', 'underline'] + rules = ["double_emphasis", "emphasis", "underline"] def __init__(self, configuration): - self.doformat = configuration['formatting']['discord_to_irc'] + self.doformat = configuration["formatting"]["discord_to_irc"] def replace_double_emphasis(self, matchobj): - return self.syntax['double_emphasis']['irc'] + matchobj.group(2) + self.syntax['double_emphasis']['irc'] + return self.syntax["double_emphasis"]["irc"] + matchobj.group(2) + self.syntax["double_emphasis"]["irc"] def replace_emphasis(self, matchobj): if matchobj.group(2): res = matchobj.group(2) else: res = matchobj.group(5) - return self.syntax['emphasis']['irc'] + res + self.syntax['emphasis']['irc'] + return self.syntax["emphasis"]["irc"] + res + self.syntax["emphasis"]["irc"] def replace_underline(self, matchobj): - return self.syntax['underline']['irc'] + matchobj.group(2) + self.syntax['underline']['irc'] + return self.syntax["underline"]["irc"] + matchobj.group(2) + self.syntax["underline"]["irc"] def sanitize(self, message): - message = re.sub(r'\\([^A-Za-z0-9])',r'\1', message) + message = re.sub(r"\\([^A-Za-z0-9])", r"\1", message) return message def format(self, message): @@ -60,44 +55,48 @@ def format(self, message): Surround formatted groups with IRC flags based on matching regex """ for rule in self.rules: - regex = self.syntax[rule]['re'] + regex = self.syntax[rule]["re"] m = regex.search(message) if m is not None: - message = regex.sub(getattr(self, 'replace_%s' % rule), message) + message = regex.sub(getattr(self, "replace_%s" % rule), message) return message -class I2DFormatter: +class I2DFormatter: B_FLAG, I_FLAG, U_FLAG = (0x01, 0x02, 0x04) symbols = { IRC_BOLD: B_FLAG, IRC_ITALIC: I_FLAG, IRC_UNDERLINE: U_FLAG, - IRC_RESET: False + IRC_RESET: False, } def __init__(self, configuration): - self.doformat = configuration['formatting']['irc_to_discord'] + self.doformat = configuration["formatting"]["irc_to_discord"] def sanitize(self, message): """ Remove color tags, and format tags if no formatting setting Escape discord format tags """ - replacements = [('\\','\\\\'), ('~~', '\\~\\~')] + replacements = [("\\", "\\\\"), ("~~", "\\~\\~")] message = replace_all(message, replacements) - message = re.sub(r'(\b)_([^\b])', r'\1\\_\2', message) - message = re.sub(r'([^\b])_(\b)', r'\1\\_\2', message) + message = re.sub(r"(\b)_([^\b])", r"\1\\_\2", message) + message = re.sub(r"([^\b])_(\b)", r"\1\\_\2", message) + + message = re.sub(r"(\b)\*([^\b])", r"\1\\*\2", message) + message = re.sub(r"([^\b])\*(\b)", r"\1\\*\2", message) - message = re.sub(r'(\b)\*([^\b])', r'\1\\*\2', message) - message = re.sub(r'([^\b])\*(\b)', r'\1\\*\2', message) - if not self.doformat: - message = re.sub(r'['+ IRC_BOLD + IRC_UNDERLINE + IRC_ITALIC + IRC_RESET +']', '', message) - return re.sub(r'\x03\d{2}(?:,\d{2})', '', message) + message = re.sub( + r"[" + IRC_BOLD + IRC_UNDERLINE + IRC_ITALIC + IRC_RESET + "]", + "", + message, + ) + return re.sub(r"\x03\d{2}(?:,\d{2})", "", message) def format(self, message): message = self.sanitize(message) @@ -107,21 +106,24 @@ def format(self, message): """ Create dict with all characters and their format """ - char_list = [(c,0) for c in message] + char_list = [(c, 0) for c in message] counter = 0 while counter < len(char_list): - char_tuple=char_list[counter] - - if char_tuple[0] in self.symbols: # Formatting character + char_tuple = char_list[counter] + + if char_tuple[0] in self.symbols: # Formatting character del char_list[counter] for i in range(counter, len(char_list)): if self.symbols[char_tuple[0]]: - char_list[i] = (char_list[i][0], char_list[i][1]^self.symbols[char_tuple[0]]) + char_list[i] = ( + char_list[i][0], + char_list[i][1] ^ self.symbols[char_tuple[0]], + ) else: char_list[i] = (char_list[i][0], 0) - else: # Common character. Goto next one - counter+=1 - + else: # Common character. Goto next one + counter += 1 + """ Create intervals of formatting types """ @@ -138,21 +140,21 @@ def format(self, message): if char_tuple[1] & self.U_FLAG: underline_i = [DSC_UNDERLINE, 0, False] else: - if char_tuple[1] & self.B_FLAG ^ char_list[key-1][1] & self.B_FLAG: + if char_tuple[1] & self.B_FLAG ^ char_list[key - 1][1] & self.B_FLAG: if bold_i is not None: bold_i[2] = key intervals.append(bold_i) bold_i = None else: bold_i = [DSC_BOLD, key, False] - if char_tuple[1] & self.I_FLAG ^ char_list[key-1][1] & self.I_FLAG: + if char_tuple[1] & self.I_FLAG ^ char_list[key - 1][1] & self.I_FLAG: if italic_i is not None: italic_i[2] = key intervals.append(italic_i) italic_i = None else: italic_i = [DSC_ITALIC, key, False] - if char_tuple[1] & self.U_FLAG ^ char_list[key-1][1] & self.U_FLAG: + if char_tuple[1] & self.U_FLAG ^ char_list[key - 1][1] & self.U_FLAG: if underline_i is not None: underline_i[2] = key intervals.append(underline_i) @@ -178,7 +180,7 @@ def format(self, message): """ if intervals == []: return message - + """ Order intervals (not included > included) """ @@ -187,7 +189,7 @@ def format(self, message): while len(intervals) > 1: included = False current = intervals[key] - for k_tested, interval in enumerate(intervals[key+1:]): + for k_tested, interval in enumerate(intervals[key + 1 :]): if is_included(current, interval) == 0: included = True continue @@ -195,21 +197,21 @@ def format(self, message): ordered_intervals.append(intervals[key]) del intervals[key] else: - key = (key+1)%len(intervals) + key = (key + 1) % len(intervals) if len(intervals) == 1: ordered_intervals.append(intervals[0]) - + """ Position the formatting elements """ - res = ''.join([c[0] for c in char_list]) + res = "".join([c[0] for c in char_list]) add = [] - for c in range(len(res)+1): + for c in range(len(res) + 1): add.append([]) for c in range(len(res)): for i in ordered_intervals[::-1]: - if c == i[2]-1: - add[c+1].append(i[0]) + if c == i[2] - 1: + add[c + 1].append(i[0]) for i in ordered_intervals: if c == i[1]: add[c].append(i[0]) @@ -217,5 +219,5 @@ def format(self, message): """ Output the final string """ - result = ''.join(''.join(add[i]) + res[i] for i in range(len(res))) + ''.join(add[len(res)]) + result = "".join("".join(add[i]) + res[i] for i in range(len(res))) + "".join(add[len(res)]) return result diff --git a/src/ircclient.py b/src/ircclient.py index 34a6c03..a3e2fc1 100644 --- a/src/ircclient.py +++ b/src/ircclient.py @@ -1,74 +1,79 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python3( # coding: utf-8 import irc.bot import threading import time import ssl + from . import utils from .notification import Notification - from .formatting import I2DFormatter -irc.client.ServerConnection.buffer_class.errors = 'replace' +irc.client.ServerConnection.buffer_class.errors = "replace" + +logger = utils.get_logger(__name__) class IRCClient(irc.bot.SingleServerIRCBot): def __init__(self, configuration): - self.h_server = configuration['irc']["server"] - self.h_port = int(configuration['irc']["port"]) - self.h_ssl = configuration['irc']['ssl'] - self.h_nickname = configuration['irc']["nickname"] - self.h_channel = configuration['irc']["channel"] - self.h_owner = configuration['irc']["owner"] - self.h_cmd_prefix = configuration['irc']["cmd_prefix"] - self.h_log_events = configuration['irc']["log_events"] - self.h_output_msg = configuration['irc']["output_msg"] - self.h_output_cmd = configuration['irc']["output_cmd"] - self.h_formatter = I2DFormatter(configuration) - self.h_discord = None + self.h_server = configuration["irc"]["server"] + self.h_port = int(configuration["irc"]["port"]) + self.h_ssl = configuration["irc"]["ssl"] + self.h_nickname = configuration["irc"]["nickname"] + self.h_channel = configuration["irc"]["channel"] + self.h_owner = configuration["irc"]["owner"] + self.h_cmd_prefix = configuration["irc"]["cmd_prefix"] + self.h_log_events = configuration["irc"]["log_events"] + self.h_output_msg = configuration["irc"]["output_msg"] + self.h_output_cmd = configuration["irc"]["output_cmd"] + self.h_formatter = I2DFormatter(configuration) + self.h_discord = None self.h_connection = None - self.master_bot = False - self.callback = { - 'notification' : { - 'nick_in_use' :self.unimplemented}, - 'message' : { - 'default' :self.h_send_message, - 'raw' :self.h_send_message}, - 'user' : { - 'default' :self.unimplemented, - 'join' :self.on_dsc_join, - 'quit' :self.on_dsc_quit, - 'change_nick' :self.on_dsc_change_nick}, + self.master_bot = False + self.callback = { + "notification": {"nick_in_use": self.unimplemented}, + "message": {"default": self.h_send_message, "raw": self.h_send_message}, + "user": { + "default": self.unimplemented, + "join": self.on_dsc_join, + "quit": self.on_dsc_quit, + "change_nick": self.on_dsc_change_nick, + }, } - if 'master_bot' in configuration['irc'].keys(): - self.master_bot = configuration['irc']["master_bot"] + if "master_bot" in configuration["irc"].keys(): + self.master_bot = configuration["irc"]["master_bot"] if self.h_ssl: ssl_factory = irc.connection.Factory(wrapper=ssl.wrap_socket) - super().__init__([(self.h_server, self.h_port)], self.h_nickname, self.h_nickname, connect_factory=ssl_factory) + super().__init__( + [(self.h_server, self.h_port)], + self.h_nickname, + self.h_nickname, + connect_factory=ssl_factory, + ) else: super().__init__([(self.h_server, self.h_port)], self.h_nickname, self.h_nickname) def unimplemented(self, notif): - print(notif) - print('This feature is not yet implemented') + logger.info(notif) + logger.info("This feature is not yet implemented") def on_dsc_quit(self, notif): if self.master_bot is False: self.disconnect() - self.h_send_notification('notification', 'bot_killed', None, notif.src_user) + self.h_send_notification("notification", "bot_killed", None, notif.src_user) def on_dsc_change_nick(self, notif): if self.master_bot is False: self.h_connection.nick(notif.dst_user) - self.h_nickname = notif.dst_user - self.h_send_notification('notification', 'bot_ch_nick', None, notif.src_user, notif.dst_user) + self.h_nickname = notif.dst_user + self.h_send_notification("notification", "bot_ch_nick", None, notif.src_user, notif.dst_user) def on_dsc_join(self, notif): conf = utils.read_config() - conf['irc']['nickname'] = notif.src_user + conf["irc"]["nickname"] = notif.src_user irc_client = IRCClient(conf) irc_client.set_discord(self.h_discord) irc_client.h_run() @@ -79,11 +84,11 @@ def set_discord(self, discord): def on_welcome(self, server, event): self.h_connection = server server.join(self.h_channel) - print("[IRC] Logged in as:") - print("[IRC] %s" % self.h_nickname) + logger.info("[IRC] Logged in as:") + logger.info("[IRC] %s" % self.h_nickname) if self.master_bot is False: # notify dsc that the user is OK only when it's connected to the chan - self.h_send_notification('notification', 'bot_created', self, self.h_nickname) + self.h_send_notification("notification", "bot_created", self, self.h_nickname) def on_pubmsg(self, server, event): username = event.source.nick @@ -121,14 +126,13 @@ def on_action(self, server, event): self.h_raw_send_to_discord("\\* **" + username + "** " + content) - def on_join(self, server, event): if event.source.nick == self.h_nickname: return if self.h_log_events: message = "*%s* has joined the channel" % event.source.nick - self.h_send_notification('message', 'raw', message, event.source.nick) - self.h_send_notification('join', None, message, event.source.nick) + self.h_send_notification("message", "raw", message, event.source.nick) + self.h_send_notification("join", None, message, event.source.nick) def on_part(self, server, event): if event.source.nick == self.h_nickname: @@ -138,8 +142,8 @@ def on_part(self, server, event): reason = event.arguments[0] if self.h_log_events: message = "*%s* has left the channel (%s)" % (event.source.nick, reason) - self.h_send_notification('message', 'raw', message, event.source.nick) - self.h_send_notification('part', None, message, event.source.nick) + self.h_send_notification("message", "raw", message, event.source.nick) + self.h_send_notification("part", None, message, event.source.nick) def on_quit(self, server, event): if event.source.nick == self.h_nickname: @@ -149,47 +153,54 @@ def on_quit(self, server, event): reason = event.arguments[0] if self.h_log_events: message = "*%s* has quit the channel (%s)" % (event.source.nick, reason) - self.h_send_notification('message', 'raw', message, event.source.nick) - self.h_send_notification('quit', None, message, event.source.nick) - + self.h_send_notification("message", "raw", message, event.source.nick) + self.h_send_notification("quit", None, message, event.source.nick) def on_kick(self, server, event): reason = "*" if len(event.arguments) > 1: reason = event.arguments[1] if self.h_log_events: - message = "*%s* has been kicked of the channel (%s)" % (event.arguments[0], reason) - self.h_send_notification('message', 'raw', message, event.arguments[0]) - self.h_send_notification('kick', None, message, event.arguments[0]) + message = "*%s* has been kicked of the channel (%s)" % ( + event.arguments[0], + reason, + ) + self.h_send_notification("message", "raw", message, event.arguments[0]) + self.h_send_notification("kick", None, message, event.arguments[0]) time.sleep(2) server.join(self.h_channel) def on_nicknameinuse(self, server, event): if self.master_bot is True: server.nick((self.h_nickname + "_")) - self.h_nickname = self.h_nickname+'_' + self.h_nickname = self.h_nickname + "_" else: # user bot. If the nickname is already in use, we suppose that the user is connected onto DSC & IRC # so we just kill the connection and notify the discord class, in order to drop it from the list - print('User already in use on IRC. Killing thread & notifying the discord class') - self.h_send_notification('notification', 'nick_in_use', None, self.h_nickname) + logger.info("User already in use on IRC. Killing thread & notifying the discord class") + self.h_send_notification("notification", "nick_in_use", None, self.h_nickname) self.die() def h_send_to_discord(self, username, content): if self.master_bot is True: message = self.h_output_msg.replace(":username:", username).replace(":message:", content) - print("[IRC] %s" % message) + logger.info("[IRC] %s" % message) if content.startswith(self.h_cmd_prefix): - self.h_send_notification('message', 'raw', self.h_output_cmd.replace(":username:", username), username) - self.h_send_notification('message', 'raw', content, username) + self.h_send_notification( + "message", + "raw", + self.h_output_cmd.replace(":username:", username), + username, + ) + self.h_send_notification("message", "raw", content, username) else: - self.h_send_notification('message', None, content, username) + self.h_send_notification("message", None, content, username) def h_raw_send_to_discord(self, message): if self.master_bot is True: - print("[IRC] %s" % message) - self.h_send_notification('message', 'raw', message, None) + logger.info("[IRC] %s" % message) + self.h_send_notification("message", "raw", message, None) # notification system @@ -204,7 +215,7 @@ def get_notification(self, notif): def h_send_message(self, notif): if self.master_bot is True or self.h_nickname == notif.src_user: - self.h_connection.privmsg(self.h_channel,notif.message) + self.h_connection.privmsg(self.h_channel, notif.message) def h_format_text(self, message): return self.h_formatter.format(message) diff --git a/src/notification.py b/src/notification.py index ce7fcd6..5011153 100644 --- a/src/notification.py +++ b/src/notification.py @@ -3,44 +3,47 @@ class Notification(object): """docstring for Notification""" + def __init__(self, n_type, subtype=None, message=None, user=None, dest_user=None): - self.allowed_types = ['notification', 'message', 'raw_message', 'quit', 'join'] - self.src_user = None - self.dst_user = None - self.message = None - self.n_type = None - self.subtype = None + self.allowed_types = ["notification", "message", "raw_message", "quit", "join"] + self.src_user = None + self.dst_user = None + self.message = None + self.n_type = None + self.subtype = None self.allowed_types = { - 'notification' : ['nick_in_use', 'bot_created', 'bot_killed', 'bot_ch_nick'], - 'message' : ['raw'], - 'query' : [], - 'quit' : [], - 'part' : [], - 'join' : [], - 'kick' : [], - 'user' : ['join', 'quit', 'change_nick'] - } + "notification": ["nick_in_use", "bot_created", "bot_killed", "bot_ch_nick"], + "message": ["raw"], + "query": [], + "quit": [], + "part": [], + "join": [], + "kick": [], + "user": ["join", "quit", "change_nick"], + } if n_type not in self.allowed_types.keys(): - print('the notification "'+n_type+'" is not yet implemented.') + print('the notification "' + n_type + '" is not yet implemented.') return else: if subtype is not None: - if subtype not in self. allowed_types[n_type]: - print('the subtype "'+subtype+'" is not yet implemented.') + if subtype not in self.allowed_types[n_type]: + print('the subtype "' + subtype + '" is not yet implemented.') return else: - subtype = 'default' + subtype = "default" - self.n_type = n_type + self.n_type = n_type self.src_user = user self.dst_user = dest_user - self.message = message - self.subtype = subtype + self.message = message + self.subtype = subtype def __str__(self): - return "Type: "+str(self.n_type)+"\r\n" \ - "Subtype: "+str(self.subtype)+"\r\n" \ - "Src User: "+str(self.src_user)+"\r\n" \ - "Dst User: "+str(self.dst_user)+"\r\n" \ - "Msg: "+str(self.message) \ No newline at end of file + return ( + "Type: " + str(self.n_type) + "\r\n" + "Subtype: " + str(self.subtype) + "\r\n" + "Src User: " + str(self.src_user) + "\r\n" + "Dst User: " + str(self.dst_user) + "\r\n" + "Msg: " + str(self.message) + ) diff --git a/src/utils.py b/src/utils.py index 6586e73..039f224 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,6 +1,8 @@ -import sys import json +import logging import os +import sys + def replace_all(text, l): for t in l: @@ -8,7 +10,7 @@ def replace_all(text, l): return text -def is_included(a,b): +def is_included(a, b): """ Return 0 if a is included in b Return -1 if a intersects b but a not included in b and b not included in a @@ -21,6 +23,7 @@ def is_included(a,b): else: return 1 + def read_config(config_file=None): config_file = os.path.join("config", "config.json") if config_file is None else config_file @@ -29,3 +32,18 @@ def read_config(config_file=None): with open(config_file, encoding="utf-8") as f: return json.loads(f.read()) + + +def get_logger(module_name, log_level=logging.INFO): + from discord.utils import _ColourFormatter, stream_supports_colour + + logger = logging.getLogger(module_name) + logger.setLevel(log_level) + handler = logging.StreamHandler() + if stream_supports_colour(handler.stream): + formatter = _ColourFormatter() + else: + formatter = logging.Formatter("[{asctime}] [{levelname:<8}] {name}: {message}", "%Y-%m-%d %H:%M:%S", style="{") + handler.setFormatter(formatter) + logger.addHandler(handler) + return logger