Init commit
This commit is contained in:
commit
12c87b99d3
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Stickers
|
||||||
|
Cat.log
|
||||||
|
test.*
|
||||||
|
cogs/__pycache__
|
||||||
|
yarxi.db
|
91
FFC.py
Executable file
91
FFC.py
Executable 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'))
|
49
XOR.py
Executable file
49
XOR.py
Executable 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
0
__init__.py
Normal file
861
bot.py
Executable file
861
bot.py
Executable 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
120
cogs/SSC.py
Executable 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
15
cogs/_colors.py
Executable 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
128
cogs/activity_counter.py
Normal 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
34
cogs/bomber.py
Executable 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
85
cogs/bridge.py
Normal 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
31
cogs/checks.py
Executable 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
28
cogs/demotivator.py
Normal 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
62
cogs/direct_messages.py
Executable 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
537
cogs/economic.py
Executable 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
7
cogs/emojis.py
Executable 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
38
cogs/error_handler.py
Normal 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
53
cogs/errors.py
Normal 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
57
cogs/file_ext.py
Normal 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
179
cogs/japanese.py
Normal 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
155
cogs/logs.py
Executable 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
65
cogs/moderation.py
Executable 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
129
cogs/music.py
Normal 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
45
cogs/music_syn.py
Normal 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
106
cogs/private_channels.py
Executable 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
3
cogs/profile.py
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
import PIL
|
||||||
|
from PIL import Image
|
||||||
|
|
62
cogs/role_panel.py
Executable file
62
cogs/role_panel.py
Executable 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))
|
BIN
cogs/settings/__pycache__/server.cpython-39.pyc
Normal file
BIN
cogs/settings/__pycache__/server.cpython-39.pyc
Normal file
Binary file not shown.
97
cogs/settings/server.py
Normal file
97
cogs/settings/server.py
Normal 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
94
cogs/slashes.py
Normal 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
53
cogs/stickers.py
Normal 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
212
cogs/talk_module.py
Executable 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
9
cogs/tamagochi.py
Normal 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
63
cogs/timeble.py
Normal 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
30
cogs/translate.py
Executable 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
9
cogs/translation.py
Normal 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
32
cogs/translator.py
Normal 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
13
config.json
Executable 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
17
dark.mplstyle
Normal 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
107
logs.py
Executable 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
1
prefixes.json
Executable file
@ -0,0 +1 @@
|
|||||||
|
{"default": "*", "774388788783874059": "/", "822157545622863902": "*"}
|
4
pyrightconfig.json
Normal file
4
pyrightconfig.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"venv": "env",
|
||||||
|
"venvpath": "."
|
||||||
|
}
|
1
speak_data.json
Executable file
1
speak_data.json
Executable file
File diff suppressed because one or more lines are too long
51
test_db.py
Executable file
51
test_db.py
Executable 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
151
translations.json
Normal 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"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user