<commit_before>import json
import os
import re
from random import choice
from threading import Lock

import discord
from colour import Color
from discord.ext.commands import cooldown, BucketType

from bot.bot import command
from utils.utilities import slots2dict, split_string
import logging

logger = logging.getLogger('debug')
manage_server = discord.Permissions(48)  # Manage server and channels
color_perms = discord.Permissions(268435504)  # Manage server, channels and roles


class ManagementHandler:
    def __init__(self, bot):
        self.bot = bot
        self.servers = {}
        self.path = os.path.join(os.getcwd(), 'data', 'servers.json')
        self._lock = Lock()
        self.reload_config()

    def reload_config(self):
        if os.path.exists(self.path):
            with open(self.path, 'r') as f:
                self.servers = json.load(f)

    @staticmethod
    def msg2dict(msg):
        d = {}
        attachments = [attachment['url'] for attachment in msg.attachments if 'url' in attachment]
        d['attachments'] = ', '.join(attachments)
        return d

    def format_on_edit(self, before, after, conf, check_equal=True):
        bef_content = before.content
        aft_content = after.content
        if check_equal:
            if bef_content == aft_content:
                return

        user = before.author

        message = conf['message']
        d = self.msg2dict(before)
        d = slots2dict(user, d)
        for e in ['name', 'before', 'after']:
            d.pop(e, None)

        d['channel'] = after.channel.mention
        message = message.format(name=str(user), **d,
                                 before=bef_content, after=aft_content)

        return message

    @staticmethod
    def format_join_leave(member, conf):
        d = slots2dict(member)
        d.pop('user', None)
        message = conf['message'].format(user=str(member), **d)
        return message

    def format_on_delete(self, msg, conf):
        content = msg.content
        user = msg.author

        message = conf['message']
        d = self.msg2dict(msg)
        d = slots2dict(user, d)
        for e in ['name', 'message']:
            d.pop(e, None)

        d['channel'] = msg.channel.mention
        message = message.format(name=str(user), message=content, **d)
        return message

    @staticmethod
    def get_channel(s, server):
        matches = re.findall(r'(?!<#)*\d+(?=>)*', s)
        if matches:
            id = matches[0]
            channel = server.get_channel(id)
            return channel

    def add_color_to_json(self, name, serverid, roleid):
        colors = self.get_colors(serverid)
        colors[name] = roleid

        self.save_json()

    def delete_color_from_json(self, name, serverid):
        colors = self.get_colors(serverid)
        try:
            del colors[name]
        except Exception as e:
            print(e)

        self.save_json()

    @staticmethod
    def get_roles_from_ids(server_roles, *ids):
        roles = []
        for r in ids:
            try:
                int(r)
            except:
                continue

            r = discord.utils.find(lambda role: role.id == str(r), server_roles)
            if r:
                roles.append(r)

        return roles

    @staticmethod
    def get_users_from_ids(server, *ids):
        users = []
        for i in ids:
            try:
                int(i)
            except:
                continue

            user = server.get_member(i)
            if user:
                users.append(user)

        return users

    async def join_leave(self, ctx, channel, message, add_color, join=True):
        key = 'join' if join else 'leave'
        user = ctx.message.author
        channel_ = ctx.message.channel
        server = ctx.message.server
        if not user.permissions_in(channel_).manage_channels:
            return await self.bot.send_message(channel_, "You don't have manage channel permissions")

        chn = self.get_channel(channel, server)
        if chn is None:
            return await self.bot.send_message(channel_, 'Could not get channel %s' % channel)

        config = self.servers.get(server.id, {})
        config[key] = {'message': message, 'channel': chn.id,
                       'add_color': add_color}
        self.servers[server.id] = config
        self.save_json()
        config_ = json.dumps(config[key], ensure_ascii=False)
        await self.bot.say('Current {} message config ```json\n{}```'.format(key, config_), delete_after=120)

        return config

    async def message_edited(self, ctx, channel, message, key='on_edit'):
        user = ctx.message.author
        channel_ = ctx.message.channel
        server = ctx.message.server
        if not user.permissions_in(channel_).manage_channels:
            return await self.bot.send_message(channel_, "You don't have manage channel permissions")

        config = self.get_config(server.id)
        chn = self.get_channel(channel, server)
        if chn is None:
            return await self.bot.send_message(channel_,
                                               'Could not get channel %s' % channel)

        config[key] = {'message': message, 'channel': chn.id}
        self.save_json()
        config_ = json.dumps(config[key], ensure_ascii=False)
        await self.bot.send_message(channel_,
                                    '{} config ```json\n{}```'.format(key, config_))

        return config

    def save_json(self):
        def save():
            try:
                with open(self.path, 'w', encoding='utf-8') as f:
                    json.dump(self.servers, f, ensure_ascii=False, indent=4)
                    return True
            except:
                return False

        with self._lock:
            for i in range(2):
                if save():
                    return

    def get_config(self, serverid):
        conf = self.servers.get(serverid, None)
        if conf is None:
            conf = {}
            self.servers[serverid] = conf

        return conf

    def get_join(self, serverid):
        config = self.get_config(serverid)
        return config.get('join', None)

    def get_leave(self, serverid):
        config = self.get_config(serverid)
        return config.get('leave', None)

    def get_colors(self, serverid):
        config = self.servers.get(serverid, {})
        if serverid not in self.servers:
            self.servers[serverid] = config

        colors = config.get('colors', {})
        if 'colors' not in config:
            config['colors'] = colors

        return colors

    def get_mute_whitelist(self, serverid):
        config = self.get_config(serverid)
        if 'unmutable' not in config:
            config['unmutable'] = []

        return config.get('unmutable', [])

    def set_muted_role(self, serverid, roleid):
        conf = self.get_config(serverid)
        conf['muted_role'] = roleid

        self.save_json()

    def remove_removed_colors(self, serverid):
        server = self.bot.get_server(serverid)
        roles = server.roles
        colors = self.get_colors(serverid)

        removed = []

        for key, color in list(colors.items()):
            role = discord.utils.find(lambda r: r.id == color, roles)
            if role is None:
                self.delete_color_from_json(key, serverid)
                removed.append(key)

        return removed


class Management:
    def __init__(self, bot):
        self.bot = bot
        self.utils = ManagementHandler(bot)
        self.bot.management = self.utils

    async def on_message_delete(self, msg):
        if msg.author.bot or msg.channel.id == '336917918040326166':
            return

        conf = self.utils.get_config(msg.server.id).get('on_delete', None)
        if conf is None:
            return

        channel = msg.server.get_channel(conf['channel'])
        if channel is None:
            return

        message = self.utils.format_on_delete(msg, conf)
        message = split_string(message)
        for m in message:
            try:
                await self.bot.send_message(channel, m)
            except:
                await self.bot.send_message(self.bot.get_channel('252872751319089153'), '{} posted spam string'.format(msg.author))

    async def on_message_edit(self, before, after):
        if before.author.bot or before.channel.id == '336917918040326166':
            return

        conf = self.utils.get_config(before.server.id).get('on_edit', None)
        if not conf:
            return

        channel = before.server.get_channel(conf['channel'])
        if channel is None:
            return

        message = self.utils.format_on_edit(before, after, conf)
        if message is None:
            return

        message = split_string(message, maxlen=1960)
        for m in message:
            await self.bot.send_message(channel, m)

    async def on_member_join(self, member):
        server = member.server

        server_config = self.utils.get_config(server.id)
        if server_config is None:
            return

        conf = server_config.get('join', None)
        if conf is None:
            return

        channel = server.get_channel(conf['channel'])
        if channel is None:
            return

        message = self.utils.format_join_leave(member, conf)

        await self.bot.send_message(channel, message)

        if conf['add_color']:
            colors = server_config.get('colors', {})

            if colors and channel is not None:
                role = None
                for i in range(3):
                    color = choice(list(colors.values()))
                    roles = server.roles
                    role = list(filter(lambda r: r.id == color, roles))
                    if role:
                        break

                if role:
                    await self.bot.add_roles(member, role[0])

        if server.id == '217677285442977792':
            await self.bot._wants_to_be_noticed(member, server, remove=False)

    async def on_member_remove(self, member):
        server = member.server
        conf = self.utils.get_leave(server.id)
        if conf is None:
            return

        channel = server.get_channel(conf['channel'])
        if channel is None:
            return

        d = slots2dict(member)
        d.pop('user', None)
        message = conf['message'].format(user=str(member), **d)
        await self.bot.send_message(channel, message)

    @command(pass_context=True, required_perms=manage_server)
    async def leave_message(self, ctx, channel, message=None):
        if channel.lower() == 'off':
            self.utils.get_config(ctx.message.server.id).pop('leave', None)
            await self.bot.say('Removed on leave config')
            return

        if message is None:
            return await self.bot.say('You need to specify a message')

        old = self.utils.get_config(ctx.message.server.id).get('leave', {})
        conf = await self.utils.join_leave(ctx, channel, message, False, join=False)
        if not isinstance(conf, dict):
            return

        try:
            self.utils.format_on_delete(ctx.message, conf['leave'])<commit_msg>Add testing for role saving<commit_after>import json
import os
import re
from random import choice
from threading import Lock

import discord
from colour import Color
from discord.ext.commands import cooldown, BucketType

from bot.bot import command
from utils.utilities import slots2dict, split_string
import logging

logger = logging.getLogger('debug')
manage_server = discord.Permissions(48)  # Manage server and channels
color_perms = discord.Permissions(268435504)  # Manage server, channels and roles


class ManagementHandler:
    def __init__(self, bot):
        self.bot = bot
        self.servers = {}
        self.path = os.path.join(os.getcwd(), 'data', 'servers.json')
        self._lock = Lock()
        self.reload_config()

    def reload_config(self):
        if os.path.exists(self.path):
            with open(self.path, 'r') as f:
                self.servers = json.load(f)

    @staticmethod
    def msg2dict(msg):
        d = {}
        attachments = [attachment['url'] for attachment in msg.attachments if 'url' in attachment]
        d['attachments'] = ', '.join(attachments)
        return d

    def format_on_edit(self, before, after, conf, check_equal=True):
        bef_content = before.content
        aft_content = after.content
        if check_equal:
            if bef_content == aft_content:
                return

        user = before.author

        message = conf['message']
        d = self.msg2dict(before)
        d = slots2dict(user, d)
        for e in ['name', 'before', 'after']:
            d.pop(e, None)

        d['channel'] = after.channel.mention
        message = message.format(name=str(user), **d,
                                 before=bef_content, after=aft_content)

        return message

    @staticmethod
    def format_join_leave(member, conf):
        d = slots2dict(member)
        d.pop('user', None)
        message = conf['message'].format(user=str(member), **d)
        return message

    def format_on_delete(self, msg, conf):
        content = msg.content
        user = msg.author

        message = conf['message']
        d = self.msg2dict(msg)
        d = slots2dict(user, d)
        for e in ['name', 'message']:
            d.pop(e, None)

        d['channel'] = msg.channel.mention
        message = message.format(name=str(user), message=content, **d)
        return message

    @staticmethod
    def get_channel(s, server):
        matches = re.findall(r'(?!<#)*\d+(?=>)*', s)
        if matches:
            id = matches[0]
            channel = server.get_channel(id)
            return channel

    def add_color_to_json(self, name, serverid, roleid):
        colors = self.get_colors(serverid)
        colors[name] = roleid

        self.save_json()

    def delete_color_from_json(self, name, serverid):
        colors = self.get_colors(serverid)
        try:
            del colors[name]
        except Exception as e:
            print(e)

        self.save_json()

    @staticmethod
    def get_roles_from_ids(server_roles, *ids):
        roles = []
        for r in ids:
            try:
                int(r)
            except:
                continue

            r = discord.utils.find(lambda role: role.id == str(r), server_roles)
            if r:
                roles.append(r)

        return roles

    @staticmethod
    def get_users_from_ids(server, *ids):
        users = []
        for i in ids:
            try:
                int(i)
            except:
                continue

            user = server.get_member(i)
            if user:
                users.append(user)

        return users

    async def join_leave(self, ctx, channel, message, add_color, join=True):
        key = 'join' if join else 'leave'
        user = ctx.message.author
        channel_ = ctx.message.channel
        server = ctx.message.server
        if not user.permissions_in(channel_).manage_channels:
            return await self.bot.send_message(channel_, "You don't have manage channel permissions")

        chn = self.get_channel(channel, server)
        if chn is None:
            return await self.bot.send_message(channel_, 'Could not get channel %s' % channel)

        config = self.servers.get(server.id, {})
        config[key] = {'message': message, 'channel': chn.id,
                       'add_color': add_color}
        self.servers[server.id] = config
        self.save_json()
        config_ = json.dumps(config[key], ensure_ascii=False)
        await self.bot.say('Current {} message config ```json\n{}```'.format(key, config_), delete_after=120)

        return config

    async def message_edited(self, ctx, channel, message, key='on_edit'):
        user = ctx.message.author
        channel_ = ctx.message.channel
        server = ctx.message.server
        if not user.permissions_in(channel_).manage_channels:
            return await self.bot.send_message(channel_, "You don't have manage channel permissions")

        config = self.get_config(server.id)
        chn = self.get_channel(channel, server)
        if chn is None:
            return await self.bot.send_message(channel_,
                                               'Could not get channel %s' % channel)

        config[key] = {'message': message, 'channel': chn.id}
        self.save_json()
        config_ = json.dumps(config[key], ensure_ascii=False)
        await self.bot.send_message(channel_,
                                    '{} config ```json\n{}```'.format(key, config_))

        return config

    def save_json(self):
        def save():
            try:
                with open(self.path, 'w', encoding='utf-8') as f:
                    json.dump(self.servers, f, ensure_ascii=False, indent=4)
                    return True
            except:
                return False

        with self._lock:
            for i in range(2):
                if save():
                    return

    def get_config(self, serverid):
        conf = self.servers.get(serverid, None)
        if conf is None:
            conf = {}
            self.servers[serverid] = conf

        return conf

    def get_join(self, serverid):
        config = self.get_config(serverid)
        return config.get('join', None)

    def get_leave(self, serverid):
        config = self.get_config(serverid)
        return config.get('leave', None)

    def get_colors(self, serverid):
        config = self.servers.get(serverid, {})
        if serverid not in self.servers:
            self.servers[serverid] = config

        colors = config.get('colors', {})
        if 'colors' not in config:
            config['colors'] = colors

        return colors

    def get_mute_whitelist(self, serverid):
        config = self.get_config(serverid)
        if 'unmutable' not in config:
            config['unmutable'] = []

        return config.get('unmutable', [])

    def set_muted_role(self, serverid, roleid):
        conf = self.get_config(serverid)
        conf['muted_role'] = roleid

        self.save_json()

    def remove_removed_colors(self, serverid):
        server = self.bot.get_server(serverid)
        roles = server.roles
        colors = self.get_colors(serverid)

        removed = []

        for key, color in list(colors.items()):
            role = discord.utils.find(lambda r: r.id == color, roles)
            if role is None:
                self.delete_color_from_json(key, serverid)
                removed.append(key)

        return removed


class Management:
    def __init__(self, bot):
        self.bot = bot
        self.utils = ManagementHandler(bot)
        self.bot.management = self.utils

    async def on_message_delete(self, msg):
        if msg.author.bot or msg.channel.id == '336917918040326166':
            return

        conf = self.utils.get_config(msg.server.id).get('on_delete', None)
        if conf is None:
            return

        channel = msg.server.get_channel(conf['channel'])
        if channel is None:
            return

        message = self.utils.format_on_delete(msg, conf)
        message = split_string(message)
        for m in message:
            try:
                await self.bot.send_message(channel, m)
            except:
                await self.bot.send_message(self.bot.get_channel('252872751319089153'), '{} posted spam string'.format(msg.author))

    async def on_message_edit(self, before, after):
        if before.author.bot or before.channel.id == '336917918040326166':
            return

        conf = self.utils.get_config(before.server.id).get('on_edit', None)
        if not conf:
            return

        channel = before.server.get_channel(conf['channel'])
        if channel is None:
            return

        message = self.utils.format_on_edit(before, after, conf)
        if message is None:
            return

        message = split_string(message, maxlen=1960)
        for m in message:
            await self.bot.send_message(channel, m)

    async def on_member_join(self, member):
        server = member.server

        server_config = self.utils.get_config(server.id)
        if server_config is None:
            return

        conf = server_config.get('join', None)
        if conf is None:
            return

        channel = server.get_channel(conf['channel'])
        if channel is None:
            return

        message = self.utils.format_join_leave(member, conf)

        await self.bot.send_message(channel, message)

        if conf['add_color']:
            colors = server_config.get('colors', {})

            if colors and channel is not None:
                role = None
                for i in range(3):
                    color = choice(list(colors.values()))
                    roles = server.roles
                    role = list(filter(lambda r: r.id == color, roles))
                    if role:
                        break

                if role:
                    await self.bot.add_roles(member, role[0])

        if server.id == '217677285442977792':
            await self.bot._wants_to_be_noticed(member, server, remove=False)

    async def on_member_remove(self, member):
        server = member.server
        conf = self.utils.get_leave(server.id)
        if conf is None:
            return

        channel = server.get_channel(conf['channel'])
        if channel is None:
            return

        d = slots2dict(member)
        d.pop('user', None)
        logger.debug('Member roles before leaving' + ', '.join(member.roles))
        message = conf['message'].format(user=str(member), **d)
        await self.bot.send_message(channel, message)

    @command(pass_context=True, required_perms=manage_server)
    async def leave_message(self, ctx, channel, message=None):
        if channel.lower() == 'off':
            self.utils.get_config(ctx.message.server.id).pop('leave', None)
            await self.bot.say('Removed on leave config')
            return

        if message is None:
            return await self.bot.say('You need to specify a message')

        old = self.utils.get_config(ctx.message.server.id).get('leave', {})
        conf = await self.utils.join_leave(ctx, channel, message, False, join=False)
        if not isinstance(conf, dict):
            return

        try:
            self.utils.format_on_delete(ctx.message, conf['leave'])
{'fraction_non_alphanumeric': 0.10533084974277097, 'fraction_numerical': 0.009224764945893206, 'mean_word_length': 1.9619072638907133, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6673744', 'n_tokens_mistral': 6628, 'n_tokens_neox': 6345, 'n_words': 1574}