From 1027a07ea183002ef74ec81a123bfdc707c33a25 Mon Sep 17 00:00:00 2001 From: Profitroll Date: Sat, 5 Feb 2022 02:31:32 +0200 Subject: [PATCH] Initial Bot --- config.json | 8 ++ functions.py | 200 ++++++++++++++++++++++++++++++++++++ locale/en.json | 30 ++++++ locale/ru.json | 30 ++++++ locale/uk.json | 30 ++++++ yusarin.py | 272 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 570 insertions(+) create mode 100644 config.json create mode 100644 functions.py create mode 100644 locale/en.json create mode 100644 locale/ru.json create mode 100644 locale/uk.json create mode 100644 yusarin.py diff --git a/config.json b/config.json new file mode 100644 index 0000000..af5dbcc --- /dev/null +++ b/config.json @@ -0,0 +1,8 @@ +{ + "owner": SET-OWNER-ID, + "bot_name": "Yusa Nishimori", + "bot_token": "SET-BOT-TOKEN", + "bot_prefix": "$", + "bot_locale": "en", + "bot_activity": "How-Low-Hello - Keep on Burnin'" +} \ No newline at end of file diff --git a/functions.py b/functions.py new file mode 100644 index 0000000..16ad724 --- /dev/null +++ b/functions.py @@ -0,0 +1,200 @@ +import discord, json, os, shutil +from datetime import datetime +from pathlib import Path + +path = Path(__file__).resolve().parent + +log_size = 512 + +# Check latest log size +def checkSize(): + global path + path = str(path) + + try: + if not os.path.isdir(f"{path}/logs"): + os.mkdir(f"{path}/logs") + + log = os.stat(path + '/logs/latest.log') + global log_size + + if (log.st_size / 1024) > log_size: + with open(path + '/logs/latest.log', 'rb') as f_in: + with gzip.open(f'{path}/logs/{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz', 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + print(f'Copied {path}/logs/{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz') + open(path + '/logs/latest.log', 'w').close() + except FileNotFoundError: + print('Not found') + pass + +# Append string to log +def appendLog(message, guild="none"): + global path + + message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] [{str(guild)}] {message}' + + print(message_formatted) + + checkSize() + + log = open(path + '/logs/latest.log', 'a') + log.write(f'{message_formatted}\n') + log.close() + +def saveJson(value, filename): + with open(filename, 'w', encoding="utf-8") as f: + json.dump(value, f, indent=4, ensure_ascii=False) + f.close() + +def loadJson(filename): + try: + with open(filename, 'r', encoding="utf-8") as json_file: + output = json.load(json_file) + json_file.close() + except Exception as exp: + output = {} + return output + +def getMsg(string): + global path + config = loadJson("config.json") + try: + locale = loadJson(f'{path}/locale/{config["bot_locale"]}.json') + return locale["messages"][string] + except: + return f"Could not get locale string {string}" + +def guildConfGet(guild, variable): + global path + config = loadJson(f"{path}/guilds/{str(guild)}/config.json") + try: + return config[variable] + except: + return None + +def guildConfSet(guild, variable, value): + global path + config = loadJson(f"{path}/guilds/{str(guild)}/config.json") + config[variable] = value + try: + saveJson(config, f"{path}/guilds/{str(guild)}/config.json") + except: + os.mkdir(f"{path}/guilds/{str(guild)}") + os.mkdir(f"{path}/guilds/{str(guild)}/channels") + saveJson(config, f"{path}/guilds/{str(guild)}/config.json") + +def guildConfReset(guild, variable): + global path + config = loadJson(f"{path}/guilds/{str(guild)}/config.json") + del config[variable] + try: + saveJson(config, f"{path}/guilds/{str(guild)}/config.json") + except: + os.mkdir(f"{path}/guilds/{str(guild)}") + os.mkdir(f"{path}/guilds/{str(guild)}/channels") + saveJson(config, f"{path}/guilds/{str(guild)}/config.json") + +def isUserVoice(vc): + global path + try: + channels_list = os.listdir(f"{path}/guilds/{str(vc.guild.id)}/channels/") + if f"{str(vc.id)}.json" in channels_list: + return True + else: + return False + except: + return False + +async def removeUserVoice(vc): + global path + channels_list = os.listdir(f"{path}/guilds/{str(vc.guild.id)}/channels/") + if f"{vc.id}.json" in channels_list: + vc_file = f"{path}/guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json" + vc_conf = loadJson(vc_file) + + needed_channel = discord.utils.get(vc.guild.channels, id=vc.id) + nomic_channel = discord.utils.get(vc.guild.channels, id=vc_conf["nomic"]) + + os.remove(vc_file) + + await needed_channel.delete() + appendLog(f"Removed voice channel {str(needed_channel.id)} of user {str(vc_conf['ownerid'])}", guild=vc.guild.id) + await nomic_channel.delete() + appendLog(f"Removed nomic channel {str(nomic_channel.id)} of channel {str(needed_channel.id)}", guild=vc.guild.id) + else: + return + +async def createUserVoice(vc, category, member): + global path + chan = {} + overwrites_channel = { + vc.guild.default_role: discord.PermissionOverwrite(view_channel=True), + vc.guild.me: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True), + member: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True) + } + overwrites_nomic = { + vc.guild.default_role: discord.PermissionOverwrite(view_channel=False, read_messages=False), + vc.guild.me: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True), + member: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True) + } + created_channel = await vc.guild.create_voice_channel(f"Канал {member.name}", category=category, overwrites=overwrites_channel) + appendLog(f"Created voice channel {str(created_channel.id)} for user {str(member.id)}", guild=vc.guild.id) + if not os.path.isdir(f"{path}/guilds/{str(created_channel.guild.id)}/channels"): + os.mkdir(f"{path}/guilds/{str(created_channel.guild.id)}/channels") + vc_file = f"{path}/guilds/{str(created_channel.guild.id)}/channels/{str(created_channel.id)}.json" + chan["ownerid"] = member.id + saveJson(chan, vc_file) + nomic_channel = await vc.guild.create_text_channel(f"без-микро-{str(created_channel.id)}", category=category, overwrites=overwrites_nomic, topic=f"Тектовый канал для комуникации без микрофона\nID голосовой комнаты: {str(created_channel.id)}") + appendLog(f"Created nomic channel {str(nomic_channel.id)} for channel {str(created_channel.id)}", guild=vc.guild.id) + chan["nomic"] = nomic_channel.id + saveJson(chan, vc_file) + return created_channel + +def isVoiceOfUser(vc, member): + global path + vc_file = f"{path}/guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json" + vc_conf = loadJson(vc_file) + if vc_conf["ownerid"] == member.id: + return True + else: + return False + +async def changeNomicPerms(mode, vc, member): + global path + vc_file = f"{path}/guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json" + vc_conf = loadJson(vc_file) + nomic_channel = discord.utils.get(vc.guild.channels, id=vc_conf["nomic"]) + if mode == "deny": + await nomic_channel.set_permissions(member, view_channel=False) + else: + await nomic_channel.set_permissions(member, view_channel=True) + +async def guildConfigured(guild): + + output = {} + config = loadJson("config.json") + + for kind in ["channel", "category", "prefix"]: + if guildConfGet(guild.id, kind) is not None: + try: + if kind == "channel": + guild_object = discord.utils.get(guild.channels, id=guildConfGet(guild.id, kind)) + output[kind] = getMsg("configured_"+kind).format(guild_object.name) + elif kind == "category": + guild_object = discord.utils.get(guild.categories, id=guildConfGet(guild.id, kind)) + output[kind] = getMsg("configured_"+kind).format(guild_object.name) + elif kind == "prefix": + output[kind] = getMsg("configured_"+kind).format(guildConfGet(guild.id, kind)) + except Exception as exp: + if kind == "prefix": + output[kind] = getMsg("unconfigured_"+kind).format(config["bot_prefix"]) + else: + output[kind] = getMsg("unconfigured_"+kind) + else: + if kind == "prefix": + output[kind] = getMsg("unconfigured_"+kind).format(config["bot_prefix"]) + else: + output[kind] = getMsg("unconfigured_"+kind) + + return getMsg("server_config").format(output["prefix"], output["channel"], output["category"]) diff --git a/locale/en.json b/locale/en.json new file mode 100644 index 0000000..ffcbfee --- /dev/null +++ b/locale/en.json @@ -0,0 +1,30 @@ +{ + "messages": { + "shutdown": "Shutting down...", + "help": "**List of command:**\n{0}• Set parent channel: `{1}channel CHANNEL-ID`\n• Set parent category: `{2}category CATEGORY-ID`\n• Set commands prefix: `{3}prefix SYMBOL`\n\nTo reset channel/category/prefix use `reset` as argument\n\nPlease note that channel/category name ≠ ID of channel/category", + "help_owner": "• Turn off the bot: `{0}shutdown`\n", + "command_in_dm": "Commands can only be executed on the server", + "command_forbidden": "To set up private channels, you need to have **Administrator** permission on the server", + "usage_channel": "Correct usage: `{0}channel CHANNEL-ID`\nPlease note that name of channel ≠ ID of channel.\nFind out more about this here: https://support.discord.com/hc/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", + "usage_category": "Correct usage: `{0}category CATEGORY-ID`\nPlease note that name of category ≠ ID of category.\nFind out more about it here: https://support.discord.com/hc/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", + "usage_prefix": "Correct usage: `{0}prefix SYMBOL`", + "result_channel": "Voice channel **{0}** is now set as parent", + "result_category": "Category **{0}** is now ser as parent", + "result_prefix": "Command prefix **{0}** is now set as primary for this server", + "warn_channel": "⚠ Parent channel is not set!\nFor the bot to work, you need to set parent channel: `{0}channel CHANNEL-ID`", + "warn_category": "⚠ Parent category is not set!\nFor the bot to work, you need to set parent category: `{0}category CATEGORY-ID`", + "reset_channel": "Parent voice channel has been reset", + "reset_category": "Parent category has been reset", + "reset_prefix": "Commands prefix has been reset and now is `{0}`", + "none_channel": "Parent voice channel is not set", + "none_category": "Parent category is not set", + "none_prefix": "Commands prefix is not set, using default prefix `{0}`", + "server_config": "**Server status:**\n{0}\n{1}\n{2}\n\n", + "unconfigured_prefix": "ℹ Commands prefix: `{0}`", + "unconfigured_channel": "⚠ Parent channel", + "unconfigured_category": "⚠ Parent category", + "configured_prefix": "ℹ Commands prefix: `{0}`", + "configured_channel": "☑ Parent channel: **{0}**", + "configured_category": "☑ Parent category: **{0}**" + } +} \ No newline at end of file diff --git a/locale/ru.json b/locale/ru.json new file mode 100644 index 0000000..1b56b14 --- /dev/null +++ b/locale/ru.json @@ -0,0 +1,30 @@ +{ + "messages": { + "shutdown": "Выключаюсь...", + "help": "**Список команд:**\n{0}• Установить родительский канал: `{1}channel ID-КАНАЛА`\n• Установить родительскую категорию: `{2}category ID-КАТЕГОРИИ`\n• Установить префикс команд: `{3}prefix СИМВОЛ`\n\nДля сброса канала/категории/префикса используйте `reset` как аргумент\n\nОбратите внимание, что имя канала/категории ≠ ID канала/категории", + "help_owner": "• Выключиться: `{0}shutdown`\n", + "command_in_dm": "Команды можно исполнять только находясь на сервере", + "command_forbidden": "Для настройки приватных каналов нужно иметь право **Администратор** на сервере", + "usage_channel": "Правильное использование: `{0}channel ID-КАНАЛА`\nОбратите внимание, что имя канала ≠ ID канала.\nУзнайте больше об этом тут: https://support.discord.com/hc/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", + "usage_category": "Правильное использование: `{0}category ID-КАТЕГОРИИ`\nОбратите внимание, что имя категории ≠ ID категории.\nУзнайте больше об этом тут: https://support.discord.com/hc/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", + "usage_prefix": "Правильное использование: `{0}prefix СИМВОЛ`", + "result_channel": "Голосовой канал **{0}** был установлен как родительский", + "result_category": "Категория **{0}** была установлена как родительская", + "result_prefix": "Префикс **{0}** был установлен как основной для этого сервера", + "warn_channel": "⚠ Родительский канал не установлен!\nДля работы бота нужно установить канал: `{0}channel ID-КАНАЛА`", + "warn_category": "⚠ Родительская категория не установлена!\nДля работы бота нужно установить категорию: `{0}category ID-КАТЕГОРИИ`", + "reset_channel": "Родительский голосовой канал был сброшен", + "reset_category": "Родительская категория была сброшена", + "reset_prefix": "Прификс команд был сброшен, теперь это `{0}`", + "none_channel": "Родительский голосовой канал не был задан", + "none_category": "Родительская категория не была задана", + "none_prefix": "Префикс команд не был задан, используется стандартный `{0}`", + "server_config": "**Статус сервера:**\n{0}\n{1}\n{2}\n\n", + "unconfigured_prefix": "ℹ Префикс команд: `{0}`", + "unconfigured_channel": "⚠ Родительский канал", + "unconfigured_category": "⚠ Родительская категория", + "configured_prefix": "ℹ Префикс команд: `{0}`", + "configured_channel": "☑ Родительский канал: **{0}**", + "configured_category": "☑ Родительская категория: **{0}**" + } +} \ No newline at end of file diff --git a/locale/uk.json b/locale/uk.json new file mode 100644 index 0000000..be05551 --- /dev/null +++ b/locale/uk.json @@ -0,0 +1,30 @@ +{ + "messages": { + "shutdown": "Вимикаюсь...", + "help": "**Перелік команд:**\n{0}• Установить родительский канал: `{1}channel ID-КАНАЛА`\n• Установить родительскую категорию: `{2}category ID-КАТЕГОРИИ`\n• Встановити префікс команд: `{3}prefix СИМВОЛ`\n\nДля скидання каналу/ категорії/префіксу використовуйте `reset` як аргумент\n\nЗверніть увагу, що ім'я каналу/категорії ≠ ID каналу/категорії", + "help_owner": "• Выключиться: `{0}shutdown`\n", + "command_in_dm": "Команды можно исполнять только находясь на сервере", + "command_forbidden": "Для настройки приватных каналов нужно иметь право **Администратор** на сервере", + "usage_channel": "Правильное использование: `{0}channel ID-КАНАЛА`\nОбратите внимание, что имя канала ≠ ID канала.\nУзнайте больше об этом тут: https://support.discord.com/hc/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", + "usage_category": "Правильное использование: `{0}category ID-КАТЕГОРИИ`\nОбратите внимание, что имя категории ≠ ID категории.\nУзнайте больше об этом тут: https://support.discord.com/hc/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", + "usage_prefix": "Правильное использование: `{0}prefix СИМВОЛ`", + "result_channel": "Голосовой канал **{0}** был установлен как родительский", + "result_category": "Категория **{0}** была установлена как родительская", + "result_prefix": "Префикс **{0}** был установлен как основной для этого сервера", + "warn_channel": "⚠ Родительский канал не установлен!\nДля работы бота нужно установить канал: `{0}channel ID-КАНАЛА`", + "warn_category": "⚠ Родительская категория не установлена!\nДля работы бота нужно установить категорию: `{0}category ID-КАТЕГОРИИ`", + "reset_channel": "Родительский голосовой канал был сброшен", + "reset_category": "Родительская категория была сброшена", + "reset_prefix": "Прификс команд был сброшен, теперь это `{0}`", + "none_channel": "Родительский голосовой канал не был задан", + "none_category": "Родительская категория не была задана", + "none_prefix": "Префикс команд не был задан, используется стандартный `{0}`", + "server_config": "**Статус сервера:**\n{0}\n{1}\n{2}\n\n", + "unconfigured_prefix": "ℹ Префикс команд: `{0}`", + "unconfigured_channel": "⚠ Родительский канал", + "unconfigured_category": "⚠ Родительская категория", + "configured_prefix": "ℹ Префикс команд: `{0}`", + "configured_channel": "☑ Родительский канал: **{0}**", + "configured_category": "☑ Родительская категория: **{0}**" + } +} \ No newline at end of file diff --git a/yusarin.py b/yusarin.py new file mode 100644 index 0000000..e339823 --- /dev/null +++ b/yusarin.py @@ -0,0 +1,272 @@ +import discord, json, os +from functions import * +#from discord_slash import SlashCommand, SlashContext + +pid = os.getpid() + +intents = discord.Intents().all() +client = discord.Client(intents=intents) +#slash = SlashCommand(client) + +@client.event +async def on_ready(): + + print('Logged in as {0.user}'.format(client)) + + config = loadJson("config.json") + + await client.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=config["bot_activity"])) + + global path + + if not os.path.isdir(f"{path}/guilds/"): + os.mkdir(f"{path}/guilds") + + guilds_list = os.listdir(f"{path}/guilds/") + + for guild in guilds_list: + + guild_object = client.get_guild(int(guild)) + + if os.path.isdir(f"{path}/guilds/{guild}/channels"): + + channels_list = os.listdir(f"{path}/guilds/{guild}/channels/") + + for channel in channels_list: + + channel_id = channel[:-5] + + try: + + selected_channel = discord.utils.get(guild_object.voice_channels, id=int(channel_id)) + + channel_owner = loadJson(f"{path}/guilds/{guild}/channels/{channel}")["ownerid"] + + remove_channel = True + + for member in selected_channel.members: + if member.id == channel_owner: + remove_channel = False + + if remove_channel: + await removeUserVoice(selected_channel) + + except: + + os.remove(f"{path}/guilds/{guild}/channels/{channel_id}.json") + + +@client.event +async def on_voice_state_update(member, before, after): + + config = loadJson("config.json") + + vc_from = before.channel + vc_to = after.channel + + # If user left vc + if vc_to is None: + if isUserVoice(vc_from): + if isVoiceOfUser(vc_from, member): + await removeUserVoice(vc_from) + return + else: + await changeNomicPerms("deny", vc_from, member) + + # If user joined vc + else: + if isUserVoice(vc_from): + if isVoiceOfUser(vc_from, member): + await removeUserVoice(vc_from) + else: + await changeNomicPerms("deny", vc_from, member) + if isUserVoice(vc_to): + await changeNomicPerms("allow", vc_to, member) + if vc_to.id == guildConfGet(vc_to.guild.id, "channel"): + if guildConfGet(vc_to.guild.id, "category") is not None: + voice_chan = await createUserVoice(vc_to, discord.utils.get(vc_to.guild.categories, id=guildConfGet(vc_to.guild.id, "category")), member) + try: + await member.move_to(voice_chan) + except: + await removeUserVoice(voice_chan) + else: + appendLog(f"Category for guild {str(vc_to.guild.id)} is not set", guild=vc_to.guild.id) + +# ========================================================================================== + +@client.event +async def on_message(message): + + config = loadJson("config.json") + + if message.guild is not None: + try: + prefix = guildConfGet(message.guild.id, "prefix") + if prefix is None: + prefix = config["bot_prefix"] + except Exception as exp: + print(exp) + prefix = config["bot_prefix"] + else: + prefix = config["bot_prefix"] + + if message.author == client.user: + + return + + if message.content in [f"{prefix}reboot", f"{prefix}restart", f"{prefix}shutdown", f"{prefix}die"]: + + if message.author.id == config["owner"]: + + await message.channel.send(getMsg("shutdown")) + os.system(f"kill -9 {str(pid)}") + + else: + + return + + elif message.content.startswith(f"{prefix}channel"): + + fullcmd = message.content.split() + + if message.guild is not None: + + if message.author.guild_permissions.administrator: + + try: + + if fullcmd[1] == "reset": + + if guildConfGet(message.guild.id, "channel") is not None: + + guildConfReset(message.guild.id, "channel") + + await message.channel.send(getMsg("reset_channel")) + + else: + + await message.channel.send(getMsg("none_channel")) + + else: + + selected_channel = discord.utils.get(message.guild.channels, id=int(fullcmd[1])) + + guildConfSet(message.guild.id, "channel", int(fullcmd[1])) + + await message.channel.send(getMsg("result_channel").format(selected_channel.name)) + + if guildConfGet(message.guild.id, "category") is None: + + await message.channel.send(getMsg("warn_category").format(prefix)) + + except Exception as exp: + + #print(exp) + + await message.channel.send(getMsg("usage_channel").format(prefix)) + + else: + + await message.channel.send(getMsg("command_forbidden")) + + else: + await message.channel.send(getMsg("command_in_dm")) + + elif message.content.startswith(f"{prefix}category"): + + fullcmd = message.content.split() + + if message.guild is not None: + + if message.author.guild_permissions.administrator: + + try: + + if fullcmd[1] == "reset": + + if guildConfGet(message.guild.id, "category") is not None: + + guildConfReset(message.guild.id, "category") + + await message.channel.send(getMsg("reset_category")) + + else: + + await message.channel.send(getMsg("none_category")) + + else: + + selected_category = discord.utils.get(message.guild.channels, id=int(fullcmd[1])) + + guildConfSet(message.guild.id, "category", int(fullcmd[1])) + + await message.channel.send(getMsg("result_category").format(selected_category.name)) + + if guildConfGet(message.guild.id, "channel") is None: + + await message.channel.send(getMsg("warn_channel").format(prefix)) + + except Exception as exp: + + #print(exp) + + await message.channel.send(getMsg("usage_category").format(prefix)) + + else: + + await message.channel.send(getMsg("command_forbidden")) + + else: + await message.channel.send(getMsg("command_in_dm")) + + elif message.content.startswith(f"{prefix}prefix"): + + fullcmd = message.content.split() + + if message.guild is not None: + + if message.author.guild_permissions.administrator: + + try: + + if fullcmd[1] == "reset": + + if guildConfGet(message.guild.id, "prefix") is not None: + + guildConfReset(message.guild.id, "prefix") + + await message.channel.send(getMsg("reset_prefix").format(config["bot_prefix"])) + + else: + + await message.channel.send(getMsg("none_prefix").format(prefix)) + + else: + + guildConfSet(message.guild.id, "prefix", fullcmd[1]) + + await message.channel.send(getMsg("result_prefix").format(fullcmd[1])) + + except: + + await message.channel.send(getMsg("usage_prefix").format(prefix)) + + else: + await message.channel.send(getMsg("command_forbidden")) + + else: + await message.channel.send(getMsg("command_in_dm")) + + elif message.content.startswith(f"{prefix}help"): + if message.author.id == config["owner"]: + if message.guild is not None: + await message.channel.send(await guildConfigured(message.guild) + getMsg("help").format(getMsg("help_owner").format(prefix), prefix, prefix, prefix, prefix)) + else: + await message.channel.send(getMsg("help").format(getMsg("help_owner").format(prefix), prefix, prefix, prefix, prefix)) + else: + if message.guild is not None: + await message.channel.send(await guildConfigured(message.guild) + getMsg("help").format("", prefix, prefix, prefix)) + else: + await message.channel.send(getMsg("help").format("", prefix, prefix, prefix)) + +client.run(loadJson("config.json")["bot_token"]) \ No newline at end of file