Init commit

This commit is contained in:
Sweetbread 2023-05-03 19:53:01 +03:00
commit 12c87b99d3
45 changed files with 3890 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
Stickers
Cat.log
test.*
cogs/__pycache__
yarxi.db

91
FFC.py Executable file
View File

@ -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'))

1
README.md Executable file
View File

@ -0,0 +1 @@
"This is the KOTEIKA. Just a regular CAT"

49
XOR.py Executable file
View File

@ -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, 'кот'))

0
__init__.py Normal file
View File

861
bot.py Executable file
View File

@ -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="<ast>", 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()

120
cogs/SSC.py Executable file
View File

@ -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))

15
cogs/_colors.py Executable file
View File

@ -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)

128
cogs/activity_counter.py Normal file
View File

@ -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))

34
cogs/bomber.py Executable file
View File

@ -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))

85
cogs/bridge.py Normal file
View File

@ -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())

31
cogs/checks.py Executable file
View File

@ -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

28
cogs/demotivator.py Normal file
View File

@ -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())

62
cogs/direct_messages.py Executable file
View File

@ -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))

537
cogs/economic.py Executable file
View File

@ -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))

7
cogs/emojis.py Executable file
View File

@ -0,0 +1,7 @@
check_mark = '<a:check_mark2:741399223655858216>'
XX = '<a:XX:739832186428981340>'
loading = '<a:loading2:741399347220054118>'
right = 'a:cat_right:769920151143055361'
left = 'a:cat_left:769919996615852042'
center = 'a:cat_center:769922556241117185'
chocolate = ":chocolate_bar:"

38
cogs/error_handler.py Normal file
View File

@ -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))

53
cogs/errors.py Normal file
View File

@ -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))

57
cogs/file_ext.py Normal file
View File

@ -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))

179
cogs/japanese.py Normal file
View File

@ -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))

155
cogs/logs.py Executable file
View File

@ -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))

65
cogs/moderation.py Executable file
View File

@ -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))

129
cogs/music.py Normal file
View File

@ -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))

45
cogs/music_syn.py Normal file
View File

@ -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())

106
cogs/private_channels.py Executable file
View File

@ -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))

3
cogs/profile.py Executable file
View File

@ -0,0 +1,3 @@
import PIL
from PIL import Image

62
cogs/role_panel.py Executable file
View File

@ -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))

Binary file not shown.

97
cogs/settings/server.py Normal file
View File

@ -0,0 +1,97 @@
from discord import TextChannel, app_commands, Interaction
from discord.ext import commands, tasks
from bot import db
import schedule
from loguru import logger
from datetime import datetime
col = db.guild_settings
@app_commands.guild_only()
@app_commands.default_permissions(administrator=True)
class ServerSettings(app_commands.Group, name="настройки_сервера"):
def add_server_settings(self, server_id: int):
if not col.find_one({"id": server_id}):
col.insert_one({
"id": server_id,
"levelup": "none",
"flood_channels": [],
"commands_channels": [],
"AFK": {
"role": None,
"time": -1,
"outsaider_help": False,
"outsaider_help_time": -1
},
"clear_top": {
"enable": False,
"channel": None,
"active_role": None,
"voice_active_role": None
},
"aux": {
"talk": False
}
})
#@app_commands.command()
#async def обзор(self, inter: Interaction):
# await inter.response.send_message("Meow")
@app_commands.command(name="назначить_флудилкой")
async def add_flood_channel(self, inter: Interaction, channel: TextChannel = None):
self.add_server_settings(inter.guild_id)
if channel is None:
channel = inter.channel
col.update_one(
{"id": inter.guild_id},
{"$push": {"flood_channels": channel.id}}
)
await inter.response.send_message(f"Канал {channel.mention} успешно стал каналом для флуда")
@app_commands.command(name="управление_ответами")
async def set_aux_talk(self, inter: Interaction, status: bool):
self.add_server_settings(inter.guild_id)
col.update_one({'id': inter.guild_id}, {'$set': {'aux.talk': status}})
if status:
await inter.response.send_message("Теперь бот будет отвечать")
else:
await inter.response.send_message("Бот больше не будет отвечать")
#class NiceName(commands.Cog):
# def __init__(self, bot):
# self.bot = bot
#
# schedule.every().day.at("00:00:00").do(self.trigger)
# self.pending.start()
#
# @tasks.loop(seconds=1)
# async def pending(self):
# #schedule.run_pending()
# trigger
#
# def trigger(self):
# asyncio.new_event_loop().run_untill_complete(clear_top())
#
# async def clear_top(self):
# if datetime.now().day == 1:
# logger.debug("Очистка топа!!!!")
#
# for data in col.find({"clear_top.enable": True}):
# server = self.bot.get_guild(data['id'])
# channel = self.bot.get_channel(data['clear_top']['channel'])
# active = server.get_member(db.members.find({"guild_stat.822157545622863902": {"$exists": True}}).sort(f"guild_stat.{data['id']}.exp", -1)[0]['id'])
# active_role = server.get_role(data['clear_top']['active_role'])
# await active.add_roles(active_role)
#
# db.members.update_many({}, {"$unset": f"guild_stat.{server_id}"})
async def setup(bot):
bot.tree.add_command(ServerSettings())

94
cogs/slashes.py Normal file
View File

@ -0,0 +1,94 @@
import discord
from discord.ext.commands import Cog
from discord.app_commands import Choice
from discord import app_commands
from os import listdir, stat
from os.path import join
from random import choice
from requests import get
from json import loads
from loguru import logger
class Slashes(Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command()
#@logger.catch
async def error(self, inter):
0/0
@app_commands.command(description="About this bot")
async def about(self, inter):
info_text = await self.bot.tree.translator.translate(
app_commands.locale_str("info"),
inter.locale,
app_commands.TranslationContext(
app_commands.TranslationContextLocation.other,
"info"
)
)
if info_text is None:
info_text = "Meow! I'm ~~another~~ discord bot ~~that will"\
"once again try to take over the world~~.\n"\
"Creator: Сладкий Хлеб#1199\n"\
"Written on: Discord.py\n"\
"Host: </host:981229566804774944>"
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))

53
cogs/stickers.py Normal file
View File

@ -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))

212
cogs/talk_module.py Executable file
View File

@ -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))

9
cogs/tamagochi.py Normal file
View File

@ -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))

63
cogs/timeble.py Normal file
View File

@ -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())

30
cogs/translate.py Executable file
View File

@ -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

9
cogs/translation.py Normal file
View File

@ -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)

32
cogs/translator.py Normal file
View File

@ -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())

13
config.json Executable file
View File

@ -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"
}]
}

17
dark.mplstyle Normal file
View File

@ -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

107
logs.py Executable file
View File

@ -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))

1
prefixes.json Executable file
View File

@ -0,0 +1 @@
{"default": "*", "774388788783874059": "/", "822157545622863902": "*"}

4
pyrightconfig.json Normal file
View File

@ -0,0 +1,4 @@
{
"venv": "env",
"venvpath": "."
}

1
speak_data.json Executable file

File diff suppressed because one or more lines are too long

51
test_db.py Executable file
View File

@ -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])

151
translations.json Normal file
View File

@ -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Хост: </host:981229566804774944>",
"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Хост: </host:981229566804774944>",
"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"
}
}