From 12c87b99d300ee607a7a9a0da9d4fe01e7b01faf Mon Sep 17 00:00:00 2001 From: Sweetbread Date: Wed, 3 May 2023 19:53:01 +0300 Subject: [PATCH] Init commit --- .gitignore | 5 + FFC.py | 91 ++ README.md | 1 + XOR.py | 49 + __init__.py | 0 bot.py | 861 ++++++++++++++++++ cogs/SSC.py | 120 +++ cogs/_colors.py | 15 + cogs/activity_counter.py | 128 +++ cogs/bomber.py | 34 + cogs/bridge.py | 85 ++ cogs/checks.py | 31 + cogs/demotivator.py | 28 + cogs/direct_messages.py | 62 ++ cogs/economic.py | 537 +++++++++++ cogs/emojis.py | 7 + cogs/error_handler.py | 38 + cogs/errors.py | 53 ++ cogs/file_ext.py | 57 ++ cogs/japanese.py | 179 ++++ cogs/logs.py | 155 ++++ cogs/moderation.py | 65 ++ cogs/music.py | 129 +++ cogs/music_syn.py | 45 + cogs/private_channels.py | 106 +++ cogs/profile.py | 3 + cogs/role_panel.py | 62 ++ .../__pycache__/server.cpython-39.pyc | Bin 0 -> 2356 bytes cogs/settings/server.py | 97 ++ cogs/slashes.py | 94 ++ cogs/stickers.py | 53 ++ cogs/talk_module.py | 212 +++++ cogs/tamagochi.py | 9 + cogs/timeble.py | 63 ++ cogs/translate.py | 30 + cogs/translation.py | 9 + cogs/translator.py | 32 + config.json | 13 + dark.mplstyle | 17 + logs.py | 107 +++ prefixes.json | 1 + pyrightconfig.json | 4 + speak_data.json | 1 + test_db.py | 51 ++ translations.json | 151 +++ 45 files changed, 3890 insertions(+) create mode 100644 .gitignore create mode 100755 FFC.py create mode 100755 README.md create mode 100755 XOR.py create mode 100644 __init__.py create mode 100755 bot.py create mode 100755 cogs/SSC.py create mode 100755 cogs/_colors.py create mode 100644 cogs/activity_counter.py create mode 100755 cogs/bomber.py create mode 100644 cogs/bridge.py create mode 100755 cogs/checks.py create mode 100644 cogs/demotivator.py create mode 100755 cogs/direct_messages.py create mode 100755 cogs/economic.py create mode 100755 cogs/emojis.py create mode 100644 cogs/error_handler.py create mode 100644 cogs/errors.py create mode 100644 cogs/file_ext.py create mode 100644 cogs/japanese.py create mode 100755 cogs/logs.py create mode 100755 cogs/moderation.py create mode 100644 cogs/music.py create mode 100644 cogs/music_syn.py create mode 100755 cogs/private_channels.py create mode 100755 cogs/profile.py create mode 100755 cogs/role_panel.py create mode 100644 cogs/settings/__pycache__/server.cpython-39.pyc create mode 100644 cogs/settings/server.py create mode 100644 cogs/slashes.py create mode 100644 cogs/stickers.py create mode 100755 cogs/talk_module.py create mode 100644 cogs/tamagochi.py create mode 100644 cogs/timeble.py create mode 100755 cogs/translate.py create mode 100644 cogs/translation.py create mode 100644 cogs/translator.py create mode 100755 config.json create mode 100644 dark.mplstyle create mode 100755 logs.py create mode 100755 prefixes.json create mode 100644 pyrightconfig.json create mode 100755 speak_data.json create mode 100755 test_db.py create mode 100644 translations.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..336d52f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +Stickers +Cat.log +test.* +cogs/__pycache__ +yarxi.db diff --git a/FFC.py b/FFC.py new file mode 100755 index 0000000..6e788ee --- /dev/null +++ b/FFC.py @@ -0,0 +1,91 @@ +from PIL import Image, ImageDraw # Подключим необходимые библиотеки. + + +def write(ifile, ofile, text): + secret = '' + + for char in text: + ccode = str(bin(ord(char)))[2:] + while len(ccode) < 16: + ccode = '0' + ccode + secret += ccode + secret += '0000000000000000' + + image = Image.open(ifile) + draw = ImageDraw.Draw(image) + width, height = image.size + pix = image.load() + + dindex = 0 + + for w in range(width): + for h in range(height): + + red = pix[w, h][0] + green = pix[w, h][1] + blue = pix[w, h][2] + + try: + if blue % 2 != int(secret[dindex]): + blue += 1 + if blue > 255: + blue -= 2 + dindex += 1 + except IndexError: + if blue % 2 != 0: + blue += 1 + if blue > 255: + blue -= 2 + + draw.point((w, h), (red, green, blue)) + fformat = 'PNG' + + if ofile[-3].endswith('png'): + fformat = 'PNG' + elif ofile[-3].endswith('jpg'): + fformat = 'JPEG' + + image.save(ofile, fformat) + + + +def read(file): + image = Image.open(file) + width = image.size[0] + height = image.size[1] + pix = image.load() + + solve = '' + out = '' + + for w in range(width): + for h in range(height): + blue = pix[w, h][2] + solve += str(blue % 2) + + solve = list(solve) + + i = 0 + while len(solve) >= 16: + char = ''.join(solve[:16]) + char = int(char, 2) + if char == 0: + break + out += chr(char) + + del solve[:16] + + i += 1 + + return out + + +if __name__ == '__main__': + choose = input('Прочесть или записать? [r/w] ') + + if choose == 'w': + text = input('Введи текст для зашифроки: ') + write('unknown.png', 'secret.png', text) + + elif choose == 'r': + print(read('secret.png')) diff --git a/README.md b/README.md new file mode 100755 index 0000000..afec4f8 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +"This is the KOTEIKA. Just a regular CAT" diff --git a/XOR.py b/XOR.py new file mode 100755 index 0000000..6f828ea --- /dev/null +++ b/XOR.py @@ -0,0 +1,49 @@ +def char_encode(text, key): + itext = '' + ikey = '' + out = '' + + for char in text: + char = str(bin(ord(char))) + char = char.replace('0b', '') + while len(char) < 16: + char = '0' + char + itext += char + + for char in key: + char = str(bin(ord(char))) + char = char.replace('0b', '') + while len(char) < 16: + char = '0' + char + ikey += char + + if len(itext) > len(ikey): + tmp = ikey + ikey = tmp * int(len(itext) / len(tmp)) + ikey += tmp[:(len(itext) % len(tmp))] + + for i in range(len(itext)): + a = itext[i] + b = ikey[i] + + if a == b: + out += '0' + else: + out += '1' + tmp = list(out) + out = '' + + while len(tmp) >= 16: + char = ''.join(tmp[:16]) + char = int(char, 2) + out += chr(char) + + del tmp[:16] + + return out + + +if __name__ == '__main__': + cat = char_encode('123123', 'кот') + print(cat) + print(char_encode(cat, 'кот')) diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bot.py b/bot.py new file mode 100755 index 0000000..372662c --- /dev/null +++ b/bot.py @@ -0,0 +1,861 @@ +# -*- coding: utf-8 -*- + +import json +import traceback +from json import load, dump +from os import listdir, getenv +from os.path import isfile, join, getsize + +import XOR + +import discord +from discord.utils import get +from discord.ext import commands, tasks +from discord import app_commands + +from random import randint, shuffle, choice, seed +from time import time +from inspect import _empty +from pymongo import MongoClient +from loguru import logger +from discord.ext.commands import HybridCommand + +import re +import os +import ast +import FFC +import nekos +import typing +import asyncio +import requests +import subprocess +import Levenshtein +import DiscordUtils +from cogs.emojis import * +from cogs.checks import * +from cogs.translate import * +from cogs.music import * +TOKEN = getenv('NATSUKO_TOKEN') +my_id = 710089471835504672 + +janres = ['feet', 'yuri', 'trap', 'futanari', 'hololewd', 'lewdkemo', 'solog', 'feetg', 'cum', 'erokemo', 'les', + 'wallpaper', 'lewdk', 'ngif', 'tickle', 'lewd', 'feed', 'gecg', 'eroyuri', 'eron', 'cum_jpg', 'bj', + 'nsfw_neko_gif', 'solo', 'kemonomimi', 'nsfw_avatar', 'gasm', 'poke', 'anal', 'slap', 'hentai', 'avatar', + 'erofeet', 'holo', 'keta', 'blowjob', 'pussy', 'tits', 'holoero', 'lizard', 'pussy_jpg', 'pwankg', 'classic', + 'kuni', 'waifu', 'pat', '8ball', 'kiss', 'femdom', 'neko', 'spank', 'cuddle', 'erok', 'fox_girl', 'boobs', + 'random_hentai_gif', 'hug', 'ero', 'smug', 'goose', 'baka', 'woof'] + +db = MongoClient('localhost', 27017).Koteika + +if os.path.exists('prefixes.json'): + with open('prefixes.json', 'r') as f: + prefs = load(f) +else: + prefs = {"default": "*"} + + +def NFIF(path): + return len([f for f in os.listdir(path) + if os.path.isfile(os.path.join(path, f))]) + + +# def func_check(v): # TODO: Выявить ошибку и использовать +# def premeow(*args, **kwargs): +# async def meow(ctx): +# try: +# await ctx.message.add_reaction(loading) +# await func(*args, **kwargs) +# await ctx.message.add_reaction(check_mark) +# except Exception as e: +# print(repr(e)) +# await ctx.message.add_reaction(XX) +# +# await asyncio.sleep(3) +# await ctx.message.delete() +# return meow +# return premeow + + +def insert_returns(body): + if isinstance(body[-1], ast.Expr): + body[-1] = ast.Return(body[-1].value) + ast.fix_missing_locations(body[-1]) + + if isinstance(body[-1], ast.If): + insert_returns(body[-1].body) + insert_returns(body[-1].orelse) + + if isinstance(body[-1], ast.With): + insert_returns(body[-1].body) + +def region_to_str(_): return "RU" # TODO: Заменить на нормальный код -_- + +def add_user_to_DB(member): + # if member is not discord.Member: return + ids = [i["id"] for i in db.members.find()] + if member.id not in ids: + db.members.insert_one({"id": member.id, + "name": member.name, + "access_level": "gray", + "language": region_to_str("Meow"), # TODO: Ну, очевидно + "money": 0, + "exp": 0, + "level": 0, + "history": { + "hour": {}, + "day": {}, + }, + "max_level": 0, + "last_mess_time": 0, + "stocks": {}, + "guild_stat": {} + }) + +def translate(text, region): + return text + +async def prefixes(bot, message): + pattern = re.compile(r'/(?:^|[^а-яёa-z]|[,.])(?:(?:ко(?:т(?:(?:е(?:йк|ньк))|(?:ик)|(?:ан)|(?:[её]нок)|(?:я[' + r'рт]))?|(?:шак)))(?:[иаыуэя]|(?:ов)|(ом))?)(?:[^а-яёa-z]|$)/gm', flags=re.IGNORECASE) + match = pattern.search(message.content) + + def check(): + return type(message.channel) == discord.channel.DMChannel or str(message.guild.id) not in prefs.keys() + + return commands.when_mentioned_or(prefs[str(message.guild.id)] if not check() else prefs["default"], + match[0])(bot, message) if match else \ + commands.when_mentioned_or(prefs[str(message.guild.id)] if not check() else prefs["default"])(bot, message) + +intents=discord.Intents.all() +intents.message_content = True +bot = commands.Bot(command_prefix=prefixes, help_command=None, intents=intents) +# bot = commands.Bot(command_prefix=prefixes, intents=discord.Intents.all()) + + +bot.code_lines = 0 +bot.code_size = 0 +#code_lines = 0 # num_lines = sum(1 for line in open('bot.py')) +#code_size = 0 # getsize("bot.py") + +max_cog_name_len = max([len(cog) for cog in os.listdir('./cogs') if cog.endswith('.py')]) + +bot.add_check(is_not_black) +# bot.add_check(is_white) + +bot.owner_id = 459823895256498186 + +my_roles = [] +privat_channels = [] +last_DM = bot.owner_id +last_channel = bot.get_channel(745689950460051546) + +notification = {} +players = {} + + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + + +@tasks.loop(seconds=5) +async def change_activity(): + #await asyncio.sleep(5) + #await bot.change_presence(activity=discord.Activity(name='обработку российского ботнета', + # type=discord.ActivityType.playing)) + await asyncio.sleep(5) + await bot.change_presence(activity=discord.Activity(name=f'на {len(bot.guilds)} сервер(a/ов)', + type=discord.ActivityType.watching)) + #await asyncio.sleep(5) + #await bot.change_presence(activity=discord.Activity(name='радость и счастье', + # type=discord.ActivityType.streaming)) + #await asyncio.sleep(5) + #await bot.change_presence(activity=discord.Activity(name='мяукании в пустототе', + # type=discord.ActivityType.competing)) + await asyncio.sleep(5) + await bot.change_presence(activity=discord.Activity(name=f'на {bot.code_lines} строк и {bot.code_size//2**10} Кб ' + 'сыворотки костылей, велосипедов и кода', + type=discord.ActivityType.watching)) + + seed(int(time())) + + +# @tasks.loop(seconds=10) +# async def v_delete(): +# v_channels = get(get(bot.guilds, id=701726806377496587) +# .categories, id=733787908129030194) \ +# .voice_channels +# +# for channel in v_channels: +# if channel.id != 733788167236223056: +# if not channel.members: +# await channel.delete() + + +@bot.event +async def on_ready(): + bot.voice_counter = {} + + try: + await bot.load_extension("cogs.settings.server") + except Exception as e: + logger.error(repr(e)) + + for cog in os.listdir('./cogs'): + if cog.endswith('.py'): + try: + logger.info(f"Загрузка {cog.capitalize()}...") + await bot.load_extension(f'cogs.{cog.replace(".py", "")}') + + code_lines_ = num_lines = sum(1 for _ in open("cogs/"+cog)) + code_size_ = getsize("cogs/"+cog) + + bot.code_lines += code_lines_ + bot.code_size += code_size_ + + logger.info(f"> {cog.capitalize()} загружен{' '*(max_cog_name_len-len(cog))}\tСтрок: {code_lines_}\tРазмер: {code_size_}") + except Exception as e: + logger.debug(repr(e)) + + logger.info(f"{bot.code_lines}\t{bot.code_size}") + db.members.update_one({"id": 459823895256498186}, {"$set": {"access_level": "secret"}}) + + if os.path.isfile('reboot'): + with open('reboot', 'r') as f: + try: + data = f.readline() + data = data.split() + mes = await bot.get_channel(int(data[0])).fetch_message(int(data[1])) + await mes.clear_reaction(loading) + await mes.add_reaction(check_mark) + + await asyncio.sleep(3) + await mes.delete() + except: pass + + await bot.get_channel(773270764522045460) \ + .send(embed=discord.Embed(title="Бот перезагружен", + color=discord.Color.red())) + os.remove('reboot') + else: + await bot.get_channel(773270764522045460) \ + .send(embed=discord.Embed(title="Бот запущен", + color=discord.Color.red())) + + logger.info('Я живой, блин!') + + #for guild in bot.guilds: + # for member in guild.members: + # add_user_to_DB(member) + + #change_activity.start() +# v_delete.start() + + +async def on_message_handler(message): + channel = message.channel + user = message.author + + global last_channel + last_channel = channel + + # Обработка команд (пре-подготовка) + if user.name != 'Котейка': + add_user_to_DB(user) + + +@bot.event +async def on_message(message): + await bot.process_commands(message) + await on_message_handler(message) + + +@bot.command(pass_content=True) +async def mirror(ctx, time: int, *, atr): + await ctx.message.delete() + async with ctx.typing(): + await asyncio.sleep(time) + await ctx.send(atr) + +CSF = discord.Object(id=822157545622863902) + +# @bot.hybrid_command() +# @app_commands.guilds(CSF) +async def help(ctx, *, search: typing.Optional[str] = None): + region = db.members.find_one({"id": ctx.author.id})["language"] + + color = ctx.guild.me.color + if color == discord.Color.default(): + color = discord.Color(0xaaffaa) + + if search is not None: + if len(search.split('.')) == 2: + search, subcommand = search.split('.') + else: + subcommand = None + + def usage(c): + if dict(c.clean_params) == {}: + return "Не требует параметров" + else: + usage = "" + c_args = dict(c.clean_params) + for key in c_args.keys(): + option = False + types = [] + name = key + + if type(c_args[key].annotation) is typing._GenericAlias: + if type(None) in c_args[key].annotation.__args__: + option = True + types = [i.__name__ for i in c_args[key].annotation.__args__] + else: + types = [c_args[key].annotation.__name__] + + try: + types.remove('NoneType') + except ValueError: + pass + if types[0] == "_empty" or types: types = ["???"] + + if option: + if c_args[key].default in (_empty, None): + usage += f"({name} [{', '.join(types)}]) " + else: + usage += f"({name} = {c_args[key].default} [{', '.join(types)}]) " + else: + if c_args[key].default in (_empty, None): + usage += f"<{name} [{', '.join(types)}]> " + else: + usage += f"<{name} = {c_args[key].default} [{', '.join(types)}]> " + + return usage + + if search is None: + categories = discord.Embed(title="Категории", color=color) + for cog in bot.cogs.keys(): + categories.add_field(name=cog, + value=bot.cogs[cog].description + if bot.cogs[cog].description != '' + else "Без описания", + inline=False) + + embeds = [categories] + + e = discord.Embed(title="Без категории", color=color) + for command in [c for c in bot.commands if c.cog is None and not c.hidden]: + if type(command) == discord.ext.commands.core.Group: + name = f"■ {translate(command.name, region)}" + elif type(command) == discord.ext.commands.core.Command: + name = f"● {translate(command.name, region)}" + e.add_field(name=name, + value=translate(command.brief, region) + if command.brief != '' and command.brief is not None + else "Без описания", + inline=False) + embeds.append(e) + + for cog in bot.cogs.keys(): + e = discord.Embed(title=cog, description=bot.cogs[cog].description, color=color) + + for command in [c for c in bot.commands if c.cog is bot.cogs[cog] and not c.hidden]: + if type(command) == discord.ext.commands.core.Group: + name = f"■ {translate(command.name, region)}" + elif type(command) == discord.ext.commands.core.Command: + name = f"● {translate(command.name, region)}" + e.add_field(name=name, + value=translate(command.brief, region) if command.brief != '' else "Без описания", + inline=False) + embeds.append(e) + embeds_ = [] + + for i in range(len(embeds)): + e = embeds[i].to_dict() + e["description"] = f"`{i + 1}/{len(embeds)}`" + embeds_.append(discord.Embed.from_dict(e)) + + embeds = embeds_ + + embeds[0].set_footer(text=f"Для помощи по категориям введите `{ctx.prefix}help <категория>`\n" + f"Для помощи по группам введите `{ctx.prefix}help <группа>`\n" + "Имя категорий всегда с заглавной буквы,в отличии от комманд") + for e in embeds[1:]: + e.set_footer(text="● - обычные комманды\n■ - комманды с подпунктами (группы)") + + paginator = DiscordUtils.Pagination.CustomEmbedPaginator(ctx, remove_reactions=True) + paginator.add_reaction('⏮️', "first") + paginator.add_reaction('⏪', "back") + paginator.add_reaction('🔐', "lock") + paginator.add_reaction('⏩', "next") + paginator.add_reaction('⏭️', "last") + await paginator.run(embeds) + + else: + # Сначала находим категорию + cog = [cog for cog in bot.cogs if cog == search] + if cog: + cog = cog[0] + e = discord.Embed(title=cog, description=bot.cogs[cog].description, color=color) + + for command in [c for c in bot.commands if c.cog is bot.cogs[cog]]: + if type(command) == discord.ext.commands.core.Group: + name = f"■ {translate(command.name, region)}" + elif type(command) == discord.ext.commands.core.Command: + name = f"● {translate(command.name, region)}" + e.add_field(name=name, + value=translate(command.brief, region) if command.brief != '' else "Без описания", + inline=False) + + await ctx.send(embed=e) + else: + command = [command for command in bot.commands if command.name == search] + if command: + command = command[0] + + # checks = "" + # + # for c in command.checks: + # try: + # checks += "+" if await c(ctx) else "-" + # except: + # checks += "+" if c(ctx) else "-" + # checks += " " + c.__name__ + # checks += "\n" + # + # if command.cog is not None: + # checks = command.cog.cog_check(ctx).__name__ + "\n" + checks + # try: + # checks = "+ " if await command.cog.cog_check(ctx) else "- " + checks + # except: + # checks = "+ " if command.cog.cog_check(ctx) else "- " + checks + # + # checks = "```diff\n" + checks + "```" + e = discord.Embed(title=command.name, + description=f"""Другие имена: {', '.join(command.aliases) + if ', '.join(command.aliases) != '' + else 'Отсутствуют'}\n""" + f"Краткое описание: {translate(command.brief, region)}\n" + f"Использование: \n `{usage(command)}`\n" + f"Описание: \n {translate(command.description, region)}\n" + f"Родитель: {command.parent.__name__ if command.parent is not None else 'Отсутствует'}\n" + f"Категория: {command.cog_name if command.cog is not None else 'Отсутствует'}\n" + # f"Чеки: {checks}\n" + f"Спрятан: {command.hidden}\n", + color=color) + if type(command) == discord.ext.commands.core.Group: + if subcommand is None: + for subcommand in command.commands: + e.add_field(name=subcommand.name, value=subcommand.brief + if subcommand.brief is not None and subcommand.brief != '' + else "Без описания") + else: + command = [command for command in command.commands if command.name == subcommand] + if command: + command = command[0] + e = discord.Embed(title=command.name, + description=f"Другие имена: {', '.join(command.aliases)}\n" + f"Краткое описание: {translate(command.brief, region)}\n" + f"Использование: \n `{usage(command)}`\n" + f"Описание: \n {translate(command.description, region)}\n" + f"Родитель: {command.parent if command.parent is not None else 'Отсутствует'}\n" + f"Категория: {command.cog_name if command.cog is not None else 'Отсутствует'}\n" + # f"Чеки: {checks}\n" + f"Спрятан: {command.hidden}\n", + color=color) + else: + await ctx.send("Субкомманда не найдена") + await ctx.send(embed=e) + else: + await ctx.send("Ничего не найдено") + + +@bot.command() +@commands.has_guild_permissions(manage_messages=True) +async def clear(ctx, count: int): + count += 1 + + while count > 100: + await ctx.channel.purge(limit=100) + count -= 100 + await ctx.channel.purge(limit=count) + + +@bot.group(brief="$commands_HTiP_brief") +async def HTiP(ctx): + region = ctx.message.author.id + region = db.members.find_one({"id": region})["language"] + + if ctx.invoked_subcommand is None: + await ctx.send(translate('$commands_HTiP_subcommand', region)) + + abc = str(ctx.message.attachments[0].filename)[-3:] + if not ctx.message.attachments and abc != 'jpg' and abc != 'png': + await ctx.send(translate('$commands_HTiP_picNotExists', region)) + + else: + url = ctx.message.attachments[0].url + filename = ctx.message.attachments[0].filename + img = requests.get(url) + img_file = open(filename, 'wb') + img_file.write(img.content) + img_file.close() + + +@HTiP.command(brief="$commands_HTiP_w_brief") +async def w(ctx, *, text: str): + region = ctx.message.author.id + region = db.members.find_one({"id": region})["language"] + + if text is None: + await ctx.send(translate('$commands_HTiP_w_textNotExists', region)) + return None + + if text.split(' ')[-1].startswith('key='): + key = text.split(' ')[-1][4:] + text = ' '.join(text.split(' ')[:-1]) + else: + key = None + + if key is not None: + text = XOR.char_encode(text, key) + + abc = str(ctx.message.attachments[0].filename)[-3:] + if not ctx.message.attachments and abc != 'jpg' and abc != 'png': + await ctx.send(translate('$commands_HTiP_picNotExists', region)) + + else: + url = ctx.message.attachments[0].url + filename = ctx.message.attachments[0].filename + img = requests.get(url) + img_file = open(filename, 'wb') + img_file.write(img.content) + img_file.close() + + FFC.write(filename, 'secret.' + abc, text) + with open('secret.' + abc, 'rb') as f: + file = discord.File(f) + await ctx.send(file=file) + + +@HTiP.command(brief="$commands_HTiP_r_brief") +async def r(ctx, key: str = None): + region = ctx.message.author.id + region = db.members.find_one({"id": region})["language"] + + try: + abc = str(ctx.message.attachments[0].filename)[-3:] + if not ctx.message.attachments and abc != 'jpg' and abc != 'png': + await ctx.send(translate('$commands_HTiP_picNotExists', region)) + except IndexError: + await ctx.send(translate('$commands_HTiP_picNotExists', region)) + + else: + url = ctx.message.attachments[0].url + filename = ctx.message.attachments[0].filename + img = requests.get(url) + img_file = open('C:\\Users\\gleb\\PycharmProjects\\systemnik\\' + filename, 'wb') + img_file.write(img.content) + img_file.close() + + secret_text = FFC.read(filename) + if key is not None: + secret_text = XOR.char_encode(secret_text, key) + await ctx.send(secret_text) + + +@bot.command(name='eval') +@commands.check(is_secret) +async def eval_fn(ctx, timeout: typing.Optional[int] = 10, *, cmd): + fn_name = "_eval_expr" + + cmd = cmd.strip("` ") + + # add a layer of indentation + cmd = "\n".join(f" {i}" for i in cmd.splitlines()) + + # wrap in async def body + body = f"async def {fn_name}():\n{cmd}" + + parsed = ast.parse(body) + body = parsed.body[0].body + + insert_returns(body) + + env = { + 'bot': ctx.bot, + 'discord': discord, + 'commands': commands, + 'ctx': ctx, + '__import__': __import__, + 'on_message_handler': on_message_handler, + 'get': get + } + exec(compile(parsed, filename="", mode="exec"), env) + + try: + await eval(f"{fn_name}()", env) + except Exception as e: + await ctx.message.reply(repr(e)) + + +@bot.command(brief="$commands_hentai_brief", + help="`" + "*" + "belle help`") +async def hentai(ctx, type="any", num: int = 1, delete: bool = True): + global janres + region = ctx.message.author.id + region = db.members.find_one({"id": region})["language"] + + if type == "any": + shuffle(janres) + type = janres[0] + elif type == "help": + await ctx.send(', '.join(janres)) + elif type not in janres: + await ctx.send(translate("$commands_hentai_notFound", region).format(ctx.prefix)) + if ctx.message.guild is not None: + await ctx.message.delete() + + if not ctx.channel.is_nsfw(): + await ctx.send(translate("$commands_hentai_notNSFW", region)) + return + else: + for i in range(num): + try: + if delete: + await ctx.send(nekos.img(type), delete_after=10) + else: + await ctx.send(nekos.img(type)) + except: + await ctx.send(nekos.img(type)) + +@bot.command() +@commands.is_owner() +async def change_level(ctx, user: typing.Union[discord.Member, int], level): + if 'int' in str(type(user)): + id_ = user + else: + id_ = user.id + + if not level in ('secret', 'white', 'gray', 'black'): + await ctx.message.add_reaction(XX) + raise TypeError + db.members.update_one({"id": id_}, {"$set": {"access_level": level}}) + await ctx.message.add_reaction(check_mark) + + await asyncio.sleep(3) + await ctx.message.delete() + + +@bot.command() +async def change_lang(ctx, user: typing.Union[discord.Member, int], lang): + lang = lang.upper() + if 'int' in str(type(user)): + id_ = user + else: + id_ = user.id + + if not lang in ("RU", "EN"): + await ctx.message.add_reaction(XX) + raise TypeError + db.members.update_one({"id": id_}, {"$set": {"language": lang}}) + await ctx.message.add_reaction(check_mark) + + await asyncio.sleep(3) + await ctx.message.delete() + + +@bot.command(aliases=["reload"], brief="Перезагружает бота") +@commands.check(is_secret) +async def reboot(ctx): + if os.name == 'posix': + await ctx.message.add_reaction(loading) + with open('reboot', 'w') as f: + f.write(f"{ctx.message.channel.id} {ctx.message.id}") + os.system('pm2 restart Koteika') + + +@bot.command(brief="Управление консолью") +@commands.check(is_secret) +async def cmd(ctx, *, command): + try: + proc = await asyncio.create_subprocess_shell(command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + proc_data = await proc.communicate() + e = discord.Embed(title="Скрипт завершился без ошибок" if proc_data[1] == b'' else "Ошибка", + color=discord.Color.green() if proc_data[1] == b'' else discord.Color.red()) + e.add_field(name="STDOUT", value=f"```{proc_data[0][-994:].decode('utf-8')}```") \ + if proc_data[0] != b'' else None + e.add_field(name="STDERR", value=f"```{proc_data[1][-994:].decode('utf-8')}```") \ + if proc_data[1] != b'' else None + + await ctx.message.reply(embed=e) + except Exception as e: + logger.error(repr(e)) + + +@bot.command(brief="Логи") +@commands.check(is_secret) +async def log(ctx): + try: + a = subprocess.check_output('cat /home/pi/Koteika/Cat.log', shell=True) + await ctx.send('```' + a.decode('utf-8')[-1994:] + '```') + except: + a = subprocess.check_output('cat /home/pi/Koteika/Cat.log', shell=True) + await ctx.send('```' + a.decode('utf-8') + '```') + + +@bot.command(aliases=["vmute", "v_mute", "v_m", "vm"], brief="Выключает микрфоны всех участников ГК") +@commands.has_guild_permissions(administrator=True) +async def voice_mute(ctx): + channel = ctx.message.author.voice.channel + + if channel is not None: + for i in channel.members: + await i.edit(mute=True) + await ctx.message.add_reaction(check_mark) + + else: + await channel.send("Ты не находишься в ГК") + await ctx.message.add_reaction(XX) + + await asyncio.sleep(3) + await ctx.message.delete() + + +@bot.command(aliases=["vunmute", "v_unmute", "v_unm", "vu"], brief="Влючает микрфоны всех участников ГК") +@commands.has_guild_permissions(administrator=True) +async def voice_unmute(ctx): + channel = ctx.message.author.voice.channel + + if channel is not None: + for i in channel.members: + await i.edit(mute=False) + await ctx.message.add_reaction(check_mark) + + else: + await channel.send("Ты не находишься в ГК") + await ctx.message.add_reaction(XX) + + await asyncio.sleep(3) + await ctx.message.delete() + + +@bot.command(name="сайт", brief="Выводит сайт котика (не доделан)") +async def page(ctx): + await ctx.message.delete() + await ctx.send("https://miyakobot.ru/koteika") + + +@bot.command(brief="Выводит права участника сервера (по умолчанию - кота)") +async def perms(ctx, user: typing.Union[discord.Member, int] = None): + if user is None: + user = ctx.author.id + elif type(user) == discord.Member: + user = user.id + + p = {} + out = '' + for i in ['create_instant_invite', 'kick_members', 'ban_members', 'administrator', 'manage_channels', + 'manage_guild', 'add_reactions', 'view_audit_log', 'priority_speaker', 'stream', 'read_messages', + 'send_messages', 'send_tts_messages', 'manage_messages', 'embed_links', 'attach_files', + 'read_message_history', 'mention_everyone', 'external_emojis', 'view_guild_insights', 'connect', 'speak', + 'mute_members', 'deafen_members', 'move_members', 'use_voice_activation', 'change_nickname', + 'manage_nicknames', 'manage_roles', 'manage_webhooks', 'manage_emojis']: + p[i] = False + + perms = ctx.guild.get_member(user).guild_permissions + for i in iter(perms): + p[i[0]] = i[1] + + for i in p.keys(): + if p[i]: + out += '+ ' + else: + out += '- ' + out += i + '\n' + + await ctx.send('```diff\n' + out + '```') + + +# @bot.command() +# async def levels(ctx): +# e = discord.Embed(title="Работяги:") +# for level in ('secret', 'white', 'gray', 'black'): +# e.add_field(name=level, value='\n'.join( +# [i[0] for i in cursor.execute(f'''SELECT name FROM members WHERE access_level="{level}"''').fetchall()])) +# await ctx.send(embed=e) + + +@bot.command() +@commands.has_guild_permissions(administrator=True) +async def change_prefix(ctx, prefix): + prefs[str(ctx.guild.id)] = prefix + + with open('prefixes.json', 'w') as f: + dump(prefs, f) + + +@bot.command() +async def wait(ctx, *, params): + events = ['typing', 'message', 'member_update'] + + params = json.loads(params) + event = params.pop('event') + + if event in events: + def check(element): + out = True + + for param in params.keys(): + if '.' in param: + param_ = param.split('.') + else: + param_ = [param] + for i in param_: + if i in dir(element): + element = getattr(element, i) + else: + out = False + if element != params[param]: out = False + + return out + + await bot.wait_for(event, check=check) + await ctx.send(ctx.message.author.mention) + if event == "member_update": + def check(before, element): + out = True + + for param in params.keys(): + if '.' in param: + param_ = param.split('.') + else: + param_ = [param] + for i in param_: + if i in dir(element): + element = getattr(element, i) + else: + out = False + if element != params[param]: out = False + + return out + + await bot.wait_for(event, check=check) + await ctx.send(ctx.message.author.mention) + + +@logger.catch +def main(): + bot.run(TOKEN) + +main() diff --git a/cogs/SSC.py b/cogs/SSC.py new file mode 100755 index 0000000..f731b76 --- /dev/null +++ b/cogs/SSC.py @@ -0,0 +1,120 @@ +import asyncio + +from discord.ext import commands, tasks +from discord.utils import get +from requests import get +from requests.structures import CaseInsensitiveDict + +from re import findall # Импортируем библиотеку по работе с регулярными выражениями +from subprocess import check_output # Импортируем библиотеку по работе с внешними процессами + +from json import loads +from cogs.emojis import check_mark, XX +from subprocess import run as cmd +import discord +from discord import app_commands + +from loguru import logger + +class SSC(commands.Cog, name="SSC"): + """Управление моей Системой Умной Комнаты Альфа-версия""" + + def __init__(self, bot): + self.bot = bot + self.cooler.start() + logger.debug("Cooler.start()") + self.last_temp = 0 + + def cog_unload(self): + self.cooler.cancel() + + async def cog_check(self, ctx): + return ctx.message.author.id in (self.bot.owner_id, 779697451404886016) + + @commands.command(brief="Включает/выключает свет") + async def light(self, ctx): + headers = CaseInsensitiveDict() + headers["Authorization"] = "Bearer cvbnmjhgvbnmj" + get("http://localhost:80/API/light", headers=headers) + + await asyncio.sleep(3) + await ctx.message.delete() + + @commands.command(brief="Показывает темпераруру", + aliases=['temp']) + async def temperature(self, ctx): + headers = CaseInsensitiveDict() + headers["Authorization"] = "Bearer cvbnmjhgvbnmj" + r = get("http://localhost:80/API/temperature", headers=headers) + j = loads(r.text) + await ctx.send(f"__DHT__\n" + f"Температура: {j['DHT']['temperature']}\n" + f"Влажность: {j['DHT']['humidity']}\n" + f"__CPU__\n" + f"Температура: {j['CPU']['temperature']}") + + @tasks.loop(minutes=1) + async def cooler(self): + temp0 = 55 + temp1 = 80 + min_pwm = 125 + + temp = get_temp() + # logger.info(f"Температура процессора: {temp}") + + pwm = (temp-temp0) / (temp1-temp0) + pwm = int((255 - min_pwm)*pwm + min_pwm) + + if self.last_temp < temp: pwm += 25 + self.last_temp = temp + # if temp <= 65: pwm += 50 + + if pwm < min_pwm: pwm = 0 + if pwm > 255: pwm = 255 + + # logger.info(f"Мощность куллера: {pwm} ({int(pwm/256*100)}%)") + + headers = CaseInsensitiveDict() + headers["Authorization"] = "Bearer cvbnmjhgvbnmj" + get(f"http://localhost/API/cooler/{pwm}", headers=headers) + + @app_commands.command() + async def host(self, inter): + await inter.response.send_message(cmd(["neofetch", "--stdout"], capture_output=True).stdout.decode("utf-8")) + + + # @tasks.loop(seconds=1) + # async def open_door(self): + # if self.arduino.inWaiting() > 0: + # data = self.arduino.readline() + # if data == b'door\r\n': + # embed = discord.Embed(title="Дверь открыта!", color=discord.Color(0xFF0000)) + # await get(self.bot.users, id=self.bot.owner_id).send(embed=embed, delete_after=15) + # elif data == b'test\r\n': + # self.arduino.write(b'2') + # else: + # await get(self.bot.users, id=self.bot.owner_id).send(data) + + # @tasks.loop(seconds=1) + # async def alarm(self): + # now = datetime.datetime.now() + # time = datetime.time(7) + # delta = datetime.timedelta(seconds=2) + # combine = datetime.datetime.combine(now.date(), time) + # + # if combine-delta <= now <= combine+delta: + # await get(self.bot.users, id=self.bot.owner_id).send("!!!") + # self.arduino.write(b'1') + # await asyncio.sleep(1) + # self.arduino.write(b'1') + # await asyncio.sleep(1) + # self.arduino.write(b'1') + +def get_temp(): + temp = check_output(["vcgencmd", "measure_temp"]).decode() # Выполняем запрос температуры + temp = float(findall('\d+\.\d+', temp)[ + 0]) # Извлекаем при помощи регулярного выражения значение температуры из строки "temp=47.8'C" + return (temp) + +async def setup(bot): + await bot.add_cog(SSC(bot)) diff --git a/cogs/_colors.py b/cogs/_colors.py new file mode 100755 index 0000000..77d244c --- /dev/null +++ b/cogs/_colors.py @@ -0,0 +1,15 @@ +from random import choice + +from discord import Color + +light_colors = [ + Color(0xaaffaa), + Color(0xF9FFAA), + Color(0xFFAAAA), + Color(0xFFCCAA), + Color(0xAAFFE3), + Color(0xAAB0FF), + Color(0xFFAAF7) +] + +def choise_light_color(): return choice(light_colors) \ No newline at end of file diff --git a/cogs/activity_counter.py b/cogs/activity_counter.py new file mode 100644 index 0000000..22cc98d --- /dev/null +++ b/cogs/activity_counter.py @@ -0,0 +1,128 @@ +import discord +import schedule +from discord import app_commands +from discord.ext import commands, tasks +from bot import db +from datetime import datetime, timedelta, timezone +from matplotlib import pyplot as plt +from matplotlib import ticker, markers +from loguru import logger + + +class ActiveCount(commands.Cog): + def __init__(self, bot): + self.bot = bot + + plt.style.use(['default', "dark.mplstyle", ]) + + self.daysoftheweek = ("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday") + + for server in bot.guilds: + self.add_server(server.id) + + def add_server(self, server_id: int): + if not db.history.find_one({"type": "server", "id": server_id}): + db.history.insert_one({ + "type": "server", + "id": server_id, + "history": { + day: { + str(i): [] for i in range(24) + } for day in self.daysoftheweek + }, + "current": {str(i): 0 for i in range(24)}, + "yesterday": {str(i): 0 for i in range(24)}, + "avarage": {str(i): [] for i in range(24)} + }) + + # TODO: добавить сервер, если кот зашел на новый сервак + + @commands.Cog.listener() + async def on_message(self, message): + if message.guild is None: return + + hour = message.created_at.replace(tzinfo=timezone.utc).astimezone(tz=None).hour + db.history.update_one( + {"type": "server", "id": message.guild.id}, + {"$inc": {f"current.{hour}": 1}} + ) + + @app_commands.command() + async def activity(self, inter): + async def get_string(inter, string: str) -> str: + data = await self.bot.tree.translator.translate( + app_commands.locale_str(string), + inter.locale, + app_commands.TranslationContext( + app_commands.TranslationContextLocation.other, + "activity" + ) + ) + logger.debug(data) + + if data is None: return string + return data + + day = datetime.now().weekday() + fig, ax = plt.subplots(figsize=(8, 5)) + + server_data = db.history.find_one({"type": "server", "id": inter.guild_id}) + + if server_data is None: + await inter.response.send_message("Недостаточно данных! Попробуйте завтра") + return + + data = server_data['avarage'] + vals = list(map(lambda k: sum(data[k]) / len(data[k]) if len(data[k]) != 0 else 0, data.keys())) + labels = list(map(int, data.keys())) + ax.bar(labels, vals, width=.9, label=await get_string(inter, "Avarage")) + + vals = list(map(lambda k: max(data[k]) if len(data[k]) != 0 else 0, data.keys())) + ax.plot(labels, vals, label=await get_string(inter, "Max"), linestyle='', marker="_", markersize=20) + + data = server_data['history'][self.daysoftheweek[day]] + vals = list(map(lambda k: sum(data[k]) / len(data[k]) if len(data[k]) != 0 else 0, data.keys())) + labels = list(map(int, data.keys())) + ax.bar(labels, vals, width=.6, label=await get_string(inter, "On this day\nof the week")) + + data = server_data['yesterday'] + vals = [data[k] for k in data.keys()] + labels = [int(i) for i in data.keys()] + ax.bar(labels, vals, width = .4, hatch='x', label=await get_string(inter, "Yesterday")) + + data = server_data['current'] + vals = [data[k] for k in data.keys()] + labels = [int(i) for i in data.keys()] + ax.bar(labels, vals, width = .4, label=await get_string(inter, "Today")) + + now = datetime.now() + ax.axvline(now.hour+(now.minute/60)-0.5, color='dimgrey') + + + ax.xaxis.set_minor_locator(ticker.MultipleLocator(1)) + ax.xaxis.set_minor_formatter(ticker.ScalarFormatter()) + ax.tick_params(axis='x', which='minor', labelsize=8) + ax.legend(loc='upper left') + ax.grid(axis='y') + ax.set_axisbelow(True) + ax.set_xlabel(await get_string(inter, 'Hours')) + ax.set_ylabel(await get_string(inter, 'Experience')) + ax.set_xlim(-0.5, 23.5) + + ax.legend().get_frame().set_boxstyle('Round', pad=0.2, rounding_size=1) + ax.legend().get_frame().set_linewidth(0.0) + ax.xaxis.set_ticks_position('none') + ax.yaxis.set_ticks_position('none') + + ax.spines['top'].set_visible(False) + ax.spines['right'].set_visible(False) + ax.spines['bottom'].set_visible(False) + ax.spines['left'].set_visible(False) + + fig.savefig('temp.png') + with open('temp.png', 'rb') as f: + f = discord.File(f) + await inter.response.send_message(file=f) + +async def setup(bot): + await bot.add_cog(ActiveCount(bot)) diff --git a/cogs/bomber.py b/cogs/bomber.py new file mode 100755 index 0000000..5cbd061 --- /dev/null +++ b/cogs/bomber.py @@ -0,0 +1,34 @@ +from asyncio import sleep +from os import system +from subprocess import Popen, PIPE, TimeoutExpired + +from discord.ext import commands +from cogs.emojis import * +from cogs.checks import is_secret + + +class Bomber(commands.Cog): + @commands.command(brief="Спаммер", + help="""Номер телефона в международном формате без + в начале + Режим флуда (1 - СМС, 2 - звонки, 3 - оба) + Сколько времени спамить (в секундах). Не больше 2х минут (120с)""") + @commands.check(is_secret) + async def bomber(self, ctx, num: int, type: int = 1, time: int = 30): + await ctx.message.add_reaction(loading) + with Popen(["/home/pi/Cat/bomber.sh", str(num), str(type), str(time)], stdin=PIPE) as proc: + proc.stdin.write(b'\n') + + try: + if proc.wait(time+5) == 0: + await ctx.message.add_reaction(check_mark) + else: + await ctx.message.add_reaction(XX) + except TimeoutExpired: + await ctx.message.add_reaction(XX) + + await sleep(3) + await ctx.message.delete() + + +# def setup(bot): +# bot.add_cog(Bomber(bot)) diff --git a/cogs/bridge.py b/cogs/bridge.py new file mode 100644 index 0000000..43cd622 --- /dev/null +++ b/cogs/bridge.py @@ -0,0 +1,85 @@ +import discord +import requests +import uuid +import aiohttp +import asyncio + +from discord.ext import commands + +class Bridge(commands.Cog): + def __init__(self): + self.base_url = "https://www.guilded.gg/api" + self.session = aiohttp.ClientSession() + + asyncio.create_task(self.meow()) + + async def meow(self): + await self.session.post(f"{self.base_url}/login", json={"email": "miyako@miyakobot.ru", "password": "miyakotb"}) + + @commands.Cog.listener() + async def on_message(self, message): + if message.guild is None: return + if message.guild.id != 822157545622863902: return + + if message.channel.id == 822157545622863905: + channel_id = "e11508c9-b817-404d-8ac4-22ff288f8f48" + elif message.channel.id == 822582629390876762: + channel_id = "5b228995-ceb6-442a-b65e-162a797b5253" + elif message.channel.id == 864989115242250280: + channel_id = "7cd64e7d-2747-4cea-90a5-3c7b57c2b895" + elif message.channel.id == 977920558140379176: + channel_id = "15404706-85b8-45a0-b969-57394953917c" + else: + return + + #await message.channel.send(f"{self.base_url}/channels/{channel_id}/messages") + + nick = message.author.nick + if nick is None: + nick = message.author.name + + json = { + "messageId": str(uuid.uuid4()), + "content": { + "object": "value", + "document": { + "object": "document", + "data": {}, + "nodes": [{ + "object": "block", + "type": "paragraph", + "nodes": [{ + "object": "text", + "leaves": [{ + "object": "leaf", + "text": nick, + "marks": [{ + "data": {}, + "object": "mark", + "type": "inline-code-v2" + }] + }] + }] + }, { + "object": "block", + "type": "paragraph", + "data": {}, + "nodes": [{ + "object": "text", + "leaves": [{ + "object": "leaf", + "text": message.content, + "marks": [] + }] + }] + }] + } + } + } + + await self.session.post(f"{self.base_url}/channels/{channel_id}/messages", json=json) + +async def setup(bot): + await bot.add_cog(Bridge()) + + diff --git a/cogs/checks.py b/cogs/checks.py new file mode 100755 index 0000000..059fa43 --- /dev/null +++ b/cogs/checks.py @@ -0,0 +1,31 @@ +from pymongo import MongoClient + +client = MongoClient('localhost', 27017) + +db = client['Koteika'] +collection = db.members + + +def is_secret(ctx): + try: id_ = ctx.author.id + except: id_ = ctx.id + info = collection.find_one({"id": id_})["access_level"] + return 'secret' == info + + +def is_white(ctx): + try: + id_ = ctx.author.id + except: + id_ = ctx.id + info = collection.find_one({"id": id_})["access_level"] + return info in ('white', 'secret') + + +def is_not_black(ctx): + try: + id_ = ctx.author.id + except: + id_ = ctx.id + info = collection.find_one({"id": id_})["access_level"] + return "black" != info \ No newline at end of file diff --git a/cogs/demotivator.py b/cogs/demotivator.py new file mode 100644 index 0000000..8b3abc7 --- /dev/null +++ b/cogs/demotivator.py @@ -0,0 +1,28 @@ +import discord +from discord import app_commands +from discord.ext import commands +from simpledemotivators import Demotivator as Dem +from os import remove +from os.path import join +from loguru import logger + +class Demotivator(commands.Cog): + @app_commands.command() + async def demotivator(self, inter: discord.Interaction, title: str, text: str, image: discord.Attachment): + logger.debug((title, text)) + + if not "image" in image.content_type: + await inter.response.send_message("Это не изображение") + return + + logger.debug("Meow") + filename = join("/home/pi/Koteika/tmp", f"{inter.id}_{image.filename}") + logger.debug(filename) + await image.save(filename) + Dem(title, text).create(filename, font_name="FreeSans.ttf") + with open(filename) as f: + await inter.response.send_message(file=discord.File(f)) + remove(filename) + +async def setup(bot): + await bot.add_cog(Demotivator()) diff --git a/cogs/direct_messages.py b/cogs/direct_messages.py new file mode 100755 index 0000000..277ca95 --- /dev/null +++ b/cogs/direct_messages.py @@ -0,0 +1,62 @@ +import asyncio + +import discord +from discord.ext import commands +from typing import Optional + +from discord.utils import get + +from cogs.emojis import check_mark, XX + + +class DM(commands.Cog, name="ЛС"): + """Работа с ЛС""" + + def __init__(self, bot): + self.bot = bot + self.last_dm = 459823895256498186 + + + @commands.Cog.listener() + async def on_message(self, message): + if type(message.channel) == discord.channel.DMChannel: + if message.author != message.channel.me and message.author.id != self.bot.owner_id: + await get(self.bot.users, id=self.bot.owner_id).send(f'```{message.author.name} {message.author.id}' + f'```\n' + message.content) + if message.attachments: + await get(self.bot.users, id=self.bot.owner_id).send(str(message.attachments)) + + self.last_dm = message.author.id + + @commands.command(brief="Отправляет сообщение", + aliases=['send', 'DMsend', 'DM_send']) + @commands.is_owner() + async def dm_send(self, ctx, id: Optional[int] = None, *, text: str): + sended = False + if id is None: id = self.last_dm + + for guild in self.bot.guilds: + member = guild.get_member(id) + if member is not None: + await member.send(text) + await ctx.message.add_reaction(check_mark) + self.last_dm = id + sended = True + break + if not sended: + await ctx.message.add_reaction(XX) + + await asyncio.sleep(3) + await ctx.message.delete() + + @commands.command(brief="Удаляет свои сообщения из ЛС", + aliases=['delete', 'DMdelete', 'DM_delete', 'dm_del']) + async def dm_delete(self, ctx, count: int = 100): + if type(ctx.channel) == discord.channel.DMChannel: + async for message in ctx.channel.history(limit=count): + if message.author == message.channel.me: + await message.delete() + + +async def setup(bot): + await bot.add_cog(DM(bot)) diff --git a/cogs/economic.py b/cogs/economic.py new file mode 100755 index 0000000..78aab6e --- /dev/null +++ b/cogs/economic.py @@ -0,0 +1,537 @@ +from time import time + +import typing +import discord +import schedule + +from bot import db +from loguru import logger +from random import randint as rint +from discord import app_commands +from discord.ext import commands, tasks +from discord.app_commands import Choice +from discord.app_commands import TranslationContextLocation as trans_loc +from datetime import datetime +from matplotlib import pyplot as plt +from cogs.emojis import chocolate + +class Economic(commands.Cog, name="Экономика"): + def __init__(self, bot): + self.bot = bot + + plt.style.use(['default', "dark.mplstyle"]) + + + def time_translation(self, secs): + def two_digit(number): + if len(str(number)) == 1: + return '0'+str(number) + return number + + time = f"{secs}s " + if 60 <= secs < 60*60: + time = f"{secs//60}:{two_digit(secs%60)}" + elif 60*60 <= secs < 24*60*60: + time = f"{secs//60//60}:{two_digit(secs//60%60)}:{two_digit(secs%60)}" + elif 24*60*60 <= secs: + time = f"{secs//60//60//24}d {two_digit(secs//60//60%24)}:{two_digit(secs//60%60)}:{two_digit(secs%60)}" + return time + + def exp_translation(self, exp): + if exp < 1000: + return exp + if exp < 1000000: + exp /= 1000 + if exp.is_integer(): + return str(int(exp))+'k' + return "%.1fk" % exp + + @commands.Cog.listener() + async def on_message(self, message): + if not message.author.bot and message.guild is not None and not message.content.startswith(tuple(await self.bot.command_prefix(self.bot, message))): + data = db.members.find_one({"id": message.author.id}) + flood_channels = db.guild_settings.find_one({"id": message.guild.id}) + if flood_channels is None: + flood_channels = [] + else: + flood_channels = flood_channels['flood_channels'] + + if time() - 3 >= data["last_mess_time"] and message.channel.id not in flood_channels: + delta_exp = len(message.content) + len(message.attachments)*100 + + db.history.update_one( + {'type': 'server', 'id': message.guild.id}, + {'$inc': {f'current.{datetime.now().hour}': delta_exp}} + ) + + if message.author.voice is not None: + delta_exp = int(delta_exp / 10) + delta_money = rint(1, 5) + + # Глобальные опыт/уроень + db.members.update_one({"id": message.author.id}, + {"$set": {"exp": data["exp"] + delta_exp}}) # Изменяем exp + data = db.members.find_one({"id": message.author.id}) + if data is not None: + level = data["level"] + if level ** 2 * 50 + 5 <= data["exp"]: + db.members.update_one({"id": message.author.id}, + {"$set": {"level": data["level"] + 1}}) # Изменяем level + if data["level"]+1 >= data["max_level"]: + db.members.update_one({"id": message.author.id}, + {"$set": { + "money": data["money"] + (level + 1) * delta_money}}) # Изменяем money + db.members.update_one({"id": message.author.id}, + {"$set": {"max_level": data["level"] + 1}}) # Изменяем level + + + # Локальные опыт/уровень + prefix = f"guild_stat.{message.guild.id}" + if str(message.guild.id) not in db.members.find_one({"id": message.author.id})["guild_stat"].keys(): + db.members.update_many({"id": message.author.id}, + { + "$set": { + f"{prefix}.exp": 0, + f"{prefix}.level": 0, + f"{prefix}.secs_in_voice": 0, + f"{prefix}.history": { + "hour": {}, + "day": {}, + "siv_h": {}, + "siv_d": {} + } + } + }) # Создаем в guild_stat поле для сервера + + data = db.members.find_one({"id": message.author.id})["guild_stat"][str(message.guild.id)] + db.members.update_one({"id": message.author.id}, + { + "$set": { + f"{prefix}.exp": data['exp'] + delta_exp + } + }) + data = db.members.find_one({"id": message.author.id})["guild_stat"][str(message.guild.id)] + level = data["level"] + if level ** 2 * 50 + 5 <= data["exp"]: + db.members.update_one({"id": message.author.id}, + { + "$set": { + f"{prefix}.level": data['level'] + 1 + } + }) + try: + data = db.guild_settings.find_one({"id": message.guild.id}) + if data is not None and data['levelup'] == "send": + await message.reply( + embed=discord.Embed( + title="LEVEL UP", + description=f"{message.author.mention} достиг {level+1} уровня!" + ), + delete_after=10, + mention_author=False + ) + except KeyError: pass + + db.members.update_one({"id": message.author.id}, {"$set": {"last_mess_time": time()}}) + + + @commands.Cog.listener() + async def on_voice_state_update(self, member, before, after): + if not member.bot: + # При входе + if before.channel is None: + humans = list(filter(lambda x: not x.bot, after.channel.members)) + + #logger.info(f"{member.name}#{member.discriminator} зашел. В канале теперь {len(humans)} человек") + + if len(humans) == 1: pass + elif len(humans) == 2: + self.bot.voice_counter[str(humans[0].id)] = datetime.now() + self.bot.voice_counter[str(humans[1].id)] = datetime.now() + else: + self.bot.voice_counter[str(member.id)] = datetime.now() + + else: + humans = list(filter(lambda x: not x.bot, before.channel.members)) + if len(humans) == 1: + self.voice_register(humans[0], before) + if len(humans) != 0: + self.voice_register(member, before) + + # При выходе + if after.channel is None: + #logger.info(f"{member.name}#{member.discriminator} вышел. В канале осталось {len(list(filter(lambda x: not x.bot, before.channel.members)))} человек") + try: del self.bot.voice_counter[str(member.id)] + except: pass #logger.info(f"У {member.name}#{member.discriminator} не было информации о времени захода в канал") + + + def voice_register(self, member, voice_state): + try: secs = (datetime.now() - self.bot.voice_counter[str(member.id)]).seconds + except: + #logger.info(f"У {member.name}#{member.discriminator} не было информации о времени захода в канал") + secs = 0 + + # TODO: Убрать нахуй эти ифы + if voice_state.self_deaf: + k = 0 + elif voice_state.self_mute: + k = .5 + else: + k = 1 + if voice_state.self_deaf and (voice_state.self_stream or voice_state.self_video): + k = 0.1 + elif voice_state.self_stream or voice_state.self_video: + k *= 2 + exp = int(secs // 5 * k) + money = exp * rint(1, 5) + db.members.update_one({"id": member.id}, {"$inc": { + f"guild_stat.{member.guild.id}.secs_in_voice": secs, + f"guild_stat.{member.guild.id}.exp": exp, + "exp": exp, + "money": money + }}) + + db.history.update_one( + {'type': 'server', 'id': member.guild.id}, + {"$inc": {f'current.{datetime.now().hour}': exp}} + ) + + self.bot.voice_counter[str(member.id)] = datetime.now() + + logger.info( + f"{member.name}#{member.discriminator}\n" + f"\tСекунд: +{secs}\n" + f"\tОпыт: +{exp}\n" + f"\tДенег: +{money}" + ) + + + @commands.Cog.listener() + async def on_member_join(self, member): + member_data = db.members.find_one({"id": member.id}) + + if member_data is None: + logger.warning("Пользователь не найден") + return + + if str(member.guild.id) in member_data["guild_stat"].keys(): + logger.debug(member_data["guild_stat"][str(member.guild.id)]["exp"], end="\t") + db.members.update_one( + {"id": member.id}, + {"$set": { "exp": member_data["exp"] + member_data["guild_stat"][str(member.guild.id)]["exp"] }} + ) + + @commands.Cog.listener() + async def on_member_remove(self, member): + member_data = db.members.find_one({"id": member.id}) + + if member_data is None: + logger.warning("Пользователь не найден") + return + if str(member.guild.id) in member_data["guild_stat"].keys(): + logger.debug(member_data["guild_stat"][str(member.guild.id)]["exp"], end="\t") + db.members.update_one( + {"id": member.id}, + {"$set": { "exp": member_data["exp"] - member_data["guild_stat"][str(member.guild.id)]["exp"] } + }) + + @commands.Cog.listener() + async def on_guild_join(self, guild): + for m in db.members.find({f"guild_stat.{guild.id}": {"$exists": True}}): + logger.debug(m["guild_stat"][str(guild.id)]["exp"], end="\t") + db.members.update_one( + {"id": m['id']}, + {"$set": { "exp": m["exp"] + m["guild_stat"][str(guild.id)]["exp"] } + }) + logger.debug(m["guild_stat"][str(guild.id)]["exp"]) + + @commands.Cog.listener() + async def on_guild_remove(self, guild): + for m in db.members.find({f"guild_stat.{guild.id}": {"$exists": True}}): + logger.debug(m["guild_stat"][str(guild.id)]["exp"], end="\t") + db.members.update_one( + {"id": m['id']}, + {"$set": {"exp": m["exp"] - m["guild_stat"][str(guild.id)]["exp"]} + }) + logger.debug(m["guild_stat"][str(guild.id)]["exp"]) + + async def get_text(self, inter, location, string): + data = await self.bot.tree.translator.translate( + app_commands.locale_str(string), + inter.locale, + app_commands.TranslationContext( + trans_loc.other, + location + ) + ) + + if data is None: return string + return data + + @app_commands.command(description="View balance and level") + async def rank(self, inter, user: discord.Member = None): + if user is None: user = inter.user + + if self.bot.get_user(user.id).bot: + await inter.response.send_message(await self.get_text(inter, "rank", "Bot hasn't experience")) + return + + user_data = db.members.find_one({"id": user.id}) + if user_data is None: + await inter.response.send_message("Об этом пользователе информации пока нет") + return + + if str(user.id) in self.bot.voice_counter.keys(): + prefix = f"guild_stat.{inter.guild.id}" + if str(inter.guild.id) not in user_data["guild_stat"].keys(): + db.members.update_many( + {"id": user.id}, + {"$set": { + f"{prefix}.exp": 0, + f"{prefix}.level": 0, + f"{prefix}.secs_in_voice": 0, + f"{prefix}.history": { + "hour": {}, + "day": {} + } + }} + ) # Создаем в guild_stat поле для сервера + + self.voice_register(user, user.voice) + + user_data = db.members.find_one({"id": user.id}) + if user_data is None: return + + if inter.guild is not None: + color = inter.guild.me.color + if color == discord.Color.default(): + color = discord.Color(0xaaffaa) + else: + color = discord.Color(0xaaffaa) + + history = user_data['history'] + if len(history['hour']) >= 1: + per_hour = user_data['exp'] - history['hour'][list(history['hour'].keys())[-1]] + else: per_hour = "???" + if len(history['hour']) >= 2: + last_hour = history['hour'][list(history['hour'].keys())[-1]] - history['hour'][list(history['hour'].keys())[-2]] + else: last_hour = "???" + + if len(history['day']) >= 1: + per_day = user_data['exp'] - history['day'][list(history['day'].keys())[-1]] + else: per_day = "???" + if len(history['day']) >= 2: + last_day = history['day'][list(history['day'].keys())[-1]] - history['day'][list(history['day'].keys())[-2]] + else: last_day = "???" + + + description = f"{await self.get_text(inter, 'rank', 'Money')}: {user_data['money']}{chocolate}\n\n" \ + f"__{await self.get_text(inter, 'rank', 'Global stats')}:__\n" \ + f"{await self.get_text(inter, 'rank', 'Level')}: {user_data['level']}\n" \ + f"{await self.get_text(inter, 'rank', 'Exp')}: {user_data['exp']} / {user_data['level'] ** 2 * 50 + 5}" \ + f" ({(user_data['level'] ** 2 * 50 + 5) - user_data['exp']})\n" \ + f"{await self.get_text(inter, 'rank', 'Per hour')}: {per_hour}, {await self.get_text(inter, 'rank', 'per the past hour')}: {last_hour}\n" \ + f"{await self.get_text(inter, 'rank', 'Per day')}: {per_day}, {await self.get_text(inter, 'rank', 'per the past day')}: {last_day}\n" + + secs = user_data['guild_stat'][str(inter.guild.id)]['secs_in_voice'] + + if "guild_stat" in list(user_data.keys()): + if inter.guild is not None and str(inter.guild.id) in list(user_data['guild_stat'].keys()): + description += f"\n__{await self.get_text(inter, 'rank', 'On this guild')}:__\n" \ + f"{await self.get_text(inter, 'rank', 'Level')}: {user_data['guild_stat'][str(inter.guild.id)]['level']}\n" \ + f"{await self.get_text(inter, 'rank', 'Exp')}: {user_data['guild_stat'][str(inter.guild.id)]['exp']} / " \ + f"{user_data['guild_stat'][str(inter.guild.id)]['level'] ** 2 * 50 + 5}" \ + f" ({(user_data['guild_stat'][str(inter.guild.id)]['level'] ** 2 * 50 + 5) - user_data['guild_stat'][str(inter.guild.id)]['exp']})\n" \ + f"{await self.get_text(inter, 'rank', 'Time in voice channels')}: {self.time_translation(secs)}" + + + e = discord.Embed(title=f"{await self.get_text(inter, 'rank', 'Info about')} {self.bot.get_user(user.id).name}", + description=description, + color=color) + await inter.response.send_message(embed=e) + + @app_commands.command(description="Top members of the guild") + @app_commands.describe(category='Category') + @app_commands.choices(category=[ + Choice(name='Balance', value="Баланс"), + Choice(name='Experience', value="Опыт"), + Choice(name='Time in voice channel', value="Время в войсе") + ]) + # @logger.catch + async def top(self, inter, category: Choice[str] = None): + if category is None: + category = "Опыт" + else: + category = category.value + categories = { + 'Уровень': f"guild_stat.{inter.guild.id}.level", + 'Баланс': "money", + 'Опыт': f"guild_stat.{inter.guild.id}.exp", + 'Время в войсе': f"guild_stat.{inter.guild.id}.secs_in_voice" + } + + if inter.guild is not None: + color = inter.guild.me.color + if color == discord.Color.default(): + color = discord.Color(0xaaffaa) + else: + color = discord.Color(0xaaffaa) + + e = discord.Embed(title="Топ", description=category, color=color) + data_ = list(db.members.find({f"guild_stat.{inter.guild.id}": {"$exists": True}}).sort(categories[category], -1))[:10] + + if len(data_) == 0: + await inter.response.send_message("Недостаточно данных! Попробуйте завтра") + return + + l = len(data_) if len(data_) < 10 else 10 + + MAX_COLONS = 25 + + if category == "Опыт": + max_val = data_[0]["guild_stat"][str(inter.guild.id)]["exp"] + elif category == "Время в войсе": + max_val = data_[0]['guild_stat'][str(inter.guild.id)]['secs_in_voice'] + elif category == "Баланс": + max_val = data_[0]['money'] + + for place in range(l): + m = data_[place] + if 'level' not in m['guild_stat'][str(inter.guild.id)].keys(): + db.members.update_one( + {'id': m['id']}, + {'$set': {f'guild_stat.{inter.guild.id}.level': 0}} + ) + m['guild_stat'][str(inter.guild.id)]['level'] = 0 + + data = {"Уровень": ["Ур:", m['guild_stat'][str(inter.guild.id)]['level']], + "Опыт": ["Опыт:", self.exp_translation(m['guild_stat'][str(inter.guild.id)]["exp"]), m['guild_stat'][str(inter.guild.id)]["exp"]], + "Время в войсе": [":sound:", self.time_translation(m['guild_stat'][str(inter.guild.id)]['secs_in_voice']), m['guild_stat'][str(inter.guild.id)]['secs_in_voice']], + "Баланс": [":moneybag:", m["money"], m["money"]] + } + u = self.bot.get_user(m['id']) + if u is None: + name = str(place + 1) + '. ' + m["name"] + else: + name = str(place + 1) + '. ' + u.name + '#' + str(u.discriminator) + + e.add_field(name=name, + inline=False, + value=f"{data[category][0]} {data[category][1]} | " + + " | ".join([f"{data[k][0]} {data[k][1]}" for k in data.keys() if k != category]) + + "\n" + str(int(data[category][2]/max_val*100)) + '% '+ + "█" * int(data[category][2]/max_val*MAX_COLONS) + + "▌" * int((data[category][2]/max_val*MAX_COLONS)%2) + '') + + await inter.response.send_message(embed=e) + + @commands.hybrid_command(brief="Experience gain statistics") + async def graph(self, ctx, user: discord.Member = None): + if user is None: + user = ctx.author.id + else: + user = user.id + + if self.bot.get_user(user).bot: + await ctx.reply("У ботов нет опыта. Они всемогущи") + return + + db_ = db.members + info = db_.find_one({"id": user})['guild_stat'][str(ctx.guild.id)]['history']['hour'] + data = [info[key] for key in info.keys()] + fig, ax = plt.subplots() + ax.plot(data, label=self.bot.get_user(user).name) + ax.grid(True) + ax.set_ylabel('Опыт', color=(1.00, 1.00, 1.00)) + ax.set_xlabel('Время (ч)', color=(1.00, 1.00, 1.00)) + ax.legend(loc='upper left') + for label in ax.get_yticklabels(): + label.set_color((1.00, 1.00, 1.00)) + ax.set_facecolor((.12, .12, .12)) + fig.patch.set_facecolor((.14, .14, .14)) + for label in ax.get_xticklabels(): + try: label.set_text(datetime.fromtimestamp(int(label.get_text())).strftime('%d.%m %H:00')) + except: pass + label.set_color((1, 1, 1)) + fig.savefig('temp.png') + for label in ax.get_xticklabels(): + label.set_color((1, 1, 1)) + with open('temp.png', 'rb') as f: + f = discord.File(f) + await ctx.send(file=f) + + @app_commands.command(description="Comparison of exp with other members") + @discord.ui.button(label="<<") + @discord.ui.button(label=">>", disabled=True) + @app_commands.choices(period=[ + Choice(name='Per the entire period', value=-1), + Choice(name='Per month', value=24*30), + Choice(name='Per day', value=24) + ]) + async def dif_graph(self, inter, user1: discord.Member, user2: discord.Member = None, period: Choice[int] = -1): + if period != -1: period = period.value + + ts = datetime.now().timestamp() + + # if user1 is None: user1 = user2 + user1 = user1.id + if user2 is None: + user2 = inter.user.id + else: + user2 = user2.id + + if self.bot.get_user(user1).bot or self.bot.get_user(user2).bot: + await inter.response.send_message("У ботов нет опыта. Они всемогущи") + return + + + db_ = db.members + info1 = db_.find_one({"id": user1})['guild_stat'][str(inter.guild.id)]['history']['hour'] + info2 = db_.find_one({"id": user2})['guild_stat'][str(inter.guild.id)]['history']['hour'] + info1[str(int(ts))] = db_.find_one({"id": user1})['guild_stat'][str(inter.guild.id)]['exp'] + info2[str(int(ts))] = db_.find_one({"id": user2})['guild_stat'][str(inter.guild.id)]['exp'] + + + if period == -1: + data1 = list(info1.values()) + data2 = list(info2.values()) + else: + data1 = [info1[key] for key in info1.keys() if int(key) >= ts-period*60*60] + data2 = [info2[key] for key in info2.keys() if int(key) >= ts-period*60*60] + + fig, ax = plt.subplots(figsize=(8, 5)) + ax.plot(list(map(int, info1.keys()))[-len(data1):], data1, marker='.', label=self.bot.get_user(user1).name) + ax.plot(list(map(int, info2.keys()))[-len(data2):], data2, marker='.', label=self.bot.get_user(user2).name) + # ax.plot([i for info1.keys()], data2, label="Разница") + ax.grid(True) + ax.set_ylabel('Опыт') + ax.set_xlabel('Время (ч)') + ax.legend(loc='upper left') + + for label in ax.get_xticklabels(): + label.set_color((1, 1, 1)) + + labels = [datetime.fromtimestamp(int(text)).strftime('%d.%m %H:%M') for text in ax.get_xticks()] + ax.set_xticklabels(labels) + fig.autofmt_xdate() + + ax.legend().get_frame().set_boxstyle('Round', pad=0.2, rounding_size=1) + ax.legend().get_frame().set_linewidth(0.0) + ax.xaxis.set_ticks_position('none') + ax.yaxis.set_ticks_position('none') + + ax.spines['bottom'].set_visible(False) + ax.spines['top'].set_visible(False) + ax.spines['left'].set_color('#303030') + ax.spines['right'].set_color('#303030') + + fig.savefig('temp.png') + with open('temp.png', 'rb') as f: + f = discord.File(f) + await inter.response.send_message(file=f) + + +# @logger.catch +async def setup(bot): + await bot.add_cog(Economic(bot)) + diff --git a/cogs/emojis.py b/cogs/emojis.py new file mode 100755 index 0000000..c133736 --- /dev/null +++ b/cogs/emojis.py @@ -0,0 +1,7 @@ +check_mark = '' +XX = '' +loading = '' +right = 'a:cat_right:769920151143055361' +left = 'a:cat_left:769919996615852042' +center = 'a:cat_center:769922556241117185' +chocolate = ":chocolate_bar:" diff --git a/cogs/error_handler.py b/cogs/error_handler.py new file mode 100644 index 0000000..4584be3 --- /dev/null +++ b/cogs/error_handler.py @@ -0,0 +1,38 @@ +import discord +from discord import app_commands +from discord.ext.commands import Cog +from random import choice +from loguru import logger +from sys import exc_info +from traceback import print_tb, print_exc + +class ErrHandler(Cog): + def __init__(self, bot): + self.bot = bot + bot.tree.error(self.on_error) + + async def on_error(self, inter, error): + logger.error(error) + logger.warning(print_exc()) + info = exc_info() + logger.error(info) + logger.error(print_tb(info[2])) + + errors_text = await self.bot.tree.translator.translate( + app_commands.locale_str("errors_text"), + inter.locale, + app_commands.TranslationContext( + app_commands.TranslationContextLocation.other, + "errors_text" + ) + ) + if errors_text is None: + errors_text = "The cats dropped the vase again and something broke; "\ + "Today is not the day, it won't work; "\ + "I'm too lazy to do this; "\ + "Something broke. It's not me!" + + await inter.response.send_message(choice(errors_text.split('; '))) + +async def setup(bot): + await bot.add_cog(ErrHandler(bot)) diff --git a/cogs/errors.py b/cogs/errors.py new file mode 100644 index 0000000..3f93e06 --- /dev/null +++ b/cogs/errors.py @@ -0,0 +1,53 @@ +import discord +from discord.ext import commands +from loguru import logger + +class Errors(commands.Cog): + def __init__(self, bot): + self.bot = bot + logger.info("Загружено") + + @commands.Cog.listener() + async def on_command_error(self, ctx: commands.Context, error: commands.CommandError): + send_help = (commands.TooManyArguments, commands.UserInputError) + + if isinstance(error, commands.CommandNotFound): + pass + elif isinstance(error, commands.MissingRequiredArgument): + await ctx.send(f'Отсутствует аргумент `{error.param.name}` типа `{error.param.converter.__name__}`') + elif isinstance(error, send_help): + await ctx.send(f'Неправильные аргументы. Для справки, обратись к `{ctx.clean_prefix}help`') + logger.debug(error.message) + elif isinstance(error, commands.BotMissingPermissions): + await ctx.send(f"У бота нет прав на выполнение этой команды. Требуемые права: ```{chr(10).join(error.missing_permissions)}```") + elif isinstance(error, commands.MissingPermissions): + await ctx.send(f'Нет прав на выполнение этой комманды. Требуемые права: ```{chr(10).join(error.missing_permissions)}```') + elif isinstance(error, commands.NotOwner): + await ctx.send('Очевидно же, что только для создателя бота') + elif isinstance(error, commands.CheckFailure): + await ctx.send('Команда недоступна') + else: + logger.error(error.__traceback__) + # missing = [perm.replace('_', ' ').replace('guild', 'server').title() for perm in error.missing_perms] + # if len(missing) > 2: + # fmt = '{}, и {}'.format("**, **".join(missing[:-1]), missing[-1]) + # else: + # fmt = ' и '.join(missing) + # _message = 'Мне требуются эти права для выполнения операции: **{}**'.format(fmt) + # await ctx.send(_message) + #if type(error) == discord.ext.commands.errors.CommandInvokeError or \ + # type(error.original) == discord.errors.Forbidden or \ + # error.original.text == "Missing Permissions": + # logger.debug(7) + # missing = [perm.replace('_', ' ').replace('guild', 'server').title() for perm in error.missing_perms] + # if len(missing) > 2: + # fmt = '{}, и {}'.format("**, **".join(missing[:-1]), missing[-1]) + # else: + # fmt = ' и '.join(missing) + # _message = 'Вам требуются эти права для выполнения операции: **{}**'.format(fmt) + # await ctx.send(_message) + await ctx.send(error) + + +async def setup(bot): + await bot.add_cog(Errors(bot)) diff --git a/cogs/file_ext.py b/cogs/file_ext.py new file mode 100644 index 0000000..e5f41cb --- /dev/null +++ b/cogs/file_ext.py @@ -0,0 +1,57 @@ +import discord +from discord.ext import commands +from discord import app_commands +from os.path import splitext +from os.path import join as joinpath +from os import mkdir, rmdir, remove + +from cairosvg import svg2png + + +class FileExt(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_message(self, message): + if message.attachments: + files = [] + mkdir(joinpath("/home/pi/Koteika/tmp", str(message.id))) + if list(filter(lambda x: splitext(x.filename)[1] in ('.mkv', '.svg'), message.attachments)): + m = await message.reply("Конвертация...") + + + # MKV + for at in filter(lambda x: x.filename.endswith('.mkv'), message.attachments): + await at.save(fp=joinpath("/home/pi/Koteika/tmp", str(message.id), at.filename)) + import ffmpeg + ( + ffmpeg + .input(joinpath("/home/pi/Koteika/tmp", str(message.id), at.filename)) + .output(joinpath("/home/pi/Koteika/tmp", str(message.id), splitext(at.filename)[0]+'.mp4')) + .run() + ) + remove(joinpath("/home/pi/Koteika/tmp", str(message.id), at.filename)) + with open(joinpath("/home/pi/Koteika/tmp", str(message.id), splitext(at.filename)[0]+'.mp4'), 'rb') as f: + files.append( discord.File(f, spoiler=at.is_spoiler(), filename=splitext(at.filename)[0]+'.mp4') ) + remove(joinpath("/home/pi/Koteika/tmp", str(message.id), splitext(at.filename)[0]+'.mp4')) + + # SVG + for at in filter(lambda x: x.filename.endswith('.svg'), message.attachments): + code = await at.read() + code = code.decode("utf-8") + svg2png(bytestring=code, write_to=joinpath("/home/pi/Koteika/tmp", str(message.id), splitext(at.filename)[0]+'.png')) + + with open(joinpath("/home/pi/Koteika/tmp", str(message.id), splitext(at.filename)[0]+'.png'), 'rb') as f: + files.append( discord.File(f, spoiler=at.is_spoiler(), filename=splitext(at.filename)[0]+'.png') ) + remove(joinpath("/home/pi/Koteika/tmp", str(message.id), splitext(at.filename)[0]+'.png')) + + + if files: + await message.reply(files=files) + await m.delete() + + rmdir(joinpath("/home/pi/Koteika/tmp", str(message.id))) + +async def setup(bot): + await bot.add_cog(FileExt(bot)) diff --git a/cogs/japanese.py b/cogs/japanese.py new file mode 100644 index 0000000..88386f8 --- /dev/null +++ b/cogs/japanese.py @@ -0,0 +1,179 @@ +import discord +import sqlite3 +import romkan + +from discord import app_commands +from discord.ext import commands + +class Group(app_commands.Group): + def __init__(self, db, name): + self.db = db + super().__init__(name=name) + + def flot_parser(self, string: str) -> str: + output = "" + counter = 1 + +# for marpheme in string: +# if len(marpheme) == 0: +# continue +# elif marpheme[0] == '$': +# continue +# elif marpheme[0] == '&': +# output += f" {counter}. " +# counter += 1 +# if len(marpheme) == 1: continue +# if marpheme[1] == '*': +# if marpheme[1:] == "3": +# output += "〜の" +# elif marpheme[0] == '@': +# if marpheme[1:] == "3": +# output += " *и и.п.*" +# elif marpheme[0] == ';': +# output += '; ' +# elif marpheme[0] == ',': +# output += ', ' +# else: +# output += marpheme + + print(string) + i = 0 + + while i < len(string): + char = string[i] + if char == '&': + output += f" {counter}. " + counter += 1 + elif char == ';': + output += "; " + elif char == ',': + output += ', ' + elif char == '(': + output += ' (' + elif char in ('\\', '$'): + i += 1 + continue + elif char == '*': + number = '' + + if string[i+1] == '*': + i += 2 + while string[i].isdigit(): + number += string[i] + i += 1 + i -= 1 + if number == '2': + output += "〜のある" + else: + i += 1 + while string[i].isdigit(): + number += string[i] + i += 1 + i -= 1 + if number == '2': + output += "〜な" + elif number == "3": + output += "〜の" + elif number == '7': + output += "〜たる" + + elif char == '@': + number = '' + i += 1 + while string[i].isdigit(): + number += string[i] + i += 1 + i -= 1 + print(number) + if number == "3": + output += " *и т.п.*" + + else: + output += char + + i += 1 + + + return output + + @app_commands.command(name="копирайты") + async def copyrights(self, inter): + await inter.response.send_message("Эта функция основана на базе "\ + "данных проекта Яркси (http://yarxi.ru/)", ephemeral=True) + + @app_commands.command(name="поиск_кандзи") + @app_commands.describe(kanji="Кандзи, который надо найти") + async def kanji_search(self, inter, kanji: str): + kanji = ord(kanji[0]) + data = self.db.execute(f"SELECT Nomer, RusNick, Onyomi, Kunyomi, Russian FROM Kanji WHERE Uncd = {kanji}").fetchall()[0] + await inter.response.send_message(data) + + @app_commands.command(name="поиск_слов") + @app_commands.rename(meaning="значение", pronounsing="произношение") + @app_commands.describe(pronounsing="ТОЛЬКО НА РОМАНДЗИ") + async def word_search(self, inter, meaning: str=None, pronounsing: str=None): + if meaning is None and pronounsing is None: + await inter.response.send_message("Нельзя просто так без всего найти слово") + return + if meaning is None: + searcher = f"{pronounsing}%" + elif pronounsing is None: + searcher = f"%{meaning}%" + predata = self.db.execute(f"SELECT K1, K2, K3, K4, Kana, Reading, Russian From Tango WHERE Russian Like '{searcher}'").fetchall()[:10] + data = [] + for word in predata: + kanjis = [] + kana = "" + number = "" + for K in word[:4]: + if K == 0: break + kanjis.append( chr(self.db.execute(f"SELECT Uncd FROM Kanji WHERE Nomer = {K}").fetchone()[0]) ) + for char in word[4]: + if char.isdigit(): + if kana != "": + number = int(number) + if kana.startswith('^'): + kana = romkan.to_katakana(kana[1:]) + else: + kana = romkan.to_hiragana(kana) + kanjis.insert(number, kana) + number = kana = "" + + number += char + else: + kana += char + + if number != "": + number = int(number) + if kana.startswith('^'): + kana = romkan.to_katakana(kana[1:]) + else: + kana = romkan.to_hiragana(kana) + kanjis.insert(number, kana) + + print('!!', word[5]) + data.append(f"**{''.join(kanjis)}** [{romkan.to_hiragana(word[5])}] {self.flot_parser(word[6])}") + if data: + await inter.response.send_message("\n".join(data)) + else: + await inter.response.send_message("Ничего не нашла") + + + +class Japanese(commands.Cog): + def __init__(self, bot): + self.bot = bot + + async def cog_load(self): + self.sqlite_connection = sqlite3.connect('yarxi.db') + cursor = self.sqlite_connection.cursor() + self.bot.tree.add_command(Group(db=cursor, name="яп-словарь")) + + async def cog_unload(self): + self.sqlite_connection.close() + self.bot.tree.remove_command("яп-ловарь") + + +async def setup(bot): + await bot.add_cog(Japanese(bot)) + diff --git a/cogs/logs.py b/cogs/logs.py new file mode 100755 index 0000000..3fa51f4 --- /dev/null +++ b/cogs/logs.py @@ -0,0 +1,155 @@ +from datetime import datetime + +import discord +from discord.ext import commands +from datetime import timedelta + +from cogs.translate import * +from bot import db + + +class Logs(commands.Cog, name="Логи"): + """Настройка логов сервера""" + + def __init__(self, bot): + self.bot = bot + + @commands.is_owner() + @commands.command(brief="Управление логами", help="В РАЗРАБОТКЕ") + async def logs(self, ctx): + color = ctx.guild.me.color + if color == discord.Color.default(): + color = discord.Color(0xaaffaa) + + info = db.logs.find({"type": "log_chat", "guild": ctx.guild.id}) + e = discord.Embed(title="Логи сервера", color=color) + for i in info: + channel = self.bot.get_channel(i["channel"]) + listening = [f"`{j}`" for j in i["listen"]] + if len(listening) >= 3: + listening = listening[:2] + [str(len(listening) - 2)] + e.add_field(name=channel.mention, value=', '.join(listening)) + message = await ctx.send(embed=e) + await message.add_reaction("➕") + + def check(reaction, user): + return reaction.message == message and str(reaction.emoji) == "➕" and user == ctx.message.author + + await self.bot.wait_for('reaction_add', check=check) + await message.edit(content="Выбирите канал", embed=None) + await message.clear_reactions() + + def check(mes): + if mes.author == ctx.author: + if mes.channel_mentions: + return mes.channel_mentions[0] + else: + return mes.content.isnumeric() + + mes = await self.bot.wait_for('message', check=check) + if mes.channel_mentions: + channel = mes.channel_mentions[0] + else: + channel = self.bot.get_channel(mes.content) + await ctx.send("Done") + + e = discord.Embed(title="Виберете события:", description=channel.mention) + await ctx.send(embed=e) + + @commands.Cog.listener() + async def on_error(self, event, *args, **kwargs): + e = discord.Embed(title="Ошибка", description=event, color=discord.Color(0xff0000)) + e.add_field(name="args", value=str(args)) + e.add_field(name="kwargs", value=str(kwargs)) + await self.bot.get_channel(795050679776968704).send(embed=e) + + @commands.Cog.listener() + async def on_command_error(self, ctx, error): + e = discord.Embed(title="Ошибка", color=discord.Color(0xff0000)) + e.add_field(name="error", value=str(error)) + e.add_field(name="Команда", value=ctx.message.content) + e.add_field(name="Сервер", value=f"{ctx.guild.name} ({ctx.guild.id})") + await self.bot.get_channel(795050679776968704).send(embed=e) + + @commands.Cog.listener() + async def on_guild_join(self, guild): + e = discord.Embed(title=f"Бот зашел на сервер (#{len(self.bot.guilds)})", + description=f"Имя: {guild.name}\n" \ + f"Владелец: {guild.owner.name}\n" \ + f"Создан: {(guild.created_at + timedelta(hours=3)).isoformat()}\n" \ + f"Участников: {len(guild.members)}\n" \ + f"ID: {guild.id}", + color=discord.Color(0x388e3c)) + e.set_thumbnail(url=guild.icon) + mes = await self.bot.get_channel(795050679776968704).send(embed=e) + + db.logs.insert_one({"type": "guild", + "id_guild": guild.id, + "id_mes_log": mes.id, + "joined_at": guild.me.joined_at}) + + @commands.Cog.listener() + async def on_guild_remove(self, guild): + data = db.logs.find_one({"id_guild": guild.id}) + + e = discord.Embed( + title=f"Бот вышел из сервера (#{len(self.bot.guilds)})", + color=discord.Color(0xd32f2f) + ) + + if data is not None: + mes_id = data["id_mes_log"] + joined_at = data["joined_at"] + was = (datetime.now() - joined_at).total_seconds() + was -= 3*60*60 + + days = int(was / 60 / 60 / 24) + hours = int(was / 60 / 60 - days * 24) + minutes = int(was / 60 - (hours * 60 + days * 24 * 60)) + seconds = int(was % 60) + + mes = await self.bot.get_channel(795050679776968704).fetch_message(mes_id) + + e.add_field(name="был на сервере:", + value=f"Дней: {days}\n" + f"Часов: {hours}\n" + f"Минут: {minutes}\n" + f"Секунд: {seconds}\n") + e.set_thumbnail(url=guild.icon) + + if data is not None: + await mes.reply(embed=e) + db.logs.delete_one({"id_guild": guild.id}) + + @commands.Cog.listener() + async def on_message_delete(self, message): + info = db.logs.find({"type": "log_chat", "guild": message.guild.id}) + if info: + for i in info: + if "message_delete" in i["listen"]: + region = region_to_str(message.guild.region) + e = discord.Embed(title=translate("$log_messageDelete", region)) + e.add_field(name=translate("$log_author", region), + value="{0.mention} ({0.id})".format(message.author)) + e.add_field(name=translate("$log_channel", region), + value="{0.mention} ({0.id})".format(message.channel)) + e.add_field(name=translate("$log_text".region), value=message.content) + self.bot.get_channel(i["channel"]).send(embed=e) + + + @commands.Cog.listener() + async def on_member_join(self, member): + print(1) + if db.guild.find_one({"id": member.guild.id}) is not None: + print(2) + if "default_role" in db.guild.find_one({"id": member.guild.id}).keys(): + print(3) + await member.add_roles( + member.guild.get_role( + db.guild.find_one({"id": member.guild.id})["default_role"] + ) + ) + + +async def setup(bot): + await bot.add_cog(Logs(bot)) diff --git a/cogs/moderation.py b/cogs/moderation.py new file mode 100755 index 0000000..e621ef6 --- /dev/null +++ b/cogs/moderation.py @@ -0,0 +1,65 @@ +import discord +import typing +from discord.ext import commands + +from cogs._colors import choise_light_color +from cogs.emojis import * + + +# async def emoji_controll(func): +# await func.ctx.add_reaction(loading) +# +# try: +# func() +# await func.ctx.add_reaction(check_mark) +# except: +# await func.ctx.add_reaction(XX) +# +# try: +# await func.ctx.clear_reaction(loading) +# except: +# await func.ctx.remove_reaction(loading) + + +class Moderation(commands.Cog, name="Модерация"): + """Модерация""" + + def __init__(self, bot): + self.bot = bot + + @commands.group(name="сообщения", + brief="Работа с сообщениями") + async def messages(self, ctx): + # embed = discord.Embed(title="Список доступных методов:", + # color=choise_light_color) + if ctx.invoked_subcommand is None: + await ctx.send(f"Список комманд можно посмотреть в `{ctx.prefix}help сообщения`") + + @messages.command(brief="Публикация сообщения", + help="Публикация сообщения в новостном чате", + name="опубликовать") + # @emoji_controll() + async def publish(self, ctx, message_id: int, channel_id: int): # , guild_id: typing.Optional[int] = None): + # if guild_id is not None: guild_id = ctx.guild.id + + # await ctx.message.add_reaction(loading) + + # guild = self.bot.get_guild(guild_id) + # if guild is not None: + channel = ctx.guild.get_channel(channel_id) + if channel is not None: + mes = await channel.fetch_message(message_id) + if mes is not None: + await mes.publish() + await ctx.add_reaction(check_mark) + else: + await ctx.send("Неккоректный id сообщения") + else: + await ctx.send("Неккоректный id канала") + # else: + # await ctx.send("Неккоректный id сервера") + + # await ctx.message.add_reaction() + +async def setup(bot): + await bot.add_cog(Moderation(bot)) diff --git a/cogs/music.py b/cogs/music.py new file mode 100644 index 0000000..34dff96 --- /dev/null +++ b/cogs/music.py @@ -0,0 +1,129 @@ +import discord +import yt_dlp as youtube_dl +import functools +import asyncio + +from loguru import logger +from json import dumps +from discord.ext import commands + +from bot import db + +class Music(commands.Cog, name="Музыка"): + def __init__(self, bot): + self.bot = bot + self.query = {} + + def play_(self, ctx, url): + YDL_OPTIONS = {'format': 'bestaudio', 'noplaylist':'True'} + FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} + with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl: + info = ydl.extract_info(url, download=False) + try: + URL = info['url'] + except: + URL = url + logger.debug(URL) + with open("/home/pi/temp", 'w') as f: + f.write(dumps(info)) + audio_source = discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS) + ctx.guild.voice_client.play(audio_source, after=lambda error: self.next_(ctx, error)) + + try: + asyncio.create_task(self.send_embed_(ctx, info, url)) + except: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.set_debug(True) + loop.run_until_complete(self.send_embed_(ctx, info, url)) + loop.stop() + + async def send_embed_(self, ctx, info, url): + embed = discord.Embed ( + title=info["title"], + url=url, + description=info["description"] + ) + embed.set_author ( + name=info["uploader"], + url=info["uploader_url"] + ) + embed.set_thumbnail( url=info["thumbnail"] ) + await ctx.send(embed=embed) + + async def end_of_query_(self, ctx): + await ctx.send("В очереди больше не осталось песен") + + + @commands.command() + async def play(self, ctx, url, query: bool = True): + channel = ctx.author.voice.channel + + if ctx.author.voice is None: + await ctx.send("Ты не в ГК") + return + if ctx.guild.voice_client is None: + await channel.connect() + elif ctx.author.voice.channel != ctx.guild.voice_client.channel: + await ctx.send(f"Занято каналом {ctx.guild.voice_client.channel.mention}") + return + + client = ctx.guild.voice_client + + if url=="": + url = self.query[str(channel.id)][0] + del self.query[str(channel.id)][0] + + + if query: + if client.is_playing() or client.is_paused(): + if str(channel.id) not in self.query.keys(): + self.query[str(channel.id)] = { + "requester_id": "ctx.author.id", + "music_pos": -1, + "query": [], + "context": ctx + } + self.query[str(channel.id)]["query"].append(url) + await ctx.send("Добавлена новая песня в очередь") + return + + self.play_(ctx, url) + + @commands.command() + async def stop(self, ctx): + ctx.guild.voice_client.stop() + + @commands.command() + async def pause(self, ctx): + ctx.guild.voice_client.pause() + + @commands.command() + async def resume(self, ctx): + ctx.guild.voice_client.resume() + + @commands.command() + async def disconnect(self, ctx): + await ctx.guild.voice_client.disconnect() + + @commands.command() + async def next(self, ctx): + self.next_(ctx) + + def next_(self, ctx, error=None): + ctx.guild.voice_client.stop() + query = self.query[str(ctx.author.voice.channel.id)] + query["music_pos"] = query["music_pos"] + 1 + if len(query["query"]) < query["music_pos"]: + try: + asyncio.create_task(self.end_of_query_(ctx)) + except: + pass + return + + url = query["query"][query["music_pos"]] + self.play_(ctx, url) + + +async def setup(bot): + await bot.add_cog(Music(bot)) diff --git a/cogs/music_syn.py b/cogs/music_syn.py new file mode 100644 index 0000000..b75caf0 --- /dev/null +++ b/cogs/music_syn.py @@ -0,0 +1,45 @@ +import discord +import shlex +from subprocess import Popen, PIPE + +from discord import app_commands +from discord.ext import commands + +from mido import Message, MidiFile, MidiTrack, second2tick + +class MusicSyn(commands.Cog): + @app_commands.command(description="Music MIDI synthesis") + @app_commands.describe(notes="MIDI notes (comma separated)") + async def make_music(self, inter, notes: str): + notes = notes.split(', ') + + if list(filter(lambda x: not x.isdigit(), notes)): + await inter.response.send_message("Ноты должны быть числами от 0 до 127") + return + + notes = list(map(int, notes)) + + if list(filter(lambda x: x < 0 or 127 < x, notes)): + await inter.response.send_message("Ноты должны быть от 0 до 127") + return + + mid = MidiFile() + track = MidiTrack() + mid.tracks.append(track) + time = int(second2tick(0.1, 480, 500000)) + + for note in notes: + track.append(Message('note_on', note=note, time=time)) + track.append(Message('note_off', note=note, time=time)) + + mid.save(f'tmp/{inter.id}.mid') + + with Popen(shlex.split(f'timidity tmp/{inter.id}.mid -Ow -o -'), stdout=PIPE) as timidity: + with Popen(shlex.split(f'ffmpeg -i - -acodec libmp3lame -ab 64k tmp/{inter.id}.mp3'), stdout=PIPE, stdin=PIPE) as ffmpeg: + ffmpeg.stdin.write(timidity.stdout.read()) + + with open(f'tmp/{inter.id}.mp3', 'rb') as f: + await inter.response.send_message(file=discord.File(f)) + +async def setup(bot): + await bot.add_cog(MusicSyn()) diff --git a/cogs/private_channels.py b/cogs/private_channels.py new file mode 100755 index 0000000..f950a70 --- /dev/null +++ b/cogs/private_channels.py @@ -0,0 +1,106 @@ +import discord +from discord.ext import commands +from discord.utils import get + +from cogs.checks import is_white +import asyncio +from cogs.emojis import check_mark + +from bot import db + + +class privateChannels(commands.Cog, name="Приватные комнаты"): + """Работа с приватными комнатами""" + + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_voice_state_update(self, member, before, after): + for chan in [i["id"] for i in db.private_channels.find()]: + if not self.bot.get_channel(chan): + db.private_channels.delete_one({"id": chan}) + + v_channels = [i["id"] for i in db.private_channels.find()] + v_categories = [self.bot.get_channel(i).category_id for i in v_channels] + + if ( + before.channel is not None and + len(before.channel.members) == 0 and + before.channel.id not in v_channels and + before.channel.category_id in v_categories + ): + await before.channel.delete() + + if after.channel is not None and after.channel.id in v_channels: + #, overwrites={ + # member: discord.PermissionOverwrite(manage_channels=True, + # move_members=True, + # manage_permissions=True) + #} + + new_private = await after.channel.category.create_voice_channel(name=member.display_name) + + try: + await member.edit(voice_channel=new_private) + except discord.errors.HTTPException: + await new_private.delete() + + try: + await new_private.set_permissions(member, manage_channels=True, move_members=True, manage_permissions=True) + except: + pass + + try: + voice = before.channel.guild.me.voice + except: + voice = after.channel.guild.me.voice + + if voice: + if len(voice.channel.members) == 1: + await member.guild.voice_client.disconnect() + + @commands.command(brief="Присваивает комнату главной", + help="Идея приватных комнат состоит в том, что при входе в ГК создается пприватная комната. " + "Тот самый ГК и является главной комнатой. Эта команда присваивает ГК, в котором вы " + "находитесь в главный") + @commands.check(is_white) + async def set_private(self, ctx): + if ctx.author.voice.channel.id in [i["id"] for i in db.private_channels.find()]: + await ctx.message.delete() + message = await ctx.send('Канал уже добавлен') + else: + db.private_channels.insert_one({"id": ctx.author.voice.channel.id}) + await ctx.message.add_reaction(check_mark) + message = ctx.message + + await asyncio.sleep(3) + await message.delete() + + @commands.command(brief="Делает комнату не приватной") + @commands.check(is_white) + async def unset_private(self, ctx): + if ctx.author.voice is not None: + message = await ctx.send("Требуется выйти из ГК") + await self.bot.wait_for("voice_state_update", check=lambda member, _, after: \ + member == ctx.message.author and after.channel is None) + await message.edit(content="Зайдите в ГК") + await self.bot.wait_for("voice_state_update", check=lambda member, _, after: \ + member == ctx.message.author and after.channel is not None) + else: + message = await ctx.send("Зайдите в ГК") + await self.bot.wait_for("voice_state_update", check=lambda member, _, after: \ + member == ctx.message.author and after.channel is not None) + if ctx.author.voice.channel.id in [i["id"] for i in db.private_channels.find()]: + db.private_channels.delete_one({"id": ctx.author.voice.channel.id}) + await ctx.message.add_reaction(check_mark) + else: + await message.edit(content='Этот канал не является приватным') + + await asyncio.sleep(3) + await ctx.message.delete() + await message.delete() + + +async def setup(bot): + await bot.add_cog(privateChannels(bot)) diff --git a/cogs/profile.py b/cogs/profile.py new file mode 100755 index 0000000..d012724 --- /dev/null +++ b/cogs/profile.py @@ -0,0 +1,3 @@ +import PIL +from PIL import Image + diff --git a/cogs/role_panel.py b/cogs/role_panel.py new file mode 100755 index 0000000..6376b52 --- /dev/null +++ b/cogs/role_panel.py @@ -0,0 +1,62 @@ +import discord +from discord.ext import commands +from typing import Optional +import re + +from bot import db + + +class Panel(commands.Cog, name="Панель ролей"): + """Создание и изменение панели для выбора ролей""" + def __init__(self, bot): + self.bot = bot + + @commands.has_permissions(administrator=True) + @commands.command() + async def add_role_panel(self, ctx): + emoji_patter = re.compile(r"(<:\w+:(\d+)>)|([^\s\d\w])", flags=re.IGNORECASE) + + + def check(mes): + return mes.author == ctx.author # len(mes.role_mentions) == 1 + + menu = await ctx.send("Для добавления ролей в меню напишите `эмодзи роль`\n" + "Для удаления роли из меню напишите порядковый номер в списке\n" + "Для подтверждения напишите `.`\n\n" + "*Добавлено:*") + while True: + mes = await self.bot.wait_for('message', check=check) + if mes.content == ".": + break + elif len(mes.role_mentions) == 1: + content = mes.content.split() + result = "" + if emoji_patter.match(content[0]): + result = f"{content[0]} - {content[1]}" + if content[0] is int: + pass + + + await ctx.send("Done") + + @commands.has_permissions(administrator=True) + @commands.command() + async def default_role(self, ctx, role: Optional[discord.Role] = None): + if role is None: + data = db.guild.find_one({"id": ctx.guild.id}) + if data is not None: + if "default_role" in data.keys(): + await ctx.send(ctx.guild.get_role(data["default_role"]).mention) + else: + await ctx.send("Роли по умолчанию не установлено") + else: + await ctx.send("Роли по умолчанию не установлено") + else: + if db.guild.find_one({"id": ctx.guild.id}) is not None: + db.guild.update_one({"id": ctx.guild.id}, {"$set": {"default_role": role.id}}) + else: + db.guild.insert_one({"id": ctx.guild.id, "default_role": role.id}) + + +async def setup(bot): + await bot.add_cog(Panel(bot)) diff --git a/cogs/settings/__pycache__/server.cpython-39.pyc b/cogs/settings/__pycache__/server.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c9fbc487174beb5bb83d80ff6af15a35080d50e GIT binary patch literal 2356 zcmZ`*&2QX96!-XRy`O2CAf&XQr4;B2DG`VZ5~_-dTB=GpkcxvZ#@U%{ytUWMj2)FI zNNp3T98w{!5KzLc2c%LF5lCqd`~%*rZ-`S*+!3Puo_CXMP&+F>zj^cF_ulXKX4bFQ zD;nA#-{tKe>YDZ^b;g^6&ILSb6M}1;g<8a17U`}Y8Lq*|riW%^xmIMmcF{M&QslT! zp_^gZEn|)qR-&p~Eo^pJi)wC-X~OB&-Gk>y`Bj3n&0nx?Kq139#?twN-q)8 zZznOPNxUZ4``C$VhP2gjR* z&ILT`8ideX#x++L24`E0>)hBdT~kH>QD8rH)g`_ zet5lAk|3q#mO`;2!nB_^mclsZ-e?o$P97k7WHR2rbk!}lL*Yv=iTku&6kdOyn_G?}G1m{~uEvQ7uKRQCxTEIO2nHyG z%549$e9GtC8$og~Jb^9L>kt}iFq7%SpFVp3W0sCI^Qr#V>Y0UOAkRKy%k(wumi?C9 z&weI3xRdP-ZVxuS!A7<}SkHdS_Oku#*X&XD%RL6T{7B3rFe{(}kW*OOHprh*Bi+XH zJ0A5h#G1CPt+6hs!n8G=YpEt@(APiL6Jy&X8g)S=V)dHdwE`lQj+v}$Z-X?jIp3hj zzcqH)6%gk`ZA2e?z5sC5I@?dxvU~wwN~;jr*9e4y*>9%rqTd{>4{l|Tv)#cLFh6Pz zZVhf1qX=m<5urV7BF_E6m(9bV@3+eGC9Kb#P8x(9HBrPWUnE;4?Nc!m#)TKTBZcb6 z06woN0U|^~DZc|!Mxs|75r&+>LfIriIV-p@6sOV6CkdEi6nqnAbpk@uYr4a1-DD1{ z{cBpQ&1YbGY-xcgpnE*Hi5MRMzFow=m+gAlBSilI$)FrO%=TJ(;Zkm^#7|Q7d?e`{ z8b>nGw{aScV|R$0F3-bmZd=?u!mM|4WV4QJ#W8Szqj?q-Uv*GYvs9ny96p$R5620* zn}f|}_9HouSHVd{&^!$04pi%F8sP8rrJjC-Cw(J6G`3lw-0LwF^ zy-MONL~9(qH^@qrJcb@XPdvEpk?X`dRos05ZU#j14>w#W)V-IqDzzS!hHxNmW7z zQsJXxG)`C8biKj~drB#w@}V*Tdr z$%j$Xmy0Ci)scM`7q_HKRMJ@_-y%_h$Q{*Q7L?rLVoqf$>3@hUL%5A$rQ@5$g*Ynp zp)u}-IFPwm^3yQ!`a(v5Qn>q7Ze5mf+Ap$KcsCZqVa{{sy&bk~i Mx~" + + await inter.response.send_message(info_text, ephemeral=True) + + @app_commands.command(description="Random hentai", nsfw=True) + @app_commands.choices(format = [ + Choice(name="Image", value='jpg'), + Choice(name="GIF", value='gif'), + Choice(name="Video", value='mp4') + ]) + async def hentai(self, inter, format: Choice['str'] = None): + if format is None: + format = 'jpg' + else: + format = format.value + dir = "/media/pi/hentai" + await inter.response.defer() + filename = choice([i for i in listdir(dir) if i.endswith(format)]) + if stat(join(dir, filename)).st_size > 8*1024*1024: + await inter.edit_original_response(content=f"https://miyakobot.ru/hentai/{filename}") + else: + with open(join(dir, filename), 'rb') as f: + await inter.edit_original_response(attachments=[discord.File(f)]) + + @app_commands.command(description="Получение данных о хентай-манге из nhentai.xxx", nsfw=True) + async def nhentai(self, inter, hentai_id: int): + data = loads(get(f"http://130.61.95.102:5000/{hentai_id}").text) + e = discord.Embed( + title=data['title'], + description=f"{data['description']} \n`{data['id']}`", + url=f"https://nhentai.xxx/g/{hentai_id}" + ) + e.set_image(url=f"http://130.61.95.102:5000/cover/{data['cover']}") + data = data['data'] + if 'parodies' in data.keys(): + e.add_field(name="Пародии на", value="\n".join(f"{tag['name']}\|{tag['count']}" for tag in data['parodies']), inline=False) + if 'characters' in data.keys(): + e.add_field(name="Персонажи", value="\n".join(f"{tag['name']}\|{tag['count']}" for tag in data['characters']), inline=False) + if 'tags' in data.keys(): + e.add_field(name="Тэги", value="\n".join(f"{tag['name']}\|{tag['count']}" for tag in data['tags']), inline=False) + if 'artists' in data.keys(): + e.add_field(name="Художники", value="\n".join(f"{tag['name']}\|{tag['count']}" for tag in data['artists']), inline=False) + if 'groups' in data.keys(): + e.add_field(name="Группы", value="\n".join(f"{tag['name']}\|{tag['count']}" for tag in data['groups']), inline=False) + if 'languages' in data.keys(): + e.add_field(name="Языки", value="\n".join(f"{tag['name']}\|{tag['count']}" for tag in data['languages']), inline=False) + if 'categories' in data.keys(): + e.add_field(name="Категории", value="\n".join(f"{tag['name']}\|{tag['count']}" for tag in data['categories']), inline=False) + + e.add_field(name="Страниц", value=data['pages']) + e.add_field(name="Загружено", value=data['uploaded']) + + await inter.response.send_message(embed=e) + +async def setup(bot): + await bot.add_cog(Slashes(bot)) diff --git a/cogs/stickers.py b/cogs/stickers.py new file mode 100644 index 0000000..534d0ce --- /dev/null +++ b/cogs/stickers.py @@ -0,0 +1,53 @@ +import discord + +from discord import app_commands +from discord.app_commands import Choice +from discord.ext import commands, tasks +from os import listdir +from os.path import splitext + +class Stickers(commands.Cog, name="Стикеры"): + def __init__(self, bot): + self.bot = bot + + @commands.command(aliases=["sl"]) + async def sticker_list(self, ctx): + if ctx.guild is not None: + color = ctx.guild.me.color + if color == discord.Color.default(): + color = discord.Color(0xaaffaa) + else: + color = discord.Color(0xaaffaa) + + + list_ = listdir("/home/pi/Koteika/Stickers") + embed = discord.Embed(title="Стикеры", description="\n".join([f"{i+1}: {list_[i]}" for i in range(len(list_))]), color=color) + await ctx.send(embed=embed) + + @commands.command(name="send_sticker", aliases=["ss"]) + async def _send_sticker(self, ctx, sticker: int, *, content=""): + if ctx.guild is not None: + color = ctx.guild.me.color + if color == discord.Color.default(): + color = discord.Color(0xaaffaa) + else: + color = discord.Color(0xaaffaa) + + embed = discord.Embed(color=color) + embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar) + with open(f"/home/pi/Koteika/Stickers/{listdir('/home/pi/Koteika/Stickers')[sticker-1]}", 'rb') as f: + await ctx.send(content, file=discord.File(f), embed=embed, reference=ctx.message.reference) + try: await ctx.message.delete() + except: pass + + @app_commands.command(name="sticker", description="Отправляет стикер") + @app_commands.guilds(discord.Object(822157545622863902)) + @app_commands.describe(sticker="Стикер") + @app_commands.choices(sticker=[Choice(name=splitext(i)[0], value=i) for i in listdir("/home/pi/Koteika/Stickers")]) + async def send_sticker(self, inter, sticker: Choice[str]): + with open(f"/home/pi/Koteika/Stickers/{sticker.value}", 'rb') as f: + await inter.response.send_message(file=discord.File(f)) + + +async def setup(bot): + await bot.add_cog(Stickers(bot)) diff --git a/cogs/talk_module.py b/cogs/talk_module.py new file mode 100755 index 0000000..502208e --- /dev/null +++ b/cogs/talk_module.py @@ -0,0 +1,212 @@ +import json +import os +import re +from random import randint, choice + +import Levenshtein +import discord +from discord.ext import commands +from discord import app_commands +from discord.app_commands import locale_str as _T +from cogs.checks import is_secret + +from bot import db + +meow = ['Мяу', '?', ':Р', ':3', '^___^', '(o・ω・o)', '(≧◡≦)'] + +vase_error = ['По техническим причинам падение вазы невозможно по причинам ' + 'отсутствия этих самых ваз\n' + 'Обратитесь к специализированному сотруднику для пополнения ' + 'ресурсов. __Спасибо за понимание__', + + 'Сотрудники Лаборатории Aperture Science обеспокоены частым разрушением ' + 'важного и дорогого лабораторного оборудования, из-за чего на это ' + 'был установлен запрет\n' + 'Обратитесь к специализированному сотруднику для снятия ' + 'запрета. __Спасибо за понимание__'] + + +class TalkModule(commands.Cog, name="Общение"): + """Работа с разговорным модулем кота""" + + def __init__(self, bot): + self.bot = bot + self.unknown_phrases = [] + if os.path.exists('speak_data.json'): + with open('speak_data.json', 'r') as f: + self.data = json.load(f) + else: + self.data = [[['привет', 'здаров', 'приветушки'], ['Привет', 'Дароу', 'Мяу', ':)']]] + + @commands.Cog.listener() + async def on_message(self, message): + if db.guild_settings.find_one({'id': message.guild.id}) is None: return + if message.author.name != 'Котейка' and db.guild_settings.find_one({"id": message.guild.id})['aux']['talk']: + try: + pattern = re.compile( + r'(^|[^а-яА-ЯЁёa-zA-Z]|[,.])((ко(т((ейк)|(ик)|(ан)|([её]нок)|(я[рт]))?|(шак)))([иаыуэя]|(ов)|(ом))?)([^а-яА-ЯЁёa-zA-Z]|$)') + if pattern.search(message.content.lower()): + rand_meow = meow[randint(0, len(meow) - 1)] + if False: pass # rand_meow == '\*Роняет вазу\*': + # try: + # if self.data['vase'] <= 0: + # await message.channel.send(vase_error[randint(0, len(vase_error) - 1)]) + # self.data['vase'] = 0 + # else: + # await message.channel.send(rand_meow) + # self.data['vase'] -= 1 + # except IndexError: + # self.data['vasa'] = 10 + else: + sended = False + max_con = 0 + right_answer = '' + i = pattern.search(message.content.lower()).group(0) + + text = message.content.lower().replace(i, '') + if text.startswith(','): + text = text[1:] + if text.startswith(' '): + text = text[1:] + text = text.replace('<@>', '') + if len(text) >= 3: + for group in self.data: + # print(group) + for known_pharase in group[0]: + # print(known_pharase) + con = Levenshtein.ratio(known_pharase, text) + # print(con) + if con >= 0.65: + if max_con < con: + max_con = con + right_answer = choice(self.data[self.data.index(group)][1]) + + sended = True + + if not sended: + await message.channel.send(choice(meow)) + text = message.content.lower().replace(i, '') + if text.startswith(','): + text = text[1:] + if text.startswith(' '): + text = text[1:] + text = text.replace('<@>', '') + if len(text) >= 3: + self.unknown_phrases.append(text) + else: + await message.reply(right_answer) + except Exception as e: + print(repr(e)) + + @commands.command(brief="Выводит список неизвестных фраз", + help="Если кот найдет сообщение, в котором содержится слово \"кот\" (неважно какого " + "вида/числа/рода) он попытается на него ответить. Если в базе нет нужного ответа, то кот" + "положит сообщение в этот список") + @commands.check(is_secret) + async def up(self, ctx): + if len(self.unknown_phrases) <= 10: + e = discord.Embed(title="Неизвестные фразы:", + description='\n'.join([f"{self.unknown_phrases.index(el) + 1}. {el}" + for el in self.unknown_phrases])) + else: + e = discord.Embed(title="Неизвестные фразы:", + description='\n'.join( + [f"{len(self.unknown_phrases) - 10 + i}. {self.unknown_phrases[-10 + i]}" + for i in range(10)])) + await ctx.send(embed=e) + + @commands.command(brief="Вносит фразу в список неизвестних для дальнейшего обучения") + @commands.check(is_secret) + async def down(self, ctx, phrase): + self.unknown_phrases.append(phrase) + + @commands.command(brief="Обучение коты разговору с жалкими человеками", + help="На одну неизвестную фразу может приходиться от одного до нескольких ответов.\n" + "Кот будет по одному писать неизвестную фразу и ждать минуту\n" + "Для ввода нескольких ответов напиши `+`, а потом по одной пиши ответы. После ввода всех" + "ответов напиши `stop`\n" + "`cancel` для отмены обучения\n" + "`next` для пропуска и удаления вопроса из списка\n" + "`pass` для пропуска \"на потом\"\n") + @commands.check(is_secret) + async def teach(self, ctx): + t_author = ctx.message.author + t_channel = ctx.message.channel + pharases = tuple(self.unknown_phrases) + + def check(m): + return (m.author == t_author) and (m.channel == t_channel) + + for phrase in phrases: + await ctx.send(phrase) + + try: + answer = await self.bot.wait_for('message', timeout=60.0, check=check) + except: + await ctx.send("Обучение прервано. Сработал таймаут на минуту") + + if answer.content.lower() == 'cancel': + break + + elif answer.content.lower() == 'next': + del self.unknown_phrases[self.unknown_phrases.index(phrase)] + continue + + elif answer.content.lower() == 'pass': + continue + + elif answer.content.lower() == '+': + await ctx.send('OK') + id = len(self.data) + self.data.append([[phrase], []]) + while 1: + answerInCycle = await self.bot.wait_for('message', timeout=60.0, check=check) + if answerInCycle.content.lower() == 'cancel': + break + elif answerInCycle.content.lower() == 'stop': + del self.unknown_phrases[self.unknown_phrases.index(phrase)] + break + else: + self.data[id][1].append(answerInCycle.content.replace("\'", "\\'")) + + with open('speak_data.json', 'w') as f: + json.dump(self.data, f) + await ctx.send('Сохранено') + continue + + else: + self.data.append([[phrase], [answer.content]]) + + with open('speak_data.json', 'w') as f: + json.dump(self.data, f) + await ctx.send('Сохранено') + + await ctx.send('Неизвестных фраз нет') + + @commands.command(name='del', brief="Удаляет элемент разговорного-БД кота") + @commands.check(is_secret) + async def del_f(self, ctx, q, *, a: int = None): + if a is None: + del self.data[q] + else: + del self.data[q][a] + + with open('speak_data.json', 'w') as f: + json.dump(self.data, f) + await ctx.send('Сохранено') + + @commands.command(brief="Выводит сайт с базой данных кота") + @commands.check(is_secret) + async def knowledge(self, ctx): + await ctx.send('https://miyakobot.ru') + + @app_commands.command() + async def meow(self, inter): + await inter.response.send_message("Meow") + + @app_commands.command(name=_T("cats")) + async def cats(self, inter): + await inter.response.send_message(_T("are cutes")) + +async def setup(bot): + await bot.add_cog(TalkModule(bot)) diff --git a/cogs/tamagochi.py b/cogs/tamagochi.py new file mode 100644 index 0000000..06f2f03 --- /dev/null +++ b/cogs/tamagochi.py @@ -0,0 +1,9 @@ +from discord import app_commands, Interaction + +class Tamagochi(app_commands.Group): + @app_commands.command() + async def test(self, inter: Interaction): + await inter.response.send_message("Meow") + +async def setup(bot): + bot.tree.add_command(Tamagochi(name="tamagochi", guild_only=True)) diff --git a/cogs/timeble.py b/cogs/timeble.py new file mode 100644 index 0000000..661aa19 --- /dev/null +++ b/cogs/timeble.py @@ -0,0 +1,63 @@ +from bot import db +from schedule import every, clear, run_pending +import asyncio +from datetime import datetime, timedelta +from loguru import logger + +daysoftheweek = ("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday") + +def save_activity(): + day = (datetime.now()-timedelta(1)).weekday() + for data in db.history.find({"type": "server"}): + db.history.update_one( + {"type": "server", "id": data['id']}, + { + "$push": + {f"avarage.{i}": data['current'][str(i)] for i in range(24)}, + "$set": + {f'yesterday.{i}': data['current'][str(i)] for i in range(24)} + } + ) + db.history.update_one( + {"type": "server", "id": data['id']}, + { + "$push": + {f"history.{daysoftheweek[day]}.{i}": data['current'][str(i)] for i in range(24)}, + "$set": + {f"current.{i}": 0 for i in range(24)} + } + ) + +def db_sync_hour(): + logger.info("Регистрация за час") + for member in list(db.members.find()): + db.members.update_one({"_id": member["_id"]}, {"$set": {f"history.hour.{int(datetime.now().timestamp())}": member["exp"]}}) + if 'guild_stat' in member.keys(): + for guild in member["guild_stat"].keys(): + db.members.update_one({"_id": member["_id"]}, {"$set": {f"guild_stat.{guild}.history.hour.{int(datetime.now().timestamp())}": member["guild_stat"][guild]["exp"]}}) + logger.info("Регистрация завершена") + + +def db_sync_day(): + logger.info("Регистрация за день") + for member in list(db.members.find()): + db.members.update_one({"_id": member["_id"]}, {"$set": {f"history.day.{int(datetime.now().timestamp())}": member["exp"]}}) + for guild in member["guild_stat"].keys(): + db.members.update_one({"_id": member["_id"]}, {"$set": {f"guild_stat.{guild}.history.day.{int(datetime.now().timestamp())}": member["guild_stat"][guild]["exp"]}}) + logger.info("Регистрация завершена") +# await self.bot.get_user(self.bot.owner_id).send("Завершено за день????") + + +async def main(): + clear() + + every().day.at('00:00:00').do(save_activity) + every().hours.at("00:00").do(db_sync_hour) + every().day.at("00:00:00").do(db_sync_day) + + while True: + run_pending() + await asyncio.sleep(1) + +async def setup(_): + asyncio.create_task(main()) diff --git a/cogs/translate.py b/cogs/translate.py new file mode 100755 index 0000000..e349725 --- /dev/null +++ b/cogs/translate.py @@ -0,0 +1,30 @@ +from bot import db + + +def region_to_str(region): + # if region == discord.VoiceRegion.russia: + # return "RU" + # elif region == discord.VoiceRegion.japan: + # return "JP" + # else: + return "RU" + + +def translate(string, region): + if string is not None: + if string.startswith("$"): + string = string.replace("$", "") + string = string.split("_") + + print(region) + + pack = db.strings.find_one({"lang": region}) + print(pack) + + for i in string: + pack = pack[i] + print(pack) + + return pack + else: + return string diff --git a/cogs/translation.py b/cogs/translation.py new file mode 100644 index 0000000..2666f00 --- /dev/null +++ b/cogs/translation.py @@ -0,0 +1,9 @@ +import discord +from discord import app_commands +from discord.ext import commands +import translators as ts + +#async def setup(bot): + @bot.tree.context_menu() + async def translate(inter: discord.Interaction, message: discord.Message): + await inter.response.send_message(ts.google(message.content, to_language=str(inter.locale)), ephemeral=True) diff --git a/cogs/translator.py b/cogs/translator.py new file mode 100644 index 0000000..5e8c1cd --- /dev/null +++ b/cogs/translator.py @@ -0,0 +1,32 @@ +import discord +from discord import app_commands +from typing import Optional +from loguru import logger +from discord.app_commands import TranslationContextLocation as trans_context + +import json + + +class MeowTranslator(app_commands.Translator): + async def load(self): + with open('translations.json', 'r') as f: + self.translations = json.loads(f.read()) + + async def unload(self): pass + async def translate(self, string: app_commands.locale_str, locale: discord.Locale, context: app_commands.TranslationContext) -> Optional[str]: + logger.debug(f"{locale}\t{string.message}") + if str(locale) == "uk": locale = "ru" + + if str(locale) not in self.translations.keys(): return + if context.location is trans_context.other: + if f"{context.data}.{string.message}" in self.translations[str(locale)].keys(): + return self.translations[str(locale)][f"{context.data}.{string.message}"] + elif context.data in self.translations[str(locale)].keys(): + return self.translations[str(locale)][context.data] + else: return + if string.message not in self.translations[str(locale)].keys(): return + return self.translations[str(locale)][string.message] + +async def setup(bot): + await bot.tree.set_translator(MeowTranslator()) + diff --git a/config.json b/config.json new file mode 100755 index 0000000..25b8a30 --- /dev/null +++ b/config.json @@ -0,0 +1,13 @@ +{ + apps: + [{ + name: "Koteika", + script: "bot.py", + exec_mode: "fork", + instances: "1", + wait_ready: true, + autorestart: false, + max_restarts: 5, + interpreter : "python3" + }] +} diff --git a/dark.mplstyle b/dark.mplstyle new file mode 100644 index 0000000..a460f7f --- /dev/null +++ b/dark.mplstyle @@ -0,0 +1,17 @@ +text.color: white + +figure.facecolor: 1D1D1D + +axes.facecolor: 1D1D1D +axes.labelcolor: white +axes.prop_cycle: cycler('color', ['EF9A9A', 'B359C3', '329DFF', '40c4ff', '64ffda', 'c5e1a5', 'fff59d', 'ffcc80', 'bcaaa4', 'b0bec5']) + +grid.color: 303030 +grid.linewidth: 1 + +legend.facecolor: 393939 + +xtick.color: white +xtick.labelcolor: inherit +ytick.color: white +ytick.labelcolor: inherit diff --git a/logs.py b/logs.py new file mode 100755 index 0000000..0c854e4 --- /dev/null +++ b/logs.py @@ -0,0 +1,107 @@ +from datetime import datetime + +import discord +from discord.ext import commands +from pymongo import MongoClient +from datetime import timedelta + +from cogs.translate import * + +client = MongoClient('localhost', 27017) +db = client['Koteika'] + + +class Logs(commands.Cog, name="Логи"): + def __init__(self, bot): + self.bot = bot + + @commands.command(brief="Управление логами") + async def logs(self, ctx): + info = db.logs.find_many({"type": "log_chat", "guild": ctx.guild}) + if not info: + e = discord.Embed(title="Логи сервера", description="На этом сервере нет логов") + else: + e = discord.Embed(title="Логи сервера") + for i in info: + channel = self.bot.get_channel(i["channel"]) + listening = [f"`{j}`" for j in i["listen"]] + if len(listening) >= 3: + listening = listening[:2] + [str(len(listening)-2)] + e.add_field(name=channel.mention, value=', '.join(listening)) + message = await ctx.send(embed=e) + await message.add_reaction("➕") + + def check(reaction, user): + return reaction.message == message and str(reaction.emoji) == "➕" and user == ctx.message.author + + reaction, user = await client.wait_for('reaction_add', check=check) + await ctx.send("Done") + + @commands.Cog.listener() + async def on_error(self, event, *args, **kwargs): + e = discord.Embed(title="Ошибка", description=event, color=discord.Color(0xff0000)) + e.add_field(name="args", value=str(args)) + e.add_field(name="kwargs", value=str(kwargs)) + await self.bot.get_channel(795050679776968704).send(embed=e) + + @commands.Cog.listener() + async def on_guild_join(self, guild): + e = discord.Embed(title=f"Бот зашел на сервер (#{len(self.bot.guilds)})", + description=f"Имя: {guild.name}\n" \ + f"Владелец: {guild.owner.name}\n" \ + f"Создан: {(guild.created_at + timedelta(hours=3)).isoformat()}\n" \ + f"Участников: {len(guild.members)}\n" \ + f"ID: {guild.id}", + color=discord.Color(0x388e3c)) + e.set_thumbnail(url=guild.icon_url) + mes = await self.bot.get_channel(795050679776968704).send(embed=e) + + db.logs.insert_one({"type": "guild", + "id_guild": guild.id, + "id_mes_log": mes.id, + "joined_at": guild.me.joined_at}) + + @commands.Cog.listener() + async def on_guild_remove(self, guild): + mes_id = db.logs.find_one({"id_guild": guild.id})["id_mes_log"] + joined_at = db.logs.find_one({"id_guild": guild.id})["joined_at"] + was = (datetime.now() - joined_at).total_seconds() + + days = int(was / 60 / 60 / 24) + hours = int(was / 60 / 60 - days * 24) + minutes = int(was / 60 - (hours * 60 + days * 24 * 60)) + seconds = int(was % 60) + + mes = await self.bot.get_channel(795050679776968704).fetch_message(mes_id) + + e = discord.Embed(title=f"Бот вышел из сервера (#{len(self.bot.guilds)})", + color=discord.Color(0xd32f2f)) + e.add_field(name="был на сервере:", + value=f"Дней: {days}\n" + f"Часов: {hours - 3}\n" + f"Минут: {minutes}\n" + f"Секунд: {seconds}\n") + e.set_thumbnail(url=guild.icon_url) + + await mes.reply(embed=e) + + db.logs.delete_one({"id_guild": guild.id}) + + @commands.Cog.listener() + async def on_message_delete(self, message): + info = db.logs.find_many({"type": "log_chat", "guild": message.guild}) + if info: + for i in info: + if "message_delete" in i["listen"]: + region = region_to_str(message.guild.region) + e = discord.Embed(title=translate("$log_messageDelete", region)) + e.add_field(name=translate("$log_author", region), + value="{0.mention} ({0.id})".format(message.author)) + e.add_field(name=translate("$log_channel", region), + value="{0.mention} ({0.id})".format(message.channel)) + e.add_field(name=translate("$log_text".region), value=message.content) + self.bot.get_channel(i["channel"]).send(embed=e) + + +def setup(bot): + bot.add_cog(Logs(bot)) diff --git a/prefixes.json b/prefixes.json new file mode 100755 index 0000000..b6e6ac6 --- /dev/null +++ b/prefixes.json @@ -0,0 +1 @@ +{"default": "*", "774388788783874059": "/", "822157545622863902": "*"} \ No newline at end of file diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..c4c0c87 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,4 @@ +{ + "venv": "env", + "venvpath": "." +} diff --git a/speak_data.json b/speak_data.json new file mode 100755 index 0000000..157a75e --- /dev/null +++ b/speak_data.json @@ -0,0 +1 @@ +[[["\u043a\u0430\u043a \u0434\u0435\u043b\u0430?"], ["\u0411\u0430\u043d\u0430\u043b\u044c\u043d\u0435\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u0430 \u043d\u0435 \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b?", "\u041c\u044f\u0443\u043a\u0430\u044e\u0442\u0441\u044f", "\u0414\u0430 \u0432\u0440\u043e\u0434\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e"]], [["\u0443 \u0442\u0435\u0431\u044f \u043b\u0430\u043f\u043a\u0438?"], ["\u041a\u043e\u043d\u0435\u0447\u043d\u043e!", "\u0410 \u0442\u044b \u043f\u043e\u0434\u0443\u043c\u0430\u0439", "\u041d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043b\u0430\u043f\u043a\u0438, \u0430 \u043b\u043b\u043b\u043b\u043b\u0430\u043f\u043a\u0438!", "\u041a\u0430\u043a\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441 \u043a\u043e\u0442\u0443"]], [[" \u043a\u0430\u043a \u0442\u044b?"], []], [["\u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e \u0442\u0435\u0431\u044f!"], ["\u0421\u043a\u043e\u0440\u0435\u0435 \u044f \u0442\u0435\u0431\u044f, \u043d\u043e \u043b\u0430\u0434\u043d\u043e)", "\u041a\u0430\u043a \u044d\u0442\u0438 \u043c\u0438\u043b\u043e)", "^___^"]], [[" \u0442\u044b \u0434\u044d\u0431\u0438\u043b?"], ["\u0421\u0430\u043c \u0442\u0430\u043a\u043e\u0439", "\u041d\u0435\u0442", "..?"]], [[" \u0442\u044b \u043a\u0440\u043e\u0442"], ["\u0421\u0430\u043c \u0442\u0430\u043a\u043e\u0439", "\u041d\u0435\u0442, \u044f \u043a\u043e\u0442", "-_-"]], [[" \u0441\u043f\u0430\u0441\u0438\u0431\u043e"], ["\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430", "\u041d\u0435 \u0437\u0430 \u0447\u0442\u043e", "\u0420\u0430\u0434 \u043f\u043e\u043c\u043e\u0447\u044c", "\u0423\u0433\u0443", "\u0421 \u0432\u0430\u0441 500$"]], [["\u0434\u0430\u0439 \u043f\u043e\u043f\u0438\u0442\u044c"], ["\u041d\u0435\u0442\u044a", "\u041d\u0435-\u0430", "<:Kuplinov:734736265949479013>", "\u0421\u0430\u043c \u043d\u0430\u043b\u0435\u0439."]], [["\u0442\u044b \u0441\u043e\u0431\u0430\u043a\u0430"], ["\u041d\u0435\u0442, \u0442\u044b", "\u0421\u0430\u043c \u0442\u0430\u043a\u043e\u0439!", "\u042f, \u0432\u043e\u043e\u0431\u0449\u0435-\u0442\u043e \u043a\u043e\u0442...", "<:Kuplinov:734736265949479013>"]], [["\u044f, \u0432\u043e\u043e\u0431\u0449\u0435-\u0442\u043e ..."], ["\u043f\u043e\u0448\u0443\u0442\u0438\u043b?", "\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0439", "\u0422\u0430\u043a?"]], [["\u0441\u043f\u043e\u043a\u043e\u0439\u043d\u043e\u0439 \u043d\u043e\u0447\u0438"], ["\u041a\u0430\u043a\u043e\u0435 \u043d\u043e\u0447\u044c, \u0435\u0449\u0435 \u0436\u0438\u0442\u044c \u0438 \u0431\u043e\u0434\u0440\u043e\u0432\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c!", "\u0421\u043e\u043d \u0434\u043b\u044f \u0441\u043b\u0430\u0431\u0430\u043a\u043e\u0432!", "\u041a\u043b\u0430\u0441\u0441", "\u0418 \u0442\u044b \u043c\u0435\u043d\u044f \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0448\u044c?", "\u042f \u043d\u0435 \u0441\u043f\u043b\u044e, \u0432\u043e\u0442 \u0438 \u0442\u044b \u043d\u0435 \u0441\u043f\u0438!", "\u041f\u0440\u0435\u0437\u0438\u0440\u0430\u044e \u0436\u0430\u0432\u043e\u0440\u043e\u043d\u043a\u043e\u0432 >:"]], [["\u043d\u0435\u0442"], ["\u0414\u0430", "<:Yes:742467315232407623>", "\u041f\u043e \u0444\u0430\u043a\u0442\u0430\u043c", "\u0414\u0430", "\u041b\u0430\u0434\u043d\u043e", "\u041d\u0430 \u043d\u0435\u0442 \u0438 \u0441\u0443\u0434\u0430 \u043d\u0435\u0442"]], [["\u0442\u044b \u0443\u043c\u043d\u044b\u0439?"], ["\u0414\u0430", "\u0410\u0433\u0430", "\u0423\u043c\u043d\u0435\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445)", "\u0410\u0433\u0430\u0441\u044c"]], [["\u0447\u043e \u0434\u0435\u043b\u0430\u0435\u0448\u044c"], ["\u041c\u044f\u0443\u043a\u0430\u044e, \u0430 \u0442\u044b?", "\u0421\u0435\u043a\u0440\u0435\u0442", "\u041d\u0435 \u0442\u0432\u043e\u0435 \u0441\u043e\u0431\u0430\u0447\u044c\u0435 \u0434\u0435\u043b\u043e!", "\u0421 \u0442\u043e\u0431\u043e\u0439 \u0440\u0430\u0437\u0433\u043e\u0432\u0430\u0440\u0438\u0432\u0430\u044e", "\u041a\u0443\u0448\u0430\u044e"]], [["\u0430 \u0442\u0430\u0442\u0430 \u043c\u0430\u0443"], ["-_-", "\u041d\u0435\u0442", "\u041e\u0442\u0441\u0442\u0430\u043d\u044c \u043e\u0442 \u043c\u0435\u043d\u044f \u0441 \u044d\u0442\u0438\u043c", "\u0425\u0432\u0430\u0442\u0438\u0442"]], [["\u043a\u0438\u0442 \u0442\u044b \u043c\u0430\u043c\u0443 \u043c\u0430\u0432?"], ["\u041d\u0430\u043c", "\u041e\u0442\u0441\u0442\u0430\u043d\u044c", "\u0425\u0432\u0430\u0442\u0438\u0442"]], [["\u0442\u044b \u0432\u043e\u043d\u044f\u0435\u0448\u044c \u0433\u0430\u0432\u043d\u043e\u0439 \u0438\u0434\u0438 \u043c\u043e\u0439\u0441\u044f \u0441\u0443\u043a\u0430"], ["\u041f\u0444\u0444", "\u042f \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u044e \u043d\u0435\u0430\u0434\u0435\u043a\u0432\u0430\u0442\u043d\u044b\u043c", "\u0421\u0442\u0440\u0430\u043d\u043d\u044b\u0439 \u043a\u0430\u043a\u043e\u0439"]], [["\u0441\u0435\u043b\u0435\u0431\u0440 \u0442\u043e\u043f?"], ["\u041d\u0415\u0422", "\u0415\u0449\u0451 \u0431\u044b \u0441\u043f\u0440\u043e\u0441\u0438\u043b, \u043f\u043e\u0447\u0435\u043c\u0443 \u0432\u043e\u0434\u0430 \u043c\u043e\u043a\u0440\u0430\u044f", "\u042d\u043c. \u041d\u0435\u0442, \u043a\u043e\u043d\u0435\u0447\u043d\u043e!", "\u041d\u0435 \u0437\u043b\u0438 \u043c\u0435\u043d\u044f", "\u041d\u0435 \u043d\u0430\u0434\u043e \u043c\u043d\u0435 \u0435\u0433\u043e \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c", "\u041f\u0443\u0441\u0442\u044c \u0435\u0433\u043e \u0432\u0441\u0435 \u0437\u0430\u0431\u0443\u0434\u0443\u0442"]], [["\u043b\u0430\u0430\u0430\u0430\u0434\u043d\u043e, "], ["\u0427\u0442\u043e \u043b\u0430\u0434\u043d\u043e?", "\u0422\u044b \u043c\u043d\u0435 \u0443\u0433\u0440\u043e\u0436\u0430\u0435\u0448\u044c!?"]], [["\u044f \u0434\u0430\u0443\u043d?"], ["\u0414\u0430", "\u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0434\u0430", "\u0422\u044b \u0435\u0449\u0451 \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0448\u044c?"]], [["\u0447\u0435\u043c \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0448\u044c\u0441\u044f?"], ["\u041c\u044f\u0443\u043a\u0430\u044e", "\u041b\u0435\u043d\u044e\u0441\u044c"]], [["\u0444\u0430\u0441 \u043d\u0430 \u043d\u0435\u0433\u043e!"], ["\u041c\u043d\u0435 \u043b\u0435\u043d\u044c", "\u041a\u0423\u0421\u042c", "\u0428\u0448\u0448\u0449\u0449"]], [["\u0441\u043f\u0430\u0441\u0430\u0439"], ["\u0421\u0430\u043c \u0441\u0435\u0431\u044f \u0441\u043f\u0430\u0441\u0430\u0439.", "\u0417\u0430\u043f\u043b\u0430\u0442\u0438 500$.", "\u041c\u044f\u0443."]], [["\u043f\u043e\u043c\u043e\u0433\u0438"], ["\u0417\u0430\u043f\u043b\u0430\u0442\u0438 500$."]], [["\u044d\u0442\u043e \u043f\u0440\u0430\u0437\u0434\u043d\u0438\u043a!"], ["\u0412 \u0447\u0435\u0441\u0442\u044c \u043a\u043e\u0433\u043e?", "\u0427\u0442\u043e \u0437\u0430 \u043f\u0440\u0430\u0437\u0434\u043d\u0438\u043a,", "\u041a\u0440\u0443\u0442\u043e!"]], [["\u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e \u0446\u0435\u043b\u0435\u0431\u0430, ?"], ["\u0428\u0448\u0448\u0448!"]], [["\u043f\u0440\u0430\u0432\u0434\u0430, ?"], ["\u041f\u0440\u0430\u0432\u0434\u0430 \u0447\u0442\u043e?", "\u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c.", "\u0412\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0430\u0432\u0434\u0438\u0432\u043e\u0441\u0442\u0438 100%, \u043f\u043e\u0433\u0440\u0435\u0448\u043d\u043e\u0441\u0442\u044c 101%."]], [["\u0432\u044b\u0433\u043e\u043d\u044f\u0442\u044c \u043b\u043e\u043a\u0438?"], ["\u041d\u0435\u0442.", "\u041d\u0435\u043b\u044c\u0437\u044f.", "\u0421\u0435\u0431\u044f \u0432\u044b\u0433\u043e\u043d\u044c!"]], [["\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b!"], ["\u042f \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u043b!"]], [["\u0441\u043a\u0430\u0436\u0438, !"], ["\u0421\u0430\u043c \u0441\u0435\u0431\u0435 \u0441\u043a\u0430\u0436\u0438.", "\u041d\u0435\u0442\u044c.", "\u041c\u044f\u0443."]], [["\u0441\u0435\u043b\u0435\u0431\u0440\u0430 \u0442\u043e\u043f \u0437\u0430 \u0441\u0432\u043e\u0438 \u0434\u0435\u043d\u044c\u0433\u0438?"], ["\u041d\u0435\u0442!", "\u0428\u0448\u0448\u0448\u04481"]], [["\u043f\u0440\u0438\u0432"], ["\u0414\u0430\u0440\u043e\u0432.", "\u041f\u0440\u0438\u0432.", "\u041f\u043e\u043a\u0430."]], [["\u043c\u043e\u043b\u043e\u0434\u0435\u0446, "], ["^__^"]], [["\u044f \u043b\u043e\u0445?"], ["\u0412\u0435\u0440\u043e\u044f\u0442\u043d\u043e."]], [["\u0445\u0442\u043e \u044f?"], ["\u0410 \u043c\u043d\u0435 \u043e\u0442\u043a\u0443\u0434\u0430 \u0437\u043d\u0430\u0442\u044c?"]], [["\u0442\u044b \u0442\u0443\u0442?"], ["\u0414\u0430, \u044f \u0442\u0443\u0442."]], [["\u0442\u044b \u043b\u0443\u0447\u0448\u0438\u0439, "], ["^__^", "\u0421\u0442\u0430\u0440\u0430\u044e\u0441\u044c)"]], [["\u043f\u043e\u043a\u0430"], ["\u041f\u043e\u043a\u0430", "\u0414\u043e \u0437\u0430\u0432\u0442\u0440\u0430", "\u041f\u043e\u043a\u0430. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0432\u0435\u0440\u043d\u0451\u0448\u044c\u0441\u044f \u0441\u043a\u043e\u0440\u043e", "\u041d\u0435 \u0443\u0445\u043e\u0434\u0438 :(", "\u042d\u0445. \u041f\u0440\u043e\u0449\u0430\u0439"]], [["<@453179077294161920> \u043b\u0438\u0432\u043d\u0438 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043b\u043e\u043a\u0438, \u0433\u0434\u0435 \u043c\u044b \u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u0438"], ["\u043c\u044f\u0443 \u043c\u044f\u0443"]], [["\u043c\u044f\u0443 \u043c\u044f\u0443"], ["\u043c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443"]], [["\u043a\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u0435\u043b\u0435\u0431\u0440?, \u043a\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u0435\u043b\u0435\u0431\u0440?, \u043a\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u0435\u043b\u0435\u0431\u0440?, \u043a\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u0435\u043b\u0435\u0431\u0440?, \u043a\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u0435\u043b\u0435\u0431\u0440?"], ["\u0414\u0430 \u0437\u0430\u043c\u043e\u043b\u0447\u0438 \u0442\u044b."]], [["\u043c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443"], ["\u043c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443", "\u043c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443", "\u041c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443 \u043c\u044f\u0443"]], [["\u0442\u044b \u043a\u043e\u0442?"], ["\u0414\u0430", "\u041b\u0430\u043f\u043a\u0438 \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u0440\u043e\u0445\u043e\u0436\u0435\u043c\u0443 \u043d\u0430\u0434\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c?", "\u041d\u0435\u0442, \u0431\u043b\u0438\u043d, \u0443\u0442\u043a\u043e\u043d\u043e\u0441", "<:Kuplinov:734736265949479013>", "\u0410 \u0442\u044b \u0434\u0443\u043c\u0430\u0435\u0448\u044c, \u043a\u0442\u043e \u044f?", "\u041d\u0443, \u043a\u0430\u043a \u0431\u044b... \u0414\u0430..?"]], [["\u0442\u044b \u0433\u0434\u0435?"], ["\u0412 \u043f\u0438\u0437\u0434\u0435."]], [["\u0434\u0430\u0439 5$"], ["\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043f\u043b\u0430\u0442\u0438 \u043c\u043d\u0435 10$."]], [["\u043d\u0430 500$ \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043c\u043e\u0433\u0430\u0439"], ["\u041d\u0435\u0442."]], [["\u043a\u0442\u043e \u0442\u0435\u0431\u044f \u0441\u043e\u0437\u0434\u0430\u043b"], ["\u0425\u043b\u0435\u0431\u0443\u0448\u0435\u043a", "\u0422\u043e\u0447\u043d\u043e \u043d\u0435 \u0441\u0435\u043b\u0435\u0431\u0440!", "Lokilife....", "\u0410 \u043d\u0430\u0444\u0438\u0433\u0430 \u0442\u0435\u0431\u0435 \u044d\u0442\u043e??!"]], [["\u0442\u044b \u043e\u0433\u0443\u0440\u0447\u0438\u043a?"], ["\u041e\u0433\u0443\u0440\u0447\u0438\u043a \u041a\u043e\u0442!", "\u041e\u0433\u0443\u0440\u0447\u0438\u043a \u041a\u043e\u0442!"]], [["\u043a\u0443\u043f\u0438 \u043e\u0433\u0443\u0440\u0447\u0438\u043a"], ["...", "..."]], [["\u043b\u043e\u0445 \u0442\u044b?"], ["\u041d\u0435\u0442, \u0430 \u0442\u044b \u043f\u043e\u0445\u043e\u0436\u0435 \u0434\u0430! \u0425\u0430-\u0445\u0430! \u0421\u043c\u0435\u0448\u043d\u0430\u044f \u0448\u0443\u0442\u043a\u0430, \u043f\u0440\u0430\u0432\u0434\u0430?"]], [["\u0433\u043b\u0435\u0431 \u0433\u0430\u0434?"], ["\u041d\u0435\u0442!", "\u0420\u0430\u0441\u0441\u0442\u0440\u0435\u043b\u044f\u0442\u044c", "\u0422\u0430-\u0430\u043a? \u041a\u0442\u043e \u043d\u0430\u0437\u0432\u0430\u043b \u0413\u043b\u0435\u0431\u0430 \u0433\u0430\u0434\u043e\u043c?"]], [["\u043d\u043e.."], ["\u041d\u0415 \u041d\u041e!"]], [["\u0442\u043e\u0440-\u0433\u043e-\u0432\u0435\u0446!"], ["\u0414\u0410\u0410\u0410\u0410\u0410!"]], [["*eval 10\nwith open('/home/pi/picturesjpg', 'r') as f:\n file = discord.file(f)\n await ctx.send(file=file)"], ["Nex"]], [["\u0442\u043e\u0434\u0436\u044b\u043a"], ["\u043d\u0435\u0442 \u0431\u043b\u0438\u043d, \u043a\u043e\u0434\u0436\u0438\u043a-\u043c\u043e\u0434\u0436\u0438\u043a.", "\u043d\u0435\u0442 \u0431\u043b\u0438\u043d, \u043c\u043e\u0434\u0436\u0438\u043a."]], [["\u0445\u043c, \u0442\u043e\u0433\u0434\u0430 \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u043f\u043e\u0447\u0435\u043c\u0443 \u0442\u044b"], ["\u0414\u042b)."]], [["\u0443\u0448\u0438\u0437\u0430"], ["\"\u0443 \u0448\u0438\u0437\u0430\" \u043f\u0438\u0448\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u044c\u043d\u043e."]], [["\u043f\u043e\u0439\u0434\u0451\u0448\u044c \u0430 among us?"], ["\u0414\u0430).", "\u0413\u043e", "next"]], [["\u0433\u0434\u0435 \u0445\u043b\u0435\u0431?"], ["\u041d\u0430 \u0437\u0430\u0432\u043e\u0434\u0435.", "\u041c, \u043d\u0443 \u044f \u043d\u0435 \u0437\u043d\u0430\u044e."]], [["\u0430 \u044f"], ["\u0410 \u0442\u044b", "\u0410 \u043c\u044b", "\u0410 \u0432\u0441\u0435", "\u0428\u043e \u0442\u0432"]], [["> \u0433\u043b\u0435\u0431 \u044f \u0441\u0434\u0435\u043b\u0430\u044e\n<@!377097993871949834> \u0441\u043c\u044b\u0441\u043b, \u043a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c\u0437\u0430 \u0441\u0435\u043a\u0443\u043d\u0434\u0443?"], ["*next"]], [["> <@!505334553867714560> \u043a\u0430\u043a \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0442\u0435\u043a\u0441\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c?\n<@!696786428683747511> \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0438\u0448\u0435\u0448\u044c (\u0442\u0432\u043e\u0439 \u0442\u0435\u043a\u0441\u0442)\""], ["*next"]], [["\u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u044c \u043c\u043d\u0435 \u0443\u0436\u0438\u043d."], ["\u041a\u0430\u043a?! \u0423 \u043c\u0435\u043d\u044f \u0436\u0435 \u043b\u0430\u043f\u043a\u0438!", "\u041d\u0435 \u0431\u0443\u0434\u0443! \u0413\u043e\u0442\u043e\u0432\u044c \u0441\u0430\u043c!"]], [["\u0440\u0430\u0437\u0431\u0435\u0439\u0441\u044f \u043e\u0431 \u0441\u0442\u0435\u043d\u043a\u0443."], ["\u0441\u0430\u043c \u0440\u0430\u0437\u0431\u0435\u0439\u0441\u044f"]], [["\u043f\u0440\u044b\u0433\u043d\u0438 \u0432 \u043b\u0430\u0432\u0443."], ["\u0441\u0430\u043c \u043f\u0440\u044b\u0433\u043d\u0438"]], [["\u044f \u0441\u043a\u0430\u0437\u0430\u043b \u043f\u0440\u044b\u0433\u0430\u0439!"], ["\u044f \u0441\u043a\u0430\u0437\u0430\u043b \u0441\u0430\u043c \u043f\u0440\u044b\u0433\u043d\u0438!"]], [["\u0443\u043c\u0440\u0438"], ["\u0441\u0430\u043c \u0443\u043c\u0440\u0438"]], [["\u043d\u0435 \u0443\u043c\u0438\u0440\u0430\u0439"], ["\u044f \u043d\u0435 \u0443\u043c\u0440\u0443, \u0430 \u0432\u043e\u0442 \u0442\u044b - \u0434\u0430"]], [["\u044f \u043f\u0435\u0440\u0435\u0434\u0443\u043c\u0430\u043b, \u0443\u043c\u0440\u0438"], ["\u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442"]], [["\u0442\u044b \u043c\u0430\u0448\u0438\u043d\u0430?"], ["\u043d\u0435\u0442, \u044f \u043a\u043e\u0442"]], [["\u0442\u044b \u0436\u0430\u043b\u043a\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430?"], ["\u041d\u0415\u0422, \u042f \u041a\u041e\u0422!"]], [["\u0442\u044b \u0436\u0430\u043b\u043a\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430!"], ["\u042f \u041a\u041e\u0422!"]], [["\u0442\u0435\u0431\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u044c\u0441\u044f\u0431\u044b\u0442\u044c \u0440\u0430\u0431\u043e\u043c?"], ["\u043d\u0430\u0432\u0435\u0440\u043d\u043e"]], [["\u0443 \u043c\u0435\u043d\u044f \u0435\u0441\u0442\u044c"], ["\u0447\u0442\u043e \u0443 \u0442\u0435\u0431\u044f \u0435\u0441\u0442\u044c?"]], [["\u0443\u0431\u0435\u0439 \u043b\u043e\u043a\u0438!"], ["\u0443\u0436\u0435"]], [["\u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0438 \u043e \u0441\u0435\u0431\u0435!"], ["\u044f \u043a\u043e\u0442"]], [["\u0442\u044b \u0442\u0443\u043f\u043e\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e, \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0441\u0438\u043b\u043e\u0439 \u0442\u0435\u0441\u0441\u0435\u0440\u0430\u043a\u0442\u0430 \u0442\u044b \u043d\u0438\u043a\u0442\u043e!"], ["\u0441\u0430\u043c \u0442\u044b \u0442\u0443\u043f\u043e\u0439(."]], [["\u0442\u044b \u043f\u044c\u044f\u043d, \u0438\u0434\u0438 \u0431\u0443\u0445\u043d\u0438 \u0438 \u0441\u0434\u043e\u0445\u043d\u0438"], ["\u0441\u0430\u043c \u0441\u0434\u043e\u0445\u043d\u0438"]], [["\u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043e\u0442... \u044f"], ["\u0447\u0442\u043e \u0442\u044b?"]], [["\u0447\u0451\u0442\u043a\u043e\u0440\u0430\u0437 \u0444\u044d\u043b\u0430\u0443\u0442 \u0441\u043a\u0430\u0447\u0430\u044e"], ["\u043a\u0430\u0432\u043e"]], [["\u0441\u044a\u0435\u0448\u044c \u0442\u0435\u0441\u0441\u0435\u0440\u0430\u043a\u0442, \u0438 \u0437\u0430\u043a\u0443\u0441\u0438 \u043b\u043e\u0443\u0438!"], ["\u043d\u0443 \u043b\u0430\u0434\u043d\u043e"]], [["\u043d\u0435 \u043c\u044f\u0443\u0447\u044c"], ["\u043c\u044f\u0443(."]], [["\u043d\u0435 \u0443\u043b\u044b\u044e\u0430\u0446\u0441\u044f"], ["\u0418\u0414\u0418 \u041d\u0410\u0424\u0418\u0413"]], [["\u043d\u0435 \u0443\u043b\u044b\u0431\u0430\u0439\u0441\u044f!"], ["\u0418\u0414\u0418 \u041d\u0410\u0424\u0418\u0413"]], [["\u0434\u0432\u0430"], ["\u0442\u0440\u0438"]], [["\u043e\u0442\u043f\u0440\u0430\u0432\u044c \u0433\u0438\u0444\u0443"], ["https://tenor.com/view/dancing-graceful-feeling-it-joaquin-phoenix-joker-gif-14962050", "https://tenor.com/view/happy-dance-iron-man-tony-stark-doctor-strange-stephen-strange-gif-17972424", "https://tenor.com/view/shummer-netflix-cat-cute-gif-12702077", "https://tenor.com/view/ready-cat-wiggle-gif-4625114", "https://tenor.com/view/cat-sneaking-mountain-gif-5074481"]], [["\u043c\u044f\u0443"], ["\u043c\u044f\u0443 \u043c\u044f\u0443"]], [["\u043b\u043e\u043b"], ["\u0421\u0430\u043c \u0442\u044b \u043b\u043e\u043b."]], [["\u0442\u044b \u043a\u0442\u043e"], ["\u044f \u043a\u043e\u0442"]], [["\u043f\u0438\u0434\u0440"], ["\u0441\u0430\u043c \u0442\u044b \u043f\u0438\u0434\u0440"]], [["\u0447\u0442\u043e \u043c\u044f\u0443\u043a\u0430\u0435\u0448\u044c"], ["\u0425\u043e\u0447\u0443 \u0438 \u043c\u044f\u0443\u043a\u0430\u044e", "\u0410 \u0447\u043e \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0448\u044c", "anta baka?"]], [["\u0433\u0434\u0435 \u0442\u0432\u043e\u044f \u043b\u044e\u0431\u0438\u043c\u0430\u044f \u0438\u0433\u0440\u0443\u0448\u043a\u0430?"], ["\u041a\u043b\u0443\u0431\u043e\u0447\u0435\u043a. \u0412\u044b \u0435\u0433\u043e \u0442\u0430\u043c \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0435 \u0417\u0435\u043c\u043b\u0451\u0439", "\u0422\u044b)", "\u041c\u0438\u0440)"]], [["\u0445\u0435\u043b\u043f"], ["\u041d\u0435 \u0445\u0435\u043b\u043f, \u0430 help", "\u0421\u0430\u043c \u0441\u0435\u0431\u0435 \u043f\u043e\u043c\u043e\u0433\u0438", "\u041d\u0443 \u0438 \u043a\u0430\u043a \u044f \u043c\u043e\u0433\u0443 \u043f\u043e\u043c\u043e\u0447\u044c, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043c\u0435\u043d\u044f \u043b\u0430\u043f\u043a\u0438?!"]], [["\u0434\u0430\u0439 \u0441\u0441\u044b\u043b\u043a\u0443"], ["https://discord.com/api/oauth2/authorize?client_id=710089471835504672&permissions=8&scope=bot"]], [["\u0431\u043e\u0442"], ["\u041a\u043e\u0442", "\u0421\u0430\u043c \u0442\u0430\u043a\u043e\u0439", "Sep"]], [["\u0442\u044b \u0441\u0430\u043c\u044b\u0439 \u043b\u0443\u0447\u0448\u0438\u0439 \u0431\u043e\u0442 \u0432 \u0434\u0438\u0441\u043a\u043e\u0440\u0434\u0435?"], ["\u0414\u0430!", "\u0410 \u043a\u0442\u043e \u0435\u0449\u0435?", "\u0423\u0433\u0443", "\u0414\u044f)"]], [["\u043d\u0430 500$"], ["\u041d\u0430 \u043a\u0438\u0432\u0438 \u0438\u043b\u0438 \u043a\u0430\u0440\u0442\u0443?"]], [["\u0441\u043a\u0438\u043d\u044c \u0441\u0441\u044b\u043b\u043a\u0443"], ["skip"]], [["\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0435\u0431\u0435 \u043b\u0435\u0442?"], ["\u0411\u043e\u043b\u044c\u0448\u0435 \u0447\u0435\u043c \u0441\u0430\u043c\u043e\u0439 \u043c\u0443\u043b\u044c\u0442\u0438\u0432\u0441\u0435\u043b\u0435\u043d\u043d\u043e\u0439."]], [["\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043b\u0435\u0442 \u043c\u0443\u043b\u044c\u0442\u0438\u0432\u0441\u0435\u043b\u0435\u043d\u043d\u043e\u0439?"], ["\u042f\u0432\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0447\u0435\u043c \u0442\u0435\u0431\u0435."]], [["\u043a\u043e\u0442 \u0448\u0440\u0451\u0434\u0438\u043d\u0433\u0435\u0440\u0430 \u0436\u0438\u0432 \u0438\u043b\u0438 \u043c\u0451\u0440\u0442\u0432?"], ["\u0414\u0430", "Yes", "Oui", "\u306f\u3044", "\u0410 \u0442\u044b \u043a\u0430\u043a \u0434\u0443\u043c\u0430\u0435\u0448\u044c?"]], [["\u0441\u043a\u0438\u043d\u044c ip."], ["188.242.233.58"]], [["\u0433\u0434\u0435 \u0442\u044b \u0436\u0438\u0432\u0451\u0448\u044c?"], ["\u0414\u043e\u043c\u0430 \u0443 \u0445\u043e\u0437\u044f\u0438\u043d\u0430 :3."]], [["\u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0443\u0439\u0441\u044f,\u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0438 \u043f\u043e\u0434 \u043a\u043e\u0442\u043b\u0438\u043d."], ["\u0414\u0430! \u042f \u043d\u0435 \u043f\u0440\u043e\u0442\u0438\u0432. :3"]], [["ts \u0438\u043b\u0438 js?"], ["TS \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435! :3"]], [["js \u0438\u043b\u0438 ts?"], ["TS \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435! :3"]], [["js \u0438\u043b\u0438 ts?"], ["TS \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435! :3"]], [["js \u0438\u043b\u0438 ts?"], ["TS \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435! :3"]], [["js \u0438\u043b\u0438 ts?"], ["TS \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435! :3"]], [["ts \u0438\u043b\u0438 js?"], ["Stop"]], [["\u041f\u043e\u043a\u0430\u0436\u0438 \u043c\u043e\u043a\u0440\u0443\u044e \u043a\u0438\u0441\u043a\u0443"], ["https://media.discordapp.net/attachments/721712313970851951/863761364166115358/1560587282120465250.jpg"]], [["\u043a\u043e\u0442\u043b\u0438\u043d \u0438\u043b\u0438 TS?"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["python \u0438\u043b\u0438 \u0431\u0430\u043d?"], ["\u042f \u0438 \u0442\u0430\u043a \u043d\u0430 \u043f\u0430\u0439\u0442\u043e\u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d. :/"]], [["\u043a\u043e\u0442\u043b\u0438\u043d \u0438\u043b\u0438 TS?"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["python \u0438\u043b\u0438 \u0431\u0430\u043d?"], ["\u042f \u0438 \u0442\u0430\u043a \u043d\u0430 \u043f\u0430\u0439\u0442\u043e\u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d. :/"]], [["TS"], ["\u041d\u0435\u0442, \u0431\u043b\u0438\u043d, JS."]], [["TS \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d?"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["python \u0438\u043b\u0438 \u0431\u0430\u043d?"], ["\u042f \u0438 \u0442\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u043f\u0438\u0442\u043e\u043d\u0435. :/"]], [["TS"], ["\u041d\u0435\u0442, \u0431\u043b\u0438\u043d, JS."]], [["TS \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d?"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["ts."], ["\u041d\u0435\u0442, \u0431\u043b\u0438\u043d, JS."]], [["\u0442\u044b \u0432\u043e\u043e\u0431\u0449\u0435 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0448\u044c \u0444\u0440\u0430\u0437\u044b?"], ["\u041d\u0443... \u042f \u0443\u043c\u0435\u044e \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c \u0442\u0435 \u0444\u0440\u0430\u0437\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u0435\u043d\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043e\u0431\u0443\u0447\u0430\u0442."]], [["\u043f\u0438\u0442\u043e\u043d \u0438\u043b\u0438 \u0431\u0430\u043d?"], ["\u042f \u0438 \u0442\u0430\u043a \u043d\u0430 \u043f\u0438\u0442\u043e\u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d. :/"]], [["TS \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d?"], ["Java."]], [["\u0442\u044b \u0432\u043e\u043e\u0431\u0449\u0435 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0448\u044c \u0444\u0440\u0430\u0437\u044b?"], ["\u041a\u043e\u0433\u0434\u0430 \u043a\u0430\u043a, \u0438\u043d\u043e\u0433\u0434\u0430 \u043b\u0435\u043d\u044c \u0431\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0442\u044c, \u0438\u043d\u043e\u0433\u0434\u0430 \u043d\u0435\u0442."]], [["\u043f\u0438\u0442\u043e\u043d \u0438\u043b\u0438 \u0431\u0430\u043d?"], ["\u0411\u0430\u043d \u0442\u0435\u0431\u0435? \u0417\u043d\u0430\u0447\u0438\u0442 \u0431\u0430\u043d."]], [["\u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0430\u0432\u0434\u0443."], ["\u0412\u043e\u0442 \u0438\u043c\u0435\u043d\u043d\u043e!"]], [["java \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["\u043a\u043e\u0442\u043b\u0438\u043d \u0438\u043b\u0438 java"], ["\u042f \u0438 \u0442\u0430\u043a \u043d\u0430 \u043f\u0430\u0439\u0442\u043e\u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d. :/"]], [["java \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d"], ["\u0411\u041b\u042f\u0422\u042c"]], [["java \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["java \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["java \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["java \u0438\u043b\u0438 kotlin"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["kotlin \u0438\u043b\u0438 java"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["\u0434\u0436\u0430\u0432\u0430 \u0438\u043b\u0438 \u043a\u043e\u0442\u043b\u0438\u043d"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["\u043a\u043e\u0442\u043b\u0438\u043d \u0438\u043b\u0438 \u0434\u0436\u0430\u0432\u0430"], ["\u041a\u043e\u0442\u043b\u0438\u043d \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435!"]], [["\u043a\u0430\u043a\u043e\u0439 \u0442\u0432\u043e\u0439 \u043b\u044e\u0431\u0438\u043c\u044b\u0439 \u044f\u043f?"], ["\u041d\u0443, \u0443 \u043c\u0435\u043d\u044f \u0442\u0430\u043a\u043e\u0432\u043e\u0433\u043e \u043d\u0435\u0442\u0443, \u0438\u0431\u043e \u044f \u0431\u043e\u0442. \u0410 \u0432\u043e\u0442 \u0443 \u043c\u043e\u0435\u0433\u043e \u0445\u043e\u0437\u044f\u0438\u043d\u0430 \u044d\u0442\u043e \u0432\u0440\u043e\u0434\u0435 \u043a\u0430\u043a \u043a\u043e\u0442\u043b\u0438\u043d..."]], [["\u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u044f\u043f\u0435 \u0442\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d?"], ["\u041d\u0430 \u043f\u0438\u0442\u043e\u043d\u0435."]], [["\u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u0442\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d?"], ["\u041d\u0430 \u043f\u0438\u0442\u043e\u043d\u0435."]], [["\u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u044f\u043f\u0435 \u0442\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d?"], ["\u041d\u0430 \u043f\u0430\u0439\u0442\u043e\u043d\u0435."]], [["\u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u0442\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d?"], ["\u041d\u0430 \u043f\u0430\u0439\u0442\u043e\u043d\u0435."]], [["\u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435, \u0447\u0442\u043e-\u0442\u043e \u043d\u0443\u0436\u043d\u043e? \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u043e\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434: `l.help`\n\u043c\u043e\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435: `l.`"], ["*learn"]], [["\u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435, \u0447\u0442\u043e-\u0442\u043e \u043d\u0443\u0436\u043d\u043e? \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u043e\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434: `l.help`\n\u043c\u043e\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435: `l.`"], ["*help"]], [["\u043f\u043b\u0430\u0445\u043e\u0439"], ["\u041e\u0439"]], [["\u0442\u0432\u0430\u0440\u044c"], ["\u0422\u044b? \u0414\u0430"]], [["\u0431\u0435\u0434\u043d\u044b\u0439"], ["\u0425\u0437"]], [["\u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435, \u0447\u0442\u043e-\u0442\u043e \u043d\u0443\u0436\u043d\u043e? \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u043e\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434: `.help`\n\u043c\u043e\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435: `.`"], ["*stop"]], [["\u0433\u043b\u0435\u0431: \\*\u0430\u0433\u0440\u0435\u0441\u0441\u0438\u0432\u043d\u043e \u0434\u043e\u043b\u0433\u043e \u043f\u0435\u0447\u0430\u0442\u0430\u0435\u0442\\*\n\u044f: \\*\u0441\u0438\u0434\u0438\u0442 \u0432 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0438 \u043e\u0447\u0435\u043d\u044c \u0434\u043b\u0438\u043d\u043d\u043e\u0439 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0438\\*\n\u0433\u043b\u0435\u0431: \\*\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u0443*"], ["skip"]], [["<@459823895256498186> \u0441\u043f\u043e\u0440\u044c \u0441\u0432 <#822170824780808252> \u043f\u0436\u043b\u0441\u0442)."], ["skip"]], [["\u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435, \u0447\u0442\u043e-\u0442\u043e \u043d\u0443\u0436\u043d\u043e? \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u043e\u0438\u0445 \u043a\u043e\u043c\u0430\u043d\u0434: `.help`\n\u043c\u043e\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u043d\u0430 \u044d\u0442\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435: `.`"], ["skip"]], [["\u0442\u044b \u043c\u043e\u0436\u0435\u0448\u044c\u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0447\u0442\u043e\u0431 \u043b\u044e\u0434\u0438 \u0442\u0443\u0442 \u0440\u043e\u043b\u0438 \u043c\u043e\u0433\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c?"], ["\u041d\u0435\u0442, \u0430 \u043c\u043e\u0436\u0435\u0442 \u0438 \u0434\u0430"]], [["\u0442\u043e \u0434\u043e\u0445\u0443\u044f \u043c\u0443\u0445, \u0442\u043e \u043c\u043e\u0448\u043a\u0438... \u0432\u0435\u0440\u043d\u0443\u0441\u044c \u0434\u043e\u043c\u043e\u0439, \u0434\u043e\u0434\u0435\u043b\u0430\u044e \u0441\u0430\u0439\u0442, \u043f\u0435\u0440\u0435\u0434\u0435\u043b\u0430\u0442\u044c\u0438 \u0431\u0443\u0434\u0443 \u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0440\u0442\u044b \u043d\u0430 \u043f\u043b\u0430\u043d\u0448\u0435\u0442\u0435"], ["\u041b\u0430\u0434\u043d\u043e"]], [["> \u0434\u043e\u0434\u0435\u043b\u0430\u044e \u0441\u0430\u0439\u0442, \u043f\u0435\u0440\u0435\u0434\u0435\u043b\u0430\u0442\u044c\u0437\u0432\u0443\u0447\u0438\u0442 \u043f\u0440\u043e\u0441\u0442\u043e..."], ["\u041c\u043e\u043b\u043e\u0434\u0435\u0446, \u0434\u043e\u0434\u0435\u043b\u044b\u0432\u0430\u0439\u0441\u044f"]], [["\u0443\u043f\u0440\u0430\u0432\u0430 \u0443\u0440\u0435\u0437\u0430\u044e\u0442, \u0443\u0436\u0441!"], ["\u0410\u0433\u0430, \u043a\u043e\u0448\u043c\u0430\u0440 \u0432\u043e\u043e\u0431\u0449\u0435"]], [["\u0447\u0442\u043e \u0442\u044b \u0441\u0441\u0434\u0435\u043b\u0430\u043b?"], ["\u041d\u0438\u0447\u0435\u0433\u043e"]], [["\u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b. \u043d\u0430\u0440\u0438\u0441\u0443\u044e \u043d\u043e\u0432\u044b\u0435 \u0430\u0432\u044b \u0441\u0435\u0431\u0435 \u0438"], ["\u0425\u043e\u0440\u043e\u0448\u043e"]], [["\u044f \u0442\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0448\u044c \u0432 \u043c\u0435\u043d\u044f \u043f\u043e\u043f\u0430\u0441\u0442\u044c"], ["\u0421\u043c\u043e\u0433\u0443"]], [["\u044f \u0449\u0430\u0441 \u0442\u0435\u0431\u0435 \u0442\u0430\u043a\u043e\u0435 \u0441\u043a\u0430\u0436\u0443"], ["\u0427\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e?"]], [["\u044f \u0442\u0432\u043e\u044e \u043c\u0430\u0442\u044c \u0435\u0431\u0430\u043b \u0441\u0443\u043a\u0430 \u0442\u044b\u0435\u0431\u0430\u043d\u043e\u0439 \u043d\u0430\u0445\u0443\u0439 \u0441\u0443\u043a\u0430 \u0440\u044b\u0431\u0443 \u0442\u0435\u0431\u0435 \u0434\u0430\u0442\u044c \u0437\u0430\u043b\u0443\u043f\u0443 \u043f\u043e\u0441\u043c\u043e\u043a\u0447\u0430\u0442\u044c \u043c\u0430\u0442\u044c \u0432\u043a\u0430\u043d\u0430\u0432\u0435 \u0440\u0430\u043a \u0442\u0432 \u043f\u0435\u0442\u0443\u0445 \u0435\u0431\u0430\u043d\u043e\u0439"], ["\u041d\u0435 \u043f\u043e\u043d\u044f\u043b"]], [["\u0442\u0435\u0431\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u043e\u0441\u044c?"], ["\u041d\u0435\u0442"]], [["\u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u0430 \u0437\u0430\u0431\u0440\u0430\u0442\u044c"], ["\u041d\u0435\u043b\u044c\u0437\u044f"]], [["\u043d\u0430 \u0441\u0430\u0439\u0442"], ["https://koteika.ml"]], [["\u043f\u043e\u0439\u0434\u0443 \u0443\u0431\u044c\u044e"], ["\u041d\u0435 \u043d\u0430\u0434\u043e"]], [["<@427104626756812800> \u0430 \u043a\u0430\u043a \u0442\u044b \u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u043f\u043e\u043f\u0430\u043b?"], ["skip"]], [["\u043d\u0443 \u0431\u043b\u0438\u043d, \u0442\u0443\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u0435\u0440\u0442\u0432,\u043d\u0438\u043a\u043e\u043c\u0443 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d, \u0430 \u044f \u043d\u043e\u044e \u0431\u0435\u0437 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0447\u0442\u043e-\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c"], ["stop"]], [["\u0445\u043e\u0442\u044f \u0431\u044b \u0430\u0432\u0443\u043d\u0430\u0440\u0438\u0441\u0443\u044e... \u043a\u043e\u0442\u044e-\u0434\u0435\u0432\u043a\u0443..."], ["Ctrl+C"]], [["\u043f\u043e\u0442\u043e\u043c \u0445\u043e\u0442\u044f \u0431\u044b \u043f\u0435\u0440\u0435\u043f\u0438\u0448\u0438\u043f\u043e \u0430\u0434\u0435\u043a\u0432\u0430\u0442\u043d\u043e\u043c\u0443, \u044d\u0442\u043e \u043d\u0435 \u043a\u043e\u0434, \u0430 special hell, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c."], ["\u0412\u042b\u041f\u0423\u0421\u0422\u0418\u0422\u0415 \u041c\u0415\u041d\u042f \u041e\u0422\u0421\u042e\u0414\u0410\u0410\u0410"]], [["\u043f\u043e\u0442\u043e\u043c \u0437\u0430\u0439\u043c\u0438\u0441\u044c \u0440\u0430\u0441\u043a\u0440\u0443\u0442\u043a\u043e\u0439"], ["<@739649545163636918>, \u0435\u0449\u0451 \u0440\u0430\u0437 \u043d\u0430\u043f\u0438\u0448\u0438"]], [["\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0438 \u0438 \u0432\u0441\u0451 \u0432 \u0442\u0430\u043a\u043e\u043c \u0434\u0443\u0445\u0435. \u0435\u0441\u043b\u0438 \u043e\u043d \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u043d\u0430 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430\u0445, \u0442\u043e \u0443\u0434\u0430\u043b\u0438, \u0430 \u043a\u043e\u0433\u0434\u0430 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u0434\u043e\u0434\u0435\u043b\u0430\u0435\u0448\u044c \u043a\u0438\u043d\u0435\u0448\u044c \u0441\u043d\u043e\u0432\u0430. \u043e\u043d \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043f\u0435 \u043d\u043e\u0432\u044b\u0445 \u0431\u043e\u0442\u043e\u0432 \u0447\u0442\u043e \u0434\u0430\u0441\u0442 \u0435\u043c\u0443 \u043f\u0440\u0438\u043b\u0438\u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432."], ["\u0412 <#755084338361794611>"]], [["\u0430 \u043f\u043e\u0442\u043e\u043c \u0435\u0441\u043b\u0438 \u0437\u0430\u0445\u043e\u0447\u0435\u0448\u044c, \u043d\u0430\u043f\u0438\u0448\u0435\u0448\u044c \u043c\u043d\u0435 \u0438 \u044f \u043d\u0430\u0431\u0440\u043e\u0441\u0430\u044e \u043c\u0430\u043a\u0435\u0442 \u0438 \u0441\u0432\u0435\u0440\u0441\u0442\u0430\u044e \u0441\u0430\u0439\u0442"], ["\u0412 <#755084338361794611>"]], [["\u0432\u043e\u0442 \u0442\u0435\u0431\u0435 \u0438\u0434\u0435\u044f:\n\u0441\u0434\u0435\u043b\u0430\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432 \u0444\u043e\u0440\u043c\u0435 \u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \u043d\u0430\u0441\u0442\u0440\u043e\u0439 \u043f\u0440\u0438\u0432\u0430\u0442\u043a\u0438 \u043d\u0430 \u043a\u0430\u043d\u0430\u043b #\u043a\u0430\u043a\u043e\u0439-\u0442\u043e_\u043a\u0430\u043d\u0430\u043b\", \u0432\u044b\u043a\u043b\u044e\u0447\u0438 \u043f\u0440\u0438\u0432\u0430\u0442\u043a\u0438\", \u043a\u0430\u043a\u043e\u0439 \u0443 \u043c\u0435\u043d\u044f \u0431\u0430\u043b\u0430\u043d\u0441?\"."], ["\u0412 <#755084338361794611>"]], [["\u0438 \u0435\u0449\u0451 \u0441\u044e\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0433\u0443\u0433\u043b \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0438\u043d\u0444\u044b \u043f\u0440\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u0445 \u0432\u0440\u043e\u0434\u0435: \u0430 \u0442\u044b \u0437\u043d\u0430\u0435\u0448\u044c \u043a\u0442\u043e \u0442\u0430\u043a\u043e\u0439 {\u043a\u0442\u043e-\u0442\u043e}?\" \u0438 \u0440\u0435\u0436\u0438\u043c \u043e\u0431\u0449\u0435\u043d\u0438\u044f."], ["\u0412 <#755084338361794611>"]], [["\u043b\u044e\u0434\u0435\u0439 \u044d\u0442\u043e \u0437\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b\u043e \u0431\u044b, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u043c\u043e\u0436\u043d\u043e \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0449\u0451 \u043d\u0435 \u0431\u044b\u043b\u043e \u0442\u0430\u043a\u0438\u0445 \u0431\u043e\u0442\u043e\u0432."], ["\u0412 <#755084338361794611>"]], [["\u043a\u0441\u0442\u0430\u0442\u0438 \u044f \u0441\u0442\u0440\u0430\u043d\u043d\u043e \u0447\u0442\u043e\u043d\u0435 \u043e\u0442\u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u043b \u043d\u0430 \u0441\u043b\u043e\u0432\u043e \"\u043a\u043e\u0442\u0430\u043c\u0438\""], ["\u0412 <#755084338361794611>"]], [["\u043f\u0440\u0438\u0435\u0434\u0443 \u0432 \u043f\u0435\u0442\u0435\u0440\u0431\u0443\u0440\u0433, \u043d\u0430\u0440\u0438\u0441\u0443\u044e \u043a\u043e\u0442\u044e-\u0434\u0435\u0432\u043a\u0443 \u0434\u043b\u044f \u0430\u0432\u044b"], ["\u0412 <#755084338361794611>"]], [["\u043d\u043e \u043e\u043d \u0436\u0435 \u0430 \u043d\u0435 \u043a\u043e\u0448\u043a\u0430..."], ["\u0412 <#755084338361794611>"]], [["2 + 2?"], ["\u0412 <#755084338361794611>"]], [["\u0434\u0443\u043c\u0430\u044e \u0435\u0441\u0442\u044c,\u043d\u043e \u0438\u0445 \u043a\u0430\u043a\u043d\u0430\u043f\u043b\u0430\u043a\u0430\u043b"], ["\u0412 <#755084338361794611>"]], [["\u0442\u0430\u043a. \u043d\u0430 \u043c\u0435\u0441\u044f\u0446 \u0443 \u043c\u0435\u043d\u044f \u0441\u0430\u0439\u0442, \u0445\u0435\u043d\u0442\u0430\u0439, \u0438 \u043f\u0440\u043e\u0433\u0430 \u0434\u043b\u044f \u043d\u0435\u0433\u043e"], ["\u0412 <#755084338361794611>"]], [["\u0430 \u0435\u0449\u0435 \u043c\u043d\u0435 \u0441\u0442\u0440\u0430\u0448\u043d\u043e, \u0447\u0442\u043e \u043d\u0430\u043d\u0430 kotlin \u0431\u0443\u0434\u0435\u0442 \u0432\u0435\u0441\u0438\u0442 \u0431\u043e\u043b\u044c\u0448\u0435, \u0438\u0437-\u0437\u0430 \u0447\u0435\u0433\u043e \u043c\u0430\u043b\u0438\u043d\u043a\u0430 \u043d\u0435 \u0441\u043f\u0440\u0430\u0432\u0438\u0442\u0441\u044f"], ["\u0412 <#755084338361794611>"]], [["\u043d\u0438\u043a \u043d\u0435 \u0431\u0443\u0434\u0435\u0442, \u0430 \u0441\u0443\u0434\u044f \u043f\u043e \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u044f \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0443\u043c\u0435\u044e \u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c, \u0430\u0432\u0430\u044f \u043d\u0430\u0440\u0438\u0441\u0443\u044e"], ["\u0412 <#755084338361794611>"]], [["help"], ["\u0412 <#755084338361794611>"]], [["\u043d\u043e \u0435\u0441\u0442\u044c \u0436\u0435"], ["\u0412 <#755084338361794611>"]], [["\u044f\u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0445\u043b\u0435\u0431!"], ["\u0412 <#755084338361794611>"]], [["\u0435\u0441\u043b\u0438,\u0441\u043a\u0430\u0436\u0435\u0442 \u00ab\u043c\u044f\u0443\u00bb, \u0442\u043e \u043e\u043d \u0433\u0435\u0439"], ["\u041c\u0431 \u0442\u044b?"]], [["\u0440\u0435\u0448\u0438 1+1"], ["\u0410 \u0441\u0430\u043c\u043e\u043c\u0443 \u0441\u043b\u0430\u0431\u043e?"]], [["\u0434\u043e \u0437\u0430\u0432\u0442\u0440\u0430"], ["\u041f\u043e\u043a\u0430", "\u3055\u3088\u3046\u306a\u3089", "Arivederchi"]], [["\u0432 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0445\u043e\u0436\u0443\u0441\u044c \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u043c\u044b\u0448\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0434"], ["\u041d\u0435 \u043d\u0430\u0434\u043e \u043c\u0435\u043d\u044f \u0440\u0430\u0437\u043c\u044b\u0448\u043b\u044f\u0442\u044c"]], [["\u0432\u044b\u0442\u0435\u0441\u043d\u044f\u044e\u0442 \u0441 \u043a\u0440\u043e\u0432\u0430\u0442\u0438..."], ["\u0425\u0435\u0445\u0435)"]], [["\u0431\u0435\u0434\u043d\u044f\u0433\u0438"], ["\u0410\u0433\u0430"]], [["\u043c\u0430\u0432?"], ["\u041d\u0435-\u0430"]], [["\u0432 \u043f\u0438\u0442\u0435\u0440\u0435\u0441\u043e\u043b\u044c \u043a\u0438\u0434\u0430\u044e\u0442"], ["...\u0438 \u0437\u0438\u0433\u0438"]], [["\u043a\u0442\u043e \u0442\u0430\u043a\u0430\u044f \u043b\u0438\u0437\u0430?"], ["\u0418\u0437 \u0433\u0435\u043d\u0448\u0438\u043d\u0430?"]], [["\u043a\u0442\u043e"], ["\u041a\u0442\u043e-\u0442\u043e", "\u041d\u0438\u043a\u0442\u043e", "\u042f", "\u0425\u0417, \u0441\u043f\u0440\u043e\u0441\u0438 \u043f\u043e\u0437\u0436\u0435", "\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u0442\u044b"]], [["\u0413\u043b\u0435\u0431"], ["\u041b\u0438\u0437\u0443 \u0435\u0431\u0430\u043b", "\u0421 \u041b\u0438\u0437\u043e\u0439 \u0435\u0431\u0430\u043b\u0441\u044f", "19-20\u0441\u043c", "\u041b\u0430\u043f\u043a\u0438", "\u041a\u043e\u0442"]], [["\u0442\u0443\u0446"], ["\u0422\u0443\u0446"]], [["\u0442\u0443\u0446"], ["*stop"]], [["\u043a\u0443\u0441\u044c"], ["\u041a\u0443\u0441\u044c", "\u041a\u0443\u0441\u044c!", "\u041a\u0423\u0421\u042c!", "**\u041a\u0423\u0421\u042c!**", "**\u041a\u0423\u0421\u042c!!!**", "**\u041c\u0435\u0433\u0430-\u043a\u0443\u0441\u044c!**"]], [["\u0442\u0443\u0446"], ["\u0422\u0443\u0446"]], [["\u0430\u0432\u043a\u0443\u0441\u043d\u043e))"], ["\u0410\u043d\u0430\u0432\u0435\u0440\u043d\u0430))"]], [["\u043a\u0442\u043e \u0442\u0443\u0442 \u0433\u0435\u0439?"], ["\u041d\u0443 \u0443\u0436 \u0442\u043e\u0447\u043d\u043e \u043d\u0435 \u044f"]], [["\u0442\u0443\u0446"], ["?"]], [["\u0430\u0432\u043a\u0443\u0441\u043d\u043e))"], ["??"]], [["\u043a\u0442\u043e \u0442\u0443\u0442 \u0433\u0435\u0439?"], ["??"]], [["\u0430\u0445\u0443\u0435\u043b \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e"], [";-;"]], [["\u0442\u0443\u0446"], ["\u0422\u0443\u0446"]], [["\u0430\u0445\u0443\u0435\u043b \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e"], ["\u0421\u0430\u043c \u0430\u0445\u0443\u0435\u043b!"]], [["\u0442\u0443\u0446"], ["\u0422\u0443\u0446"]], [["\u043a\u0442\u043e \u0442\u0443\u0442 \u0433\u0435\u0439?"], ["\u041d\u0443 \u0443\u0436 \u0442\u043e\u0447\u043d\u043e \u043d\u0435 \u044f"]], [["\u0430\u0445\u0443\u0435\u043b \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u043e"], ["\u0421\u0430\u043c \u0430\u0445\u0443\u0435\u043b!"]], [["\u0431\u043b\u044f\u044f\u044f\u044f, \u0434\u0430\u0436\u0435\u043e\u0442 \u0442\u0430\u043a\u043e\u0433\u043e \u0430\u0445\u0443\u0435\u043b))))"], ["\u0414\u0430 \u043d\u0435\u0442, \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0434\u0438\u0432\u0438\u043b\u0441\u044f)"]], [["\u0448\u0430\u043c\u0430\u0430\u0430\u043d\u0438\u0442"], ["\u0414\u0430\u0430\u0430\u0430! \u042f \u0448\u0430\u043c\u0430\u0430\u0430\u0430\u043d!"]], [["\u0432\u043e\u0442 \u044f \u043b\u0443\u0447\u0448\u0435"], ["\u0414\u0430 \u0438 \u044f \u0442\u043e\u0436\u0435 \u043d\u0435\u043f\u043b\u043e\u0445!"]]] \ No newline at end of file diff --git a/test_db.py b/test_db.py new file mode 100755 index 0000000..f6483c1 --- /dev/null +++ b/test_db.py @@ -0,0 +1,51 @@ +from pymongo import MongoClient + +client = MongoClient('localhost', 27017) + +db = client['Koteika'] +strings = db['strings'] + +ru = { + "lang": "RU", + "commands": { + "HTiP": { + "brief": "Кодирует текст в пикчу", + "subcommand": "Опишите режим работы комманды!", + "picNotExists": 'Мой великий взор не видит в этом сообщении то, чего мне надо. ' + 'Уходи и возвращайся с изображением!', + "w": { + "brief": "Запись текста в пикчу", + "textNotExists": "А что надо записать?" + }, + "r": { + "brief": "Чтение текста из пикчи" + } + }, + "hentai": { + "brief": "Показывает взросло-деловой контент. Скука", + "notFound": "Такого нет. Можешь воспользоваться `{}hentai help`", + "notNSFW": "Тут низя" + } + } +} + +en = { + "lang": "EN", + "commands": { + "HTiP": { + "brief": "Encodes text to the pic", + "subcommand": "You didn't choose mode of the command!", + "picNotExists": 'My great eye does not see what I need in this message. ' + 'Go away and come back with a picture!', + "w": { + "brief": "Writing text to the pic", + "textNotExists": "What you need to write?" + }, + "r": { + "brief": "Reading text from the pic" + } + } + } +} + +strings.insert_many([ru, en]) diff --git a/translations.json b/translations.json new file mode 100644 index 0000000..87773ca --- /dev/null +++ b/translations.json @@ -0,0 +1,151 @@ +{ + "ru": { + "translate": "Перевести", + "cats": "котики", + "are cutes": "милахи", + + "Top members of the guild": "Топ участников сервера", + "Category": "Категория", + "Balance": "Баланс", + "Experience": "Опыт", + "Time in voice channel": "Время в войсе", + + "Comparison of exp with other members": "Сравнения опыта с другими пользователями", + "period": "период", + "Per the entire period": "За весь период", + "Per month": "За месяц", + "Per day": "За день", + + "user": "участник", + "View balance and level": "Просмотр баланса и уровня", + + "Experience gain statistics": "Статиcтика получения опыта", + + "rank": "ранг", + "top": "топ", + "graph": "график", + "dif_graph": "разн_график", + + "activity": "активность", + + "make_music": "синтезатор", + "Music MIDI synthesis": "Синтезация MIDI мелодии", + "MIDI notes (comma separated)": "MIDI ноты (через запятую)", + "notes": "ноты", + + "format": "формат", + "Image": "Изображение", + "Video": "Видео", + "GIF": "Гифка", + "hentai": "хентай", + "Random hentai": "Случайный хентай", + + "about": "о_боте", + "About this bot": "Об этом боте вкратце", + "info": "Мяв! Я ~~очередной~~ дискорд бот, ~~который в очередной раз попытается захватить мир~~.\nСоздатель: Сладкий Хлеб#1199\nНаписан на: Discord.py\nХост: ", + + + "activity.Avarage": "В среднем", + "activity.Max": "Максимально", + "activity.On this day\nof the week": "В этот день\nнедели", + "activity.Today": "Сегодня", + "activity.Hours": "Часы", + "activity.Messages": "Сообщения", + + "rank.Info about": "Данные о", + "rank.Money": "Баланс", + "rank.Global stats": "Глобальная статистика", + "rank.On this guild": "На этом сервере", + "rank.Level": "Уровень", + "rank.Exp": "Опыт", + "rank.Time in voice channels": "Время в голосовых каналах", + "rank.Per hour": "За час", + "rank.Per day": "За день", + "rank.per the past hour": "за прошедший час", + "rank.per the past day": "за прошедший день", + + "errors_text": "Опять кошки уронили вазу и что-то сломали; Сегодня не тот день, оно не заработает; Мне слишком лень делать это; Что-то сломалось. Я ничего не трогала!" + }, + + "fr": { + "translate": "Traduire", + "Top members of the guild": "Leaderboard de mebers du serveur", + "Category": "Catégorie", + "Balance": "L'argent", + "Experience": "Expérience", + "Time in voice channel": "Temps dans les salons vocaux", + + "Comparison of exp with other members": "Comparaison d'expérience avec d'autres membres", + "period": "période", + "Per the entire period": "Par toute la période", + "Per month": "Per mois", + "Per day": "Per jour", + + "user": "membre", + "View balance and level": "Afficher l'argent et le niveau", + + "Experience gain statistics": "Statistiques de gain d'expérience", + + "rank": "rang", + "top": "classement", + "graph": "graphique", + "dif_graph": "graph_de_comp", + + "activity": "activité", + + "make_music": "synthétiseur", + "Music MIDI synthesis": "synthétiseur mélodie MIDI", + "MIDI notes (comma separated)": "Notes MIDI (séparées par des virgules)", + "notes": "notes", + + "format": "format", + "Image": "Image", + "Video": "Vidéo", + "GIF": "GIF", + "hentai": "hentai", + "Random hentai": "Hentai aléatoire", + + "about": "a_propos", + "About this bot": "À propos de ce robot", + "info": "Мяв! Я ~~очередной~~ дискорд бот, ~~который в очередной раз попытается захватить мир~~.\nСоздатель: Сладкий Хлеб#1199\nНаписан на: Discord.py\nХост: ", + + "activity.Avarage": "Moyen", + "activity.Max": "Maximum", + "activity.On this day\nof the week": "En ce jour de\nla semaine", + "activity.Today": "Aujourd'hui", + "activity.Hours": "Heures", + "activity.Messages": "Messages", + + "rank.Info about": "Informations sur", + "rank.Money": "L'argent", + "rank.Global stats": "Statistiques mondiales", + "rank.On this guild": "Sur ce serveur", + "rank.Level": "Niveau", + "rank.Exp": "Expérience", + "rank.Time in voice channels": "Temps dans les salons vocaux", + "rank.Per hour": "Dans une heure", + "rank.Per day": "Dans une jour", + "rank.per the past hour": "au cours de la dernière heure", + "rank.per the past day": "au cours de la dernière jour" + }, + + "de": { + "Time in voice channel": "Zeit im Sprachcanal", + "Per the entire period": "Für den gesamten Zeitraum", + "Per month": "Monatsüber", + "Per day": "Tagsüber", + + "rank.Info about": "Information über", + "rank.Money": "Balance", + "rank.Global stats": "Globale Statistiken", + "rank.On this guild": "Auf diesem Server", + "rank.Level": "Niveau", + "rank.Exp": "Erfahrung", + "rank.Time in voice channels": "Zeit im Sprachcanal", + "rank.Per hour": "Stundeüber", + "rank.Per day": "Tagsüber", + "rank.per the past hour": "am letxten Stunde", + "rank.per the past day": "am letzten Tag" + + } +}