commit c46421267a478dc2e3f820a9581bdec075faee23 Author: dan63047 Date: Mon May 13 16:02:44 2024 +0300 init from 2021 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..974d773 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Contains bot token +data.json + +# You not supposted to see this +horny_poster.py +horny_data.json + +# shit +.idea +.vscode +aprint/__pycache__ \ No newline at end of file diff --git a/19471.ttf b/19471.ttf new file mode 100644 index 0000000..9466430 Binary files /dev/null and b/19471.ttf differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..742ea0a --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +### My personal discord-vk bot, that u used approximatley in 2019-2021 +I don't even think it's gonna work now, in 2024, but i still decided to publish it + +**main.py** contains script, that runs discord and vk bot in parallel with mute and ban checker threads. There are mostly features for discord, like activity tracking, count channel tracking, and special command that enables only for a limited time (new year event), as well as moderation functionality, such as muts, bans, logs of deleted messages. + +**midnigth.py** launches with cron one minute before new day. It downloads the picture of the day from my VK album, chooses *bad* person of the day (only for discord), and then sends the message to the chosen Discord channel and VK conversation. + +Will i revive it? No. \ No newline at end of file diff --git a/aprint/__init__.py b/aprint/__init__.py new file mode 100644 index 0000000..c4fd58c --- /dev/null +++ b/aprint/__init__.py @@ -0,0 +1 @@ +from .aprint import info, notice, warn, error, debug, none diff --git a/aprint/aprint.py b/aprint/aprint.py new file mode 100644 index 0000000..fff64db --- /dev/null +++ b/aprint/aprint.py @@ -0,0 +1,39 @@ +""" + Advanced Print Logging by thehatkid + GitHub: https://github.com/thehatkid +""" + +from colorama import init, Fore +import datetime + +init() + + +def info(text): + print(f"{Fore.LIGHTBLACK_EX}[{datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S.%f')}]" + f"{Fore.LIGHTCYAN_EX}[Info]{Fore.RESET} {text}{Fore.RESET}") + + +def notice(text): + print(f"{Fore.LIGHTBLACK_EX}[{datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S.%f')}]" + f"{Fore.LIGHTBLUE_EX}[Notice]{Fore.RESET} {text}{Fore.RESET}") + + +def warn(text): + print(f"{Fore.LIGHTBLACK_EX}[{datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S.%f')}]" + f"{Fore.YELLOW}[Warning] {text}{Fore.RESET}") + + +def error(text): + print(f"{Fore.LIGHTBLACK_EX}[{datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S.%f')}]" + f"{Fore.LIGHTRED_EX}[Error] {text}{Fore.RESET}") + + +def debug(text): + print(f"{Fore.LIGHTBLACK_EX}[{datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S.%f')}]" + f"{Fore.LIGHTMAGENTA_EX}[Debug] {text}{Fore.RESET}") + + +def none(text): + print(f"{Fore.LIGHTBLACK_EX}[{datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S.%f')}]" + f"{Fore.RESET} {text}{Fore.RESET}") diff --git a/av.png b/av.png new file mode 100644 index 0000000..da749e8 Binary files /dev/null and b/av.png differ diff --git a/bot.log b/bot.log new file mode 100644 index 0000000..90413ae --- /dev/null +++ b/bot.log @@ -0,0 +1,8 @@ +[ 24.75] [INFO] Script started 11.12.2021 21:26:26.812422 UTC [root] +[ 24.78] [WARNING] PyNaCl is not installed, voice will NOT be supported [discord.client] +[ 24.78] [WARNING] Detected discord.Client! It is highly recommended to use `commands.Bot`. Do not add any `on_socket_response` event. [discord_slash] +[ 24.95] [INFO] logging in using static token [discord.client] +[ 25.48] [INFO] Shard ID None has sent the IDENTIFY payload. [discord.gateway] +[ 25.68] [INFO] Shard ID None has connected to Gateway: ["gateway-prd-main-g9f5",{"micros":57772,"calls":["discord-sessions-green-prd-2-75",{"micros":56330,"calls":["start_session",{"micros":50451,"calls":["api-prd-main-63pc",{"micros":46236,"calls":["get_user",{"micros":4253},"add_authorized_ip",{"micros":1284},"get_guilds",{"micros":10131},"coros_wait",{"micros":1}]}]},"guilds_connect",{"micros":1,"calls":[]},"presence_connect",{"micros":191,"calls":[]}]}]}] (Session ID: 64da2f12e2bf37cd7d0e02cb9790f0fd). [discord.gateway] +[ 27.71] [INFO] Syncing commands... [discord_slash] +[ 46.21] [INFO] Logged in Discord as dan63047Bot#0924 [root] diff --git a/main.py b/main.py new file mode 100644 index 0000000..be8d9c2 --- /dev/null +++ b/main.py @@ -0,0 +1,1603 @@ +import datetime +from os import name +from typing import Text + +from discord.ext.commands.core import cooldown +from requests.api import delete +START_TIME = datetime.datetime.utcnow() +import locale +locale.setlocale(locale.LC_NUMERIC, ('ru_RU', 'UTF-8')) +import sys +sys.tracebacklimit = 0 +import discord +import discord_slash +import vk_api +import json +import random +import asyncio +import requests +import threading +import logging +import time +import re +from string import Template +from discord import utils +from vk_api.bot_longpoll import VkBotLongPoll, VkBotEventType +from discord_slash.utils.manage_commands import create_choice, create_option, create_permission +from PIL import Image, ImageDraw, ImageFont + +root_logger = logging.getLogger() +factory = logging.getLogRecordFactory() +def add_secs(*args, **kwargs): + record = factory(*args, **kwargs) + record.secs = record.relativeCreated/1000 + return record +logging.setLogRecordFactory(add_secs) +root_logger.setLevel(logging.INFO) +try: + log_path = f'logs/bot_log{str(START_TIME)}.log' + handler = logging.FileHandler(log_path, 'w', 'utf-8') +except: + log_path = 'bot.log' + handler = logging.FileHandler(log_path, 'w', 'utf-8') +ch = logging.StreamHandler() +f = logging.Formatter('[{secs:10.2f}] [{levelname:s}] {message:s} [{name:s}]', style="{") +handler.setFormatter(f) +ch.setFormatter(f) +root_logger.addHandler(handler) +root_logger.addHandler(ch) +def log(text): + logging.info(text) +log(f"Script started {START_TIME.strftime('%d.%m.%Y %H:%M:%S.%f UTC')}") +STRING_TRUE = ['true', '1', 't', 'y', 'yes', 'д', 'да', 'ага', 'тирания'] +STRING_FALSE = ['false', '0', 'f', 'n', 'no', 'н', 'нет', 'неа', 'террор'] +ACTIVE_TOTAL = 0 +USERS_LAST_MESSAGE = {} +USERS_LAST_ACTIVE = {} +USERS_FLOOD_COUNTER = {} +INIT_SETUP_CHANNEL = {} +TO_VERIFY = {} +NEW_YEAR_STATS = {} +NEW_YEAR_NOTIFYED = {} +intents = discord.Intents.default() +intents.members = True +client = discord.Client(guild_subscriptions=True, chunk_guilds_at_startup=True, intents=intents) +slash = discord_slash.SlashCommand(client, sync_commands=True) + +def num4(num): + """Number in 4 symbols and postfix (5 symbols)""" + num = int(num) + if len(str(num)) <= 4: + return str(num) + postfixs = ["k", "M", "B", "T", "q", "Q", "s", "S"] + nl = len(str(num))-1 + scale = min(int(nl / 3), len(postfixs)) + num /= 10**(3*scale) + decimal_length = 2 - nl % 3 if int(nl / 3) <= len(postfixs) else 0 + return f"{num:.{decimal_length}f}{postfixs[scale-1]}" + +try: + with open("data.json", "r") as jsn: + data = json.load(jsn) +except Exception as e: + logging.critical(f"Some problem with 'data.json': {e}") + exit("Some problem with 'data.json'") + +for g in data['bot-data']: + if g != "counters" and g != "new_year_event": + for m in data['bot-data'][g]['active']: + ACTIVE_TOTAL += data['bot-data'][g]['active'][m] + +class Counters: + def __init__(self): + self.SESSION = {'vk_msg_recived': 0, 'vk_msg_sended': 0, 'exceptions': 0, 'dc_msg_recived': 0, + 'dc_msg_sended': 0} + try: + self.COUNTERS = data['bot-data']['counters'] + except: + self.COUNTERS = {'start_stats_timestamp': datetime.datetime.now().timestamp(), 'uptime_secs': 0, + 'start_ups': 0, 'vk_msg_recived': 0, 'vk_msg_sended': 0, 'exceptions': 0, + 'dc_msg_recived': 0, 'dc_msg_sended': 0} + self.uptime_from = self.COUNTERS['uptime_secs'] + self.COUNTERS['start_ups'] += 1 + + def incriment(self, case: str): + if case == "e": + self.SESSION['exceptions'] += 1 + self.COUNTERS['exceptions'] += 1 + elif case == "vk-r": + self.SESSION['vk_msg_recived'] += 1 + self.COUNTERS['vk_msg_recived'] += 1 + elif case == "vk-s": + self.SESSION['vk_msg_sended'] += 1 + self.COUNTERS['vk_msg_sended'] += 1 + elif case == "dc-r": + self.SESSION['dc_msg_recived'] += 1 + self.COUNTERS['dc_msg_recived'] += 1 + elif case == "dc-s": + self.SESSION['dc_msg_sended'] += 1 + self.COUNTERS['dc_msg_sended'] += 1 + data['bot-data']['counters'] = self.COUNTERS + + def show(self, which: str): + if which == "session": + return self.SESSION + elif which == "counters": + return self.COUNTERS + + def incriment_uptime(self): + self.COUNTERS['uptime_secs'] = self.uptime_from + ( + datetime.datetime.utcnow().timestamp() - START_TIME.timestamp()) + data['bot-data']['counters'] = self.COUNTERS + +counters = Counters() + +class MyVkLongPoll(VkBotLongPoll): + def listen(self): + while True: + try: + for event in self.check(): + yield event + except Exception as e: + counters.incriment("e") + err = "A problem with VK LongPull: " + str(e) + log(err) + continue + +def debug(): + numbers = counters.show("session") + return f"Последний запуск: {START_TIME.strftime('%d.%m.%Y %H:%M:%S.%f UTC')}\n\ +Поймано исключений: {numbers['exceptions']}\n\n\ +**ВК**\n\ + - Отправлено сообщений: {numbers['vk_msg_sended']}\n\ + - Получено сообщений: {numbers['vk_msg_recived']}\n\n\ +**Дискорд**\n\ + - Отправлено сообщений: {numbers['dc_msg_sended']}\n\ + - Получено сообщений: {numbers['dc_msg_recived']}\n" + + +def counters_print(): + numbers = counters.show("counters") + return f"**Значения накопительных счётчиков \ +(созданы {datetime.datetime.fromtimestamp(numbers['start_stats_timestamp']).strftime('%d.%m.%Y %H:%M:%S UTC')})**\n\ +Запусков: {numbers['start_ups']}\n\ +Всего поймано исключений: {numbers['exceptions']}\n\n\ +*ВК*\n\ +Всего сообщений: {numbers['vk_msg_sended'] + numbers['vk_msg_recived']}\n\ + - прослушано: {numbers['vk_msg_recived']}\n\ + - отправлено: {numbers['vk_msg_sended']}\n\n\ +*Discord*\n\ +Всего сообщений: {numbers['dc_msg_recived'] + numbers['dc_msg_sended']}\n\ + - прослушано: {numbers['dc_msg_recived']}\n\ + - отправлено: {numbers['dc_msg_sended']}\n\ +" + + +def uptime_rate(): + now = datetime.datetime.utcnow() + session_up = datetime.datetime.utcnow() - START_TIME + start_time = datetime.datetime.fromtimestamp(counters.show("counters")['start_stats_timestamp']) + all_up = session_up + datetime.timedelta(seconds=counters.uptime_from) + time_from_init = now - start_time + uptime_r = (all_up.total_seconds() / time_from_init.total_seconds()) * 100 + return f'Время работы бота: {strfdelta(datetime.datetime.utcnow(), START_TIME, True, "%D:%H:%M:%S")}\n\ +Начиная с {start_time.strftime("%d.%m.%Y")}:\n\ + - Проработал: {strfdelta(datetime.datetime.utcnow(), START_TIME - datetime.timedelta(seconds=counters.uptime_from), True, "%D:%H:%M:%S")}\n\ + - Простаивал: {strfdelta(now, start_time + all_up, True, "%D:%H:%M:%S")}\n\n\ +**UPTIME РЕЙТИНГ: {locale.format_string("%.6f", uptime_r, grouping=True)} %**' + + +class DeltaTemplate(Template): + delimiter = "%" + +def strfdelta(zero:datetime.datetime, dtfrom:datetime.datetime, show_days:bool, temp=None) -> str: + if zero < dtfrom: + tdelta = dtfrom - zero + negative = True + else: + tdelta = zero - dtfrom + negative = False + if tdelta.days == 0 and temp is None: show_days = False + if show_days: + d = {"D": tdelta.days} + hours, rem = divmod(tdelta.seconds, 3600) + minutes, seconds = divmod(rem, 60) + d["H"] = '{:02d}'.format(hours) + d["M"] = '{:02d}'.format(minutes) + d["S"] = '{:02d}'.format(seconds) + if temp is None: + if negative: + t = DeltaTemplate('- %D дн. %H:%M:%S') + else: + t = DeltaTemplate('%D дн. %H:%M:%S') + else: + if negative: + t = DeltaTemplate('-'+temp) + else: + t = DeltaTemplate(temp) + return t.substitute(**d) + else: + d = {"D": tdelta.days} + hours, rem = divmod(tdelta.seconds, 3600) + minutes, seconds = divmod(rem, 60) + hours += d["D"]*24 + d["H"] = hours + d["M"] = '{:02d}'.format(minutes) + d["S"] = '{:02d}'.format(seconds) + if temp is None: + if negative: + t = DeltaTemplate('-%H:%M:%S') + else: + t = DeltaTemplate('%H:%M:%S') + else: + if negative: + t = DeltaTemplate('-'+temp) + else: + t = DeltaTemplate(temp) + return t.substitute(**d) + +def hi_msg_temp(user_mention, full_username, temp): + d = {"user_mention": user_mention, "full_username": full_username} + return DeltaTemplate(temp).substitute(**d) + +def widget_temp(temp): + d = {"until_new_year": strfdelta(datetime.datetime(year=datetime.datetime.utcnow().year, month=12, day=31, hour=21, minute=0), datetime.datetime.utcnow(), False, "%Hч%Mм"), + "next_year": str(datetime.datetime.now().year + 1)} + return DeltaTemplate(temp).substitute(**d) + +def can_use_it(dude, guild_id): + has_role = False + if data['bot-data'][str(guild_id)].get("role_access"): + for i in dude.roles: + if i.id == data['bot-data'][str(guild_id)]["role_access"]: + has_role = True + if dude.id == data['main-data']['owner_id'] or dude.id == data['bot-data'][str(guild_id)]['access'] or has_role: + return True + else: + return False + +def count_active(dude_id, guild_id, len_of_message, len_of_attachment): + global ACTIVE_TOTAL + if not data['bot-data'][str(guild_id)]['active'].get(str(dude_id)): + data['bot-data'][str(guild_id)]['active'][str(dude_id)] = len_of_message + len_of_attachment*10 + else: + message_limit = 30 + if len_of_message > message_limit: + data['bot-data'][str(guild_id)]['active'][str(dude_id)] += message_limit + len_of_attachment*10 + ACTIVE_TOTAL += message_limit + len_of_attachment*10 + else: + data['bot-data'][str(guild_id)]['active'][str(dude_id)] += len_of_message + len_of_attachment*10 + ACTIVE_TOTAL += len_of_message + len_of_attachment*10 + +async def get_image_stats(msg): + avatar = await msg.author.avatar_url_as(format="png", size=256).read() + image_file = open("av.png", "wb") + image_file.write(avatar) + image_file.close() + X = 1000 + Y = 400 + a = str(msg.author.id) + active_role_level = -1 + if data['bot-data'][str(msg.guild.id)].get("roles") and data['bot-data'][str(msg.guild.id)].get("active_roles"): + if len(data['bot-data'][str(msg.guild.id)]['roles']) == len(data['bot-data'][str(msg.guild.id)]['active_roles']): + for i in msg.author.roles: + for ar in data['bot-data'][str(msg.guild.id)]["roles"]: + if i.id == ar: + active_role_level = data['bot-data'][str(msg.guild.id)]["roles"].index(ar) + show_progress_to_next_role = True + if active_role_level != len(data['bot-data'][str(msg.guild.id)]["roles"])-1: + string_next_role = f'До роли "{utils.get(msg.guild.roles, id=data["bot-data"][str(msg.guild.id)]["roles"][active_role_level+1]).name}"' + user_next_role_active = data['bot-data'][str(msg.guild.id)]["active_roles"][active_role_level+1] + if active_role_level == -1: + user_currect_role = "Неактивный" + user_currect_role_active = 0 + else: + user_currect_role = utils.get(msg.guild.roles, id=data['bot-data'][str(msg.guild.id)]["roles"][active_role_level]).name + user_currect_role_active = data['bot-data'][str(msg.guild.id)]["active_roles"][active_role_level] + else: + show_progress_to_next_role = False + user_next_role_active = data['bot-data'][str(msg.guild.id)]["active"][a] + user_currect_role = utils.get(msg.guild.roles, id=data['bot-data'][str(msg.guild.id)]["roles"][active_role_level]).name + user_currect_role_active = 0 + else: + show_progress_to_next_role = False + user_next_role_active = data['bot-data'][str(msg.guild.id)]["active"][a] + user_currect_role = f"Ролей: {len(data['bot-data'][str(msg.guild.id)]['roles'])}, требований: {len(data['bot-data'][str(msg.guild.id)]['active_roles'])}. Настройте бота!" + user_currect_role_active = 0 + else: + show_progress_to_next_role = False + user_next_role_active = data['bot-data'][str(msg.guild.id)]["active"][a] + user_currect_role = "" + user_currect_role_active = 0 + place = sorted(data['bot-data'][str(msg.guild.id)]["active"], key=lambda i: data['bot-data'][str(msg.guild.id)]["active"][i], reverse=True).index(a)+1 + bar_offset = ((user_next_role_active - user_currect_role_active) - (data['bot-data'][str(msg.guild.id)]['active'][a] - user_currect_role_active)) / (user_next_role_active - user_currect_role_active) * (X - 100) + im = Image.new("RGB", (X, Y), (25, 25, 25)) + draw = ImageDraw.Draw(im) + av_pil = Image.open("av.png") + av_r = av_pil.resize((164, 164)) + font = ImageFont.truetype("19471.ttf", 50) + big_font = ImageFont.truetype("19471.ttf", 130) + small_font = ImageFont.truetype("19471.ttf", 30) + draw.line((50, Y - 50, X - 50, Y - 50), (0, 0, 0), 33) + draw.line((50, Y - 50, X - 50 - bar_offset, Y - 50), (255, 255, 255), 33) + draw.text((230, 50), msg.author.display_name, font=font) + draw.text((230, 160), user_currect_role, font=font) + draw.text((230, 100), f"#{place} на сервере {msg.guild.name}\nПрисоединился {strfdelta(datetime.datetime.utcnow(), msg.author.joined_at, data['bot-data'][str(msg.guild.id)]['days_in_timedelta'])} назад", font=small_font) + size3 = font.getsize(locale.format_string("%d", user_next_role_active - data['bot-data'][str(msg.guild.id)]['active'][a], grouping=True)) + if show_progress_to_next_role: + size1 = small_font.getsize(string_next_role) + draw.text((X - size1[0] - 50 , Y-135), string_next_role, font=small_font, align="right") + draw.text((X - size3[0] - 50 , Y-110), locale.format_string("%d", user_next_role_active - data['bot-data'][str(msg.guild.id)]['active'][a], grouping=True), font=font, align="right") + im.paste(av_r, (50, 50, 214, 214)) + draw.text((50, Y - 170), locale.format_string("%d", data['bot-data'][str(msg.guild.id)]['active'][a], grouping=True), font=big_font) + im.save("stats.png", "png") + +async def get_image_stats_all(msg): + Y = 145 + font = ImageFont.truetype("19471.ttf", 50) + lower_hat_font = ImageFont.truetype("19471.ttf", 30) + longest_name_size_x = font.getsize(msg.guild.name)[0] + for u in data['bot-data'][str(msg.guild.id)]["active"]: + try: + name = client.get_guild(msg.guild.id).get_member(int(u)).display_name + except Exception: + member = requests.get(f"https://discordapp.com/api/users/{u}", headers={'Authorization': f'Bot {data["main-data"]["token"]}'}) + if member.ok: + member = member.json() + name = f"{member['username']}#{member['discriminator']}" + else: + name = f"id{u}" + name_size_x = lower_hat_font.getsize(name)[0] + Y += 30 + longest_name_size_x = max(longest_name_size_x, name_size_x) + X = longest_name_size_x + 140 + lower_hat_font.getsize("Актив")[0] + 10 + im = Image.new("RGB", (X, Y), (25, 25, 25)) + draw = ImageDraw.Draw(im) + title = msg.guild.name + draw.text((X / 2 - font.getsize(title)[0] / 2, 5), title, font=font) + draw.text((10, 60), "Пользователь", font=lower_hat_font) + draw.text((longest_name_size_x + 140, 60), "Актив", font=lower_hat_font) + start_table_y = 100 + to_draw = sorted(data['bot-data'][str(msg.guild.id)]["active"], key=lambda i: data['bot-data'][str(msg.guild.id)]["active"][i], reverse=True) + all_a = 0 + for u in to_draw: + try: + name = client.get_guild(msg.guild.id).get_member(int(u)).display_name + except Exception: + member = requests.get(f"https://discordapp.com/api/users/{u}", headers={'Authorization': f'Bot {data["main-data"]["token"]}'}) + if member.ok: + member = member.json() + name = f"{member['username']}#{member['discriminator']}" + else: + name = f"id{u}" + draw.text((10, start_table_y), name, font=lower_hat_font) + size_a = lower_hat_font.getsize(locale.format_string("%d", data['bot-data'][str(msg.guild.id)]["active"][u], grouping=True)) + draw.text((longest_name_size_x + 140 + lower_hat_font.getsize("Актив")[0] - size_a[0], start_table_y), locale.format_string("%d", data['bot-data'][str(msg.guild.id)]["active"][u], grouping=True), font=lower_hat_font) + start_table_y += 30 + all_a += data['bot-data'][str(msg.guild.id)]["active"][u] + start_table_y += 10 + draw.text((10, start_table_y), "Весь сервер:", font=lower_hat_font) + size_a = lower_hat_font.getsize(locale.format_string("%d", all_a, grouping=True)) + draw.text((longest_name_size_x + 140 + lower_hat_font.getsize("Актив")[0] - size_a[0], start_table_y), locale.format_string("%d", all_a, grouping=True), font=lower_hat_font) + im.save("stats_all.png", "png") + +async def get_image_stats_all_guilds(msg): + Y = 145 + font = ImageFont.truetype("19471.ttf", 50) + lower_hat_font = ImageFont.truetype("19471.ttf", 30) + longest_name_size_x = font.getsize("Актив всех серверов")[0] + guilds = {} + for g in data['bot-data']: + if g != "counters": + name = client.get_guild(int(g)).name + name_size_x = lower_hat_font.getsize(name)[0] + Y += 30 + longest_name_size_x = max(longest_name_size_x, name_size_x) + active = sum( + data['bot-data'][g]["active"][m] + for m in data["bot-data"][g]["active"] + ) + guilds[g] = {'name': name, 'active': active} + X = longest_name_size_x + 140 + lower_hat_font.getsize("Актив")[0] + 10 + im = Image.new("RGB", (X, Y), (25, 25, 25)) + draw = ImageDraw.Draw(im) + title = "Актив всех серверов" + draw.text((X / 2 - font.getsize(title)[0] / 2, 5), title, font=font) + draw.text((10, 60), "Сервер", font=lower_hat_font) + draw.text((longest_name_size_x + 140, 60), "Актив", font=lower_hat_font) + start_table_y = 100 + to_draw = sorted(guilds, key=lambda i: guilds[i]["active"], reverse=True) + all_a = 0 + for u in to_draw: + draw.text((10, start_table_y), guilds[u]["name"], font=lower_hat_font) + size_a = lower_hat_font.getsize(locale.format_string("%d", guilds[u]["active"], grouping=True)) + draw.text((longest_name_size_x + 140 + lower_hat_font.getsize("Актив")[0] - size_a[0], start_table_y), locale.format_string("%d", guilds[u]["active"], grouping=True), font=lower_hat_font) + start_table_y += 30 + all_a += guilds[u]["active"] + start_table_y += 10 + draw.text((10, start_table_y), "Все сервера:", font=lower_hat_font) + size_a = lower_hat_font.getsize(locale.format_string("%d", all_a, grouping=True)) + draw.text((longest_name_size_x + 140 + lower_hat_font.getsize("Актив")[0] - size_a[0], start_table_y), locale.format_string("%d", all_a, grouping=True), font=lower_hat_font) + im.save("stats_all_guilds.png", "png") + +@client.event +async def on_ready(): + log(f'Logged in Discord as {client.user}') + global NEW_YEAR_STATS + NEW_YEAR_STATS = data['bot-data']["new_year_event"] + for g in data['bot-data']: + if g != "counters" and g != "new_year_event": + if data['bot-data'][g]['state'] != 'normal': + data['bot-data'][g]['state'] = 'normal' + NEW_YEAR_NOTIFYED[g] = False + if not NEW_YEAR_STATS.get(g): + NEW_YEAR_STATS[g] = {'cooldown': 1638835200, "times_used": {}, "type_used":{"ban": 0, "not_ban": 0}, "victums": {}, "longest_mute":None, "shortest_mute":None} + + +@client.event +async def on_message(message): + if isinstance(message.channel, discord.TextChannel): + if message.author == client.user: + counters.incriment("dc-s") + m = message.content.replace("\n", "⁞") + log(f'Bot in {message.guild} #{message.channel}: "{m}", attachments: {message.attachments}') + else: + m = message.content.replace("\n", "⁞") + log(f'{message.author} in {message.guild} #{message.channel}: "{m}", attachments: {message.attachments}') + counters.incriment("dc-r") + if not data['bot-data'].get(str(message.guild.id)): + data['bot-data'][str(message.guild.id)] = {"active": {}, "muted": {}, "banned": {}, "count_channel": 1, "access": message.guild.owner_id, "state": "normal", "command_prefix": "$", 'can_ban': False, 'react_on_join_leave_members': False, 'days_in_timedelta': True, "days_for_spam": 1} + if not data.get("game"): + data['game'] = {} + if not USERS_LAST_ACTIVE.get(str(message.guild.id)): + USERS_LAST_ACTIVE[str(message.guild.id)] = {str(message.author.id): datetime.datetime(2020, 1, 1)} + if not USERS_LAST_MESSAGE.get(str(message.guild.id)): + USERS_LAST_MESSAGE[str(message.guild.id)] = {str(message.author.id): ""} + if not USERS_FLOOD_COUNTER.get(str(message.guild.id)): + USERS_FLOOD_COUNTER[str(message.guild.id)] = {str(message.author.id): 0} + if not USERS_LAST_ACTIVE[str(message.guild.id)].get(str(message.author.id)): + USERS_LAST_ACTIVE[str(message.guild.id)][str(message.author.id)] = datetime.datetime(2020, 1, 1) + if not USERS_LAST_MESSAGE[str(message.guild.id)].get(str(message.author.id)): + USERS_LAST_MESSAGE[str(message.guild.id)][str(message.author.id)] = "" + if not USERS_FLOOD_COUNTER[str(message.guild.id)].get(str(message.author.id)): + USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] = 0 + + if not message.author.bot and (message.content != "" or len(message.attachments) > 0) and (USERS_LAST_MESSAGE[str(message.guild.id)][str(message.author.id)] != message.content or len(message.attachments) > 0): + count_active(message.author.id, message.guild.id, len(message.content), len(message.attachments)) + + if data['bot-data'][str(message.guild.id)].get("mute_role") and data['bot-data'][str(message.guild.id)].get('flood_limit'): + invite_search = re.search(r"discord\.gg\/\w+", message.content) + if invite_search is not None and not can_use_it(message.author, message.guild.id): + USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] = data['bot-data'][str(message.guild.id)]['flood_limit'] + await message.delete() + elif 747797669648269364 in message.raw_role_mentions: + USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] += 3 + elif len(message.attachments) == 0 and (USERS_LAST_MESSAGE[str(message.guild.id)][str(message.author.id)] == message.content or (message.created_at - USERS_LAST_ACTIVE[str(message.guild.id)][str(message.author.id)]) < datetime.timedelta(0, 2,5)): + USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] += 1 + elif USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] > 0: + USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] -= 1 + if USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] >= data['bot-data'][str(message.guild.id)]['flood_limit']: + role = utils.get(message.guild.roles, id=data['bot-data'][str(message.guild.id)]['mute_role']) + await message.author.add_roles(role) + now = datetime.datetime.utcnow() + mute = datetime.timedelta(days=1) + mute_time = now + mute + data['bot-data'][str(message.guild.id)]['muted'][str(message.author.id)] = {"timestamp": mute_time.timestamp(), "reason": "Спам", "by_id": client.user.id, "done": datetime.datetime.utcnow().timestamp()} + emb = discord.Embed(description=f"От {client.user.mention} специально для {message.author.mention}\n**Срок молчалки:** {strfdelta(now+mute, now, data['bot-data'][str(message.guild.id)]['days_in_timedelta'])}", title=f"Молчалка за спам", timestamp=mute_time, colour=0x0000ff) + emb.set_author(name=f"{message.author.name} (Ник: {message.author.nick})", icon_url=message.author.avatar_url) + await message.channel.send(f"Ты в муте за спам {message.author.mention}", embed=emb) + USERS_FLOOD_COUNTER[str(message.guild.id)][str(message.author.id)] = 0 + + USERS_LAST_MESSAGE[str(message.guild.id)][str(message.author.id)] = message.content + USERS_LAST_ACTIVE[str(message.guild.id)][str(message.author.id)] = message.created_at + + if data['bot-data'][str(message.guild.id)].get("roles") and data['bot-data'][str(message.guild.id)].get("active_roles") and not message.author.bot: + if len(data['bot-data'][str(message.guild.id)]['roles']) == len(data['bot-data'][str(message.guild.id)]['active_roles']): + active_role_level = -1 + for i in message.author.roles: + for ar in data['bot-data'][str(message.guild.id)]["roles"]: + if i.id == ar: + active_role_level = data['bot-data'][str(message.guild.id)]["roles"].index(ar) + for ari in data['bot-data'][str(message.guild.id)]['roles']: + if active_role_level != len(data['bot-data'][str(message.guild.id)]['roles'])-1: + if data['bot-data'][str(message.guild.id)]["active"][str(message.author.id)] >= int(data['bot-data'][str(message.guild.id)]['active_roles'][data['bot-data'][str(message.guild.id)]['roles'].index(ari)]) and active_role_level < data['bot-data'][str(message.guild.id)]['roles'].index(ari): + role = utils.get(message.guild.roles, id=ari) + await message.author.add_roles(role) + text = f"{message.author.mention}, вы проявили достаточно актива для получения роли \"{role.name}\", поздравляю" + if data['bot-data'][str(message.guild.id)].get('notify_channel'): + await message.guild.get_channel(data['bot-data'][str(message.guild.id)]['notify_channel']).send(text) + else: + await message.channel.send(text) + role = utils.get(message.guild.roles, id=data['bot-data'][str(message.guild.id)]['roles'][active_role_level]) + await message.author.remove_roles(role) + + if data['bot-data'][str(message.guild.id)].get("count_log"): + if message.channel.id == data['bot-data'][str(message.guild.id)]["count_log"] and message.author != client.user: + try: + if int(message.content) == data['bot-data'][str(message.guild.id)]["count_channel"]: + data['bot-data'][str(message.guild.id)]["count_channel"] += 1 + if data['bot-data'][str(message.guild.id)].get("roles_count"): + if (data['bot-data'][str(message.guild.id)]["count_channel"]-1) % 10000 == 0: + role = utils.get(message.guild.roles, id=data['bot-data'][str(message.guild.id)]["roles_count"][3]) + await message.author.add_roles(role) + await message.pin() + elif (data['bot-data'][str(message.guild.id)]["count_channel"]-1) % 1000 == 0: + role = utils.get(message.guild.roles, id=data['bot-data'][str(message.guild.id)]["roles_count"][2]) + await message.author.add_roles(role) + await message.pin() + elif (data['bot-data'][str(message.guild.id)]["count_channel"]-1) == 500: + role = utils.get(message.guild.roles, id=data['bot-data'][str(message.guild.id)]["roles_count"][1]) + await message.author.add_roles(role) + await message.pin() + elif (data['bot-data'][str(message.guild.id)]["count_channel"]-1) == 100: + role = utils.get(message.guild.roles, id=data['bot-data'][str(message.guild.id)]["roles_count"][0]) + await message.author.add_roles(role) + await message.pin() + else: + if (data['bot-data'][str(message.guild.id)]["count_channel"]-1) % 10000 == 0: + await message.pin() + elif (data['bot-data'][str(message.guild.id)]["count_channel"]-1) % 1000 == 0: + await message.pin() + elif (data['bot-data'][str(message.guild.id)]["count_channel"]-1) == 500: + await message.pin() + elif (data['bot-data'][str(message.guild.id)]["count_channel"]-1) == 100: + await message.pin() + else: + await message.delete() + except ValueError: + await message.delete() + if message.content.lower() == "бот дай денег": + await message.channel.send("Иди нахуй") + with open("data.json", "w") as jsn: + json.dump(data, jsn) + elif isinstance(message.channel, discord.DMChannel): + log(f'Message in {message.channel} by "{message.author}": "{message.content}", attachments: {message.attachments}') + if message.content == "reboot" and message.author.id == data['main-data']['owner_id']: + await message.channel.send(f"Перезагружаюсь") + exit("Got command to exit") + if message.content == "ъ": + await message.channel.send(f"ь") + else: + log(message) + +@client.event +async def on_error(event, *args, **kwargs): + counters.incriment("e") + exc = sys.exc_info() + logging.error(f"Exception {exc[0]} in {event}: {exc[1]}", exc_info=exc) + +@client.event +async def on_guild_join(guild): + log(f"We joined to {guild}") + data['bot-data'][str(guild.id)] = {"active": {}, "muted": {}, "banned": {}, "count_channel": 1, "access": guild.owner_id, "state": "normal", 'can_ban': False, 'react_on_join_leave_members': False, 'days_in_timedelta': True, "days_for_spam": 1} + await guild.system_channel.send(f"Доброго времени суток. Спасибо, что пригласили меня на свой сервер. Используйте команду /help, чтобы узнать о моих возможностях") + with open("data.json", "w") as jsn: + json.dump(data, jsn) + +@client.event +async def on_member_join(member): + log(f"Joined member {member} to guild {member.guild}") + muted = False + banned = False + for u in data['bot-data'][str(member.guild.id)]["muted"]: + if u == str(member.id): + role = utils.get(member.guild.roles, id=data['bot-data'][str(member.guild.id)]['mute_role']) + await member.add_roles(role) + muted = True + for u in data['bot-data'][str(member.guild.id)]["banned"]: + if u == str(member.id): + banned = True + if banned: + mesg = await member.guild.system_channel.send(f'Персонаж из бан листа {member.name}#{member.discriminator} осмелился ступить на сервер. Что-ж, банхаммер на готове.') + await member.ban(reason=data['bot-data'][str(member.guild.id)]["banned"][str(member.id)]['reason']) + elif muted: + strtimediff = strfdelta(datetime.datetime.fromtimestamp(data["bot-data"][str(member.guild.id)]["muted"][str(member.id)]["timestamp"]), datetime.datetime.utcnow(), data['bot-data'][str(member.guild.id)]['days_in_timedelta']) + mesg = await member.guild.system_channel.send(f'Ку, {member.mention}. На этом сервере ты получил мут от <@{data["bot-data"][str(member.guild.id)]["muted"][str(member.id)]["by_id"]}> за "{data["bot-data"][str(member.guild.id)]["muted"][str(member.id)]["reason"]}" и он истекает через {strtimediff}') + elif data['bot-data'][str(member.guild.id)].get("role_for_verification"): + if not data['bot-data'][str(member.guild.id)].get('hi_msg'): + mesg = await member.guild.system_channel.send(f'Добро пожаловать, {member.mention}. Чтобы получить роль участника "{utils.get(member.guild.roles, id=data["bot-data"][str(member.guild.id)]["role_for_verification"]).name}" и получить доступ к серверу, поставьте реакцию на это сообщение.') + else: + mesg = await member.guild.system_channel.send(hi_msg_temp(member.mention, f"{member.name}#{member.discriminator}", data['bot-data'][str(member.guild.id)]['hi_msg']) + f'\nЧтобы получить роль участника "{utils.get(member.guild.roles, id=data["bot-data"][str(member.guild.id)]["role_for_verification"]).name}" и получить доступ к серверу, поставьте реакцию на это сообщение.') + elif data['bot-data'][str(member.guild.id)]["react_on_join_leave_members"]: + if not data['bot-data'][str(member.guild.id)].get('hi_msg'): + await member.guild.system_channel.send(f'Добро пожаловать, {member.mention}') + else: + await member.guild.system_channel.send(hi_msg_temp(member.mention, f"{member.name}#{member.discriminator}", data['bot-data'][str(member.guild.id)]['hi_msg'])) + if data['bot-data'][str(member.guild.id)].get("role_for_verification"): + await mesg.add_reaction("✅") + if not TO_VERIFY.get(str(member.guild.id)): + TO_VERIFY[str(member.guild.id)] = [] + TO_VERIFY[str(member.guild.id)].append(member.id) + if data['bot-data'][str(member.guild.id)].get("admin_log"): + emb = discord.Embed(description=f"**Дата регистрации:** {member.created_at.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**Cуществует уже:** {strfdelta(datetime.datetime.utcnow(), member.created_at, data['bot-data'][str(member.guild.id)]['days_in_timedelta'])}", title=f"Новый пользователь на сервере", timestamp=member.joined_at, colour=0xd1e9ef) + emb.set_author(name=f"{member.name}#{member.discriminator}", icon_url=member.avatar_url) + emb.set_footer(text=f"User ID: {member.id}, Bot: {member.bot}") + await client.get_channel(data['bot-data'][str(member.guild.id)]['admin_log']).send(embed=emb) + +@client.event +async def on_member_remove(member): + log(f"Removed member {member} from guild {member.guild}") + if data['bot-data'][str(member.guild.id)].get("admin_log"): + emb = discord.Embed(description=f"**Дата регистрации:** {member.created_at.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**Cуществует уже:** {strfdelta(datetime.datetime.utcnow(), member.created_at, data['bot-data'][str(member.guild.id)]['days_in_timedelta'])}\n**Пробыл на сервере**: {strfdelta(datetime.datetime.utcnow(), member.joined_at, data['bot-data'][str(member.guild.id)]['days_in_timedelta'])}", title=f"Пользователь покинул сервер", timestamp=member.joined_at, colour=0xff0000) + emb.set_author(name=f"{member.name}#{member.discriminator}", icon_url=member.avatar_url) + emb.set_footer(text=f"User ID: {member.id}, Bot: {member.bot}") + await client.get_channel(data['bot-data'][str(member.guild.id)]['admin_log']).send(embed=emb) + if data['bot-data'][str(member.guild.id)]["react_on_join_leave_members"]: + await member.guild.system_channel.send(f'{member} покинул сервер. Он пробыл здесь всего {strfdelta(datetime.datetime.utcnow(), member.joined_at, data["bot-data"][str(member.guild.id)]["days_in_timedelta"])}') + +@client.event +async def on_raw_reaction_add(payload): + if TO_VERIFY.get(str(payload.guild_id)) and data['bot-data'][str(payload.guild_id)].get("role_for_verification"): + for i in TO_VERIFY[str(payload.guild_id)]: + if i == payload.user_id: + TO_VERIFY[str(payload.guild_id)].remove(i) + await payload.member.add_roles(utils.get(client.get_guild(payload.guild_id).roles, id=data['bot-data'][str(payload.guild_id)]["role_for_verification"])) + +@client.event +async def on_raw_message_delete(payload): + if data['bot-data'][str(payload.guild_id)].get("admin_log"): + if payload.cached_message: + emb = discord.Embed(title="Было удалено сообщение", description=payload.cached_message.content, timestamp=payload.cached_message.created_at, colour=0xff0000) + emb.set_author(name=f"{payload.cached_message.author.name} (Ник: {payload.cached_message.author.nick}) #{payload.cached_message.channel}", icon_url=payload.cached_message.author.avatar_url) + if payload.cached_message.attachments: + for i in payload.cached_message.attachments: + emb.add_field(name="Прикреплено", value=i.filename, inline=False) + if payload.cached_message.reactions: + reactions = "" + for i in payload.cached_message.reactions: + if reactions == "": + reactions += f"{i.emoji} **{i.count}**" + else: + reactions += f", {i.emoji} **{i.count}**" + emb.add_field(name=f"Реакций: {len(payload.cached_message.reactions)}", value=reactions, inline=False) + else: + emb = discord.Embed(title="Было удалено сообщение", description="Сообщения не было в кеше бота", colour=0xff0000) + await client.get_channel(data['bot-data'][str(payload.guild_id)]['admin_log']).send(embed=emb) + +@client.event +async def on_member_update(before, after): + if data['bot-data'][str(before.guild.id)].get("admin_log"): + if after.nick != before.nick: + emb = discord.Embed(title="Пользователь изменил свой ник на сервере", colour=0x00ff00) + if before.nick is not None: + emb.add_field(name="Старый ник", value=before.nick) + else: + emb.add_field(name="Старый ник", value="**Нет ника**") + if after.nick is not None: + emb.add_field(name="Новый ник", value=after.nick) + else: + emb.add_field(name="Новый ник", value="**Нет ника**") + emb.set_author(name=after.name, icon_url=after.avatar_url) + await client.get_channel(data['bot-data'][str(before.guild.id)]['admin_log']).send(embed=emb) + +@client.event +async def on_member_ban(guild, user): + log(f"Banned in {guild}: {user}") + if data['bot-data'][str(guild.id)].get("admin_log"): + emb = discord.Embed(title="Пользователь был забанен", description=f"**Дата регистрации:** {user.created_at.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**Cуществует уже:** {strfdelta(datetime.datetime.utcnow(), user.created_at, data['bot-data'][str(guild.id)]['mute_role'])}\n**Пробыл на сервере**: {strfdelta(datetime.datetime.utcnow(), user.joined_at, data['bot-data'][str(guild.id)]['mute_role'])}", colour=0xff0000) + emb.set_author(name=f"{user.name}#{user.discriminator}", icon_url=user.avatar_url) + emb.set_footer(text=f"User ID: {user.id}, Bot: {user.bot}") + await client.get_channel(data['bot-data'][str(guild.id)]['admin_log']).send(embed=emb) + +@client.event +async def on_member_unban(guild, user): + log(f"Unbanned in {guild}: {user}") + if data['bot-data'][str(guild.id)].get("admin_log"): + emb = discord.Embed(title="Пользователь был разбанен", description=f"**Дата регистрации:** {user.created_at.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**Cуществует уже:** {strfdelta(datetime.datetime.utcnow(), user.created_at, data['bot-data'][str(guild.id)]['mute_role'])}", colour=0x00ff00) + emb.set_author(name=f"{user.name}#{user.discriminator}", icon_url=user.avatar_url) + emb.set_footer(text=f"User ID: {user.id}, Bot: {user.bot}") + await client.get_channel(data['bot-data'][str(guild.id)]['admin_log']).send(embed=emb) + +@client.event +async def on_invite_create(invite): + if data['bot-data'][str(invite.guild.id)].get("admin_log"): + emb = discord.Embed(title="Была создана ссылка-приглашение", description=invite.url, timestamp=invite.created_at, colour=0x0000ff) + emb.add_field(name="Канал", value=invite.channel.mention) + if invite.max_age > 0 and invite.max_age != None: + emb.add_field(name="Срок действия", value=f"{strfdelta(invite.created_at+datetime.timedelta(0, invite.max_age), invite.created_at, data['bot-data'][str(invite.guild.id)]['mute_role'])}\n(до {(invite.created_at+datetime.timedelta(0, invite.max_age)).strftime('%d.%m.%Y %H:%M:%S UTC')})") + else: + emb.add_field(name="Срок действия", value="Вечная ссылка") + if invite.max_uses > 0 and invite.max_uses != None: + emb.add_field(name="Лимит на использование", value=f"{invite.max_uses} раз") + if invite.temporary: + emb.add_field(name="Ссылка предоставляет временное членство", value="Если временным учасникам не выдадут роль, они автоматически будут кикнуты") + emb.set_author(name=invite.inviter.display_name, icon_url=invite.inviter.avatar_url) + await client.get_channel(data['bot-data'][str(invite.guild.id)]['admin_log']).send(embed=emb) + +@client.event +async def on_invite_delete(invite): + if data['bot-data'][str(invite.guild.id)].get("admin_log"): + emb = discord.Embed(title="Была удалена ссылка-приглашение", description=invite.url, colour=0xff0000) + emb.add_field(name="Канал", value=invite.channel.mention) + await client.get_channel(data['bot-data'][str(invite.guild.id)]['admin_log']).send(embed=emb) + +@client.event +async def on_raw_message_edit(payload): + if data['bot-data'][payload.data['guild_id']].get("admin_log"): + emb = discord.Embed(title="Было отредактировано сообщение", description=f"**Отредактировано:** {datetime.datetime.strptime(payload.data['edited_timestamp'], '%Y-%m-%dT%H:%M:%S.%f%z').strftime('%d.%m.%Y %H:%M:%S UTC')}", timestamp=datetime.datetime.strptime(payload.data['timestamp'], "%Y-%m-%dT%H:%M:%S.%f%z"), colour=0x0000ff) + if payload.cached_message: + emb.set_author(name=f"{payload.cached_message.author.name} (Ник: {payload.cached_message.author.nick}) #{payload.cached_message.channel}", icon_url=payload.cached_message.author.avatar_url) + emb.add_field(name="Старый текст", value=payload.cached_message.content, inline=True) + else: + emb.set_author(name=f"{payload.data['author']['username']}") + emb.add_field(name="Сообщения не было в кеше бота", value="Невозможно показать разницу", inline=True) + emb.add_field(name="Новый текст", value=payload.data['content'], inline=True) + await client.get_channel(data['bot-data'][payload.data['guild_id']]['admin_log']).send(embed=emb) + +@client.event +async def on_guild_remove(guild): + log(f"We left from {guild}") + global ACTIVE_TOTAL + for m in data['bot-data'][str(guild.id)]['active']: + ACTIVE_TOTAL -= data['bot-data'][str(guild.id)]['active'][m] + del data['bot-data'][str(guild.id)] + +@slash.slash(name="help", description="Справочный материал с описанием команд бота") +async def help(ctx): + emb = discord.Embed(title="Справочный материал с описанием команд бота", description="Эта справка призвана помочь разобраться в командах бота") + emb.add_field(name=f"/random *X* *Y*", value="Генерирует рандомное число от *X* до *Y*. Если *Y* нету, то тогда от 0 до *X*. Если и *X* нет, то тогда от 0 до 10") + emb.add_field(name=f"/stats *choice*", value="Посмотреть актив, который насчитал бот для вас, а если после команды написать *all*, то можно посмотреть актив, который насчитал бот для всего сервера") + #emb.add_field(name=f"{data['bot-data'][str(ctx.guild.id)]['command_prefix']}кнб *камень/ножницы/бумага/стат*", value='Сыграть в "Камень, ножницы, бумага" с ботом, если приписать аргумент *стат*, можно увидеть количесво побед, поражений и ничей') + emb.add_field(name=f"/slap *@user*", value='Шлёпнуть *@user* или рандомного персонажа на сервере, если не упомянут') + if can_use_it(ctx.author, ctx.guild.id): + emb.add_field(name=f"/settings", value="Даёт возможность перенастроить бота") + if data['bot-data'][str(ctx.guild.id)].get("mute_role"): + emb.add_field(name=f"/mute *@user* *[s, m, h, d, w]/DD.MM.YYYY* *reason*", value="Выдать молчалку пользователю. Команда имеет 2 обязательных аргумента: *@user* - упоминание персоны; *[s, m, h, d, w]/DD.MM.YYYY* - либо срок в относительном формате (30m - 30 минут, 7d - 7 дней), либо дата. Есть также 3-ий необязательный аргумент: *reason* - причина мута.") + emb.add_field(name=f"/unmute *@user*", value="Cнять молчалку с пользователя. *@user* - упоминание персоны") + emb.add_field(name=f"/mute_list", value="Показать активные на сервере молчалки") + if data['bot-data'][str(ctx.guild.id)]["can_ban"]: + emb.add_field(name=f"/ban *@user* *[s, m, h, d, w]/DD.MM.YYYY* *reason*", value="Забанить пользователя. Команда имеет 2 обязательных аргумента: *@user* - упоминание персоны; *[s, m, h, d, w]/DD.MM.YYYY* - либо срок в относительном формате (30m - 30 минут, 7d - 7 дней), либо дата. Есть также 3-ий необязательный аргумент: *reason* - причина бана.") + emb.add_field(name=f"/unban *id*", value="Разбанить пользователя, которого забанили командой /ban. *id* - его цифровой id") + emb.add_field(name=f"/ban_list", value="Показать активные на сервере баны") + await ctx.send(embed=emb) + +@slash.slash(name="random", description="Отправляет рандомное число от X до Y", + options=[create_option(name="x_int", description="Наименьшее число если используется вместе с y_int, иначе число больше нуля", option_type=4, required=False), + create_option(name="y_int", description="Наибольшее число", option_type=4, required=False)]) +async def randon(ctx, **kwargs): + if "y_int" in kwargs and "x_int" in kwargs: + emb = discord.Embed(title="Рандомное число") + emb.add_field(name="От", value=kwargs["x_int"], inline=True) + emb.add_field(name="До", value=kwargs["y_int"], inline=True) + emb.add_field(name="**Результат**", value=locale.format_string('**%d**', random.randint(kwargs["x_int"], kwargs["y_int"]), grouping=True), inline=False) + elif "x_int" in kwargs: + emb = discord.Embed(title="Рандомное число") + emb.add_field(name="От", value="0", inline=True) + emb.add_field(name="До", value=kwargs["x_int"], inline=True) + emb.add_field(name="**Результат**", value=locale.format_string('**%d**', random.randint(0, kwargs["x_int"]), grouping=True), inline=False) + else: + emb = discord.Embed(title="Рандомное число") + emb.add_field(name="От", value="0", inline=True) + emb.add_field(name="До", value="10", inline=True) + emb.add_field(name="**Результат**", value=locale.format_string('**%d**', random.randint(0, 10), grouping=True), inline=False) + + await ctx.send(embed=emb) + +# , permissions={ + # 747583006322983053:[ + # create_permission(747583006322983053, discord_slash.model.SlashCommandPermissionType.ROLE, False), + # create_permission(747879784364769331, discord_slash.model.SlashCommandPermissionType.ROLE, True) + # ] + #} +@slash.slash(name="debug", description="Позволяет узнать состояние бота", options=[ + create_option(name="applet", description="Название конкретной штуки, информацию о которой надо узнать", option_type=3, required=True, choices=[ + create_choice(name="Общая информация", value="default"), + create_choice(name="Антиспам система", value="spam"), + create_choice(name="Cчётчики", value="counters"), + create_choice(name="Время работы бота", value="uptime") + ]) + ]) +async def deb(ctx, applet): + if applet == "default": + await ctx.send(debug()) + elif applet == "spam": + answer = f"**Работа антиспам системы**\nДанный массив - то, как видит ситуацию антиспам система бота. На уровне *{data['bot-data'][str(ctx.guild.id)]['flood_limit']}* или выше - выдаёт мут на 1 день.\n" + for u in USERS_FLOOD_COUNTER[str(ctx.guild.id)]: + member = ctx.guild.get_member(u) + answer += f"\n{member} - {USERS_FLOOD_COUNTER[str(ctx.guild.id)][u]}" + await ctx.send(answer) + elif applet == "counters": + await ctx.send(counters_print()) + elif applet == "uptime": + await ctx.send(uptime_rate()) + +@slash.slash(name="stats", description="Бот отправит информацию о набранных очках актива", options=[ + create_option(name="choice", description=" Количество очков актива чего вам нужно узнать?", option_type=3, required=True, choices=[ + create_choice(name="Обо мне", value="me"), + create_choice(name="О сервере", value="server"), + create_choice(name="Таблица серверов", value="all servers") + ]) +]) +async def stats(ctx, choice): + if choice == "me": + await get_image_stats(ctx) + await ctx.send(file=discord.File("stats.png")) + elif choice == "server": + await get_image_stats_all(ctx) + await ctx.send(file=discord.File("stats_all.png")) + elif choice == "all servers": + await get_image_stats_all_guilds(ctx) + await ctx.send(file=discord.File("stats_all_guilds.png")) + +@slash.slash(name="slap", description="Шлёпнуть кого-нибудь))))))", options=[ + create_option(name="user", description="Выберите пользователя для шлепка, иначе будет выбран рандомный", option_type=6, required=False) +]) +async def slap(ctx, **kwargs): + guy = kwargs['user'] if "user" in kwargs else random.choice(ctx.guild.members) + await ctx.send(f"{ctx.author.mention} шлёпнул {guy.mention}") + +@slash.slash(name="gift", description="Подарить либо мешочек с нихуя, либо мут от 1 секунды до 2 часов") +async def new_year_present(ctx): + START_TIME = 1638835200 + END_TIME = 1641513600 + dt_now = datetime.datetime.utcnow() + now = dt_now.timestamp() + global NEW_YEAR_STATS + global NEW_YEAR_NOTIFYED + if not NEW_YEAR_STATS.get(str(ctx.guild.id)): + NEW_YEAR_STATS[str(ctx.guild.id)] = {'cooldown': START_TIME, "times_used": {}, "type_used":{"ban": 0, "not_ban": 0}, "victums": {}, "longest_mute":None, "shortest_mute":None} + if NEW_YEAR_STATS[str(ctx.guild.id)]["cooldown"] <= now: + victim = random.choice(ctx.guild.members) + mute = random.randint(0, 1) + if mute: + reason = "Новогодний подарок" + for_ban = victim + if data['bot-data'][str(ctx.guild.id)].get('mute_role'): + role = utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['mute_role']) + await for_ban.add_roles(role) + mute_time = random.randint(1, 7200) + data['bot-data'][str(ctx.guild.id)]['muted'][str(for_ban.id)] = {"timestamp": now + mute_time, "reason": reason, "by_id": ctx.author.id, "done": now} + emb = discord.Embed(description=f"От <@{ctx.author.id}> рандомному персонажу на сервере, которым оказался <@{for_ban.id}>\n**Подарок:** молчалка сроком {strfdelta(datetime.datetime.utcfromtimestamp(now + mute_time), dt_now, False)}", title=f"Новогодний подарок", timestamp=datetime.datetime.fromtimestamp(now + mute_time), colour=0x0000ff) + emb.set_author(name=f"{for_ban.name} (Ник: {for_ban.nick})", icon_url=for_ban.avatar_url) + emb.set_footer(text="Действует до") + else: + emb = discord.Embed(description=f"От <@{ctx.author.id}> рандомному персонажу на сервере, которым оказался <@{victim.id}>\n**Подарок:** мешочек с нихуя", title=f"Новогодний подарок", colour=0x0000ff, image="https://sun9-41.userapi.com/impf/c849128/v849128784/b0a1/GXEDUAUp-3A.jpg?size=200x200&quality=96&sign=3bb33aaba78a34581f46ed63e11c4725&type=album") + emb.set_author(name=f"{victim.name} (Ник: {victim.nick})", icon_url=victim.avatar_url) + + if not NEW_YEAR_STATS[str(ctx.guild.id)]["times_used"].get(str(ctx.author.id)): + NEW_YEAR_STATS[str(ctx.guild.id)]["times_used"][str(ctx.author.id)] = 1 + else: + NEW_YEAR_STATS[str(ctx.guild.id)]["times_used"][str(ctx.author.id)] += 1 + if not NEW_YEAR_STATS[str(ctx.guild.id)]["victums"].get(str(victim.id)): + NEW_YEAR_STATS[str(ctx.guild.id)]["victums"][str(victim.id)] = 1 + else: + NEW_YEAR_STATS[str(ctx.guild.id)]["victums"][str(victim.id)] += 1 + if mute: + NEW_YEAR_STATS[str(ctx.guild.id)]["type_used"]["ban"] += 1 + if NEW_YEAR_STATS[str(ctx.guild.id)]["longest_mute"] == None or NEW_YEAR_STATS[str(ctx.guild.id)]["longest_mute"][0] < mute_time: + NEW_YEAR_STATS[str(ctx.guild.id)]["longest_mute"] = [mute_time, victim.id, ctx.author.id] + if NEW_YEAR_STATS[str(ctx.guild.id)]["shortest_mute"] == None or NEW_YEAR_STATS[str(ctx.guild.id)]["shortest_mute"][0] > mute_time: + NEW_YEAR_STATS[str(ctx.guild.id)]["shortest_mute"] = [mute_time, victim.id, ctx.author.id] + else: + NEW_YEAR_STATS[str(ctx.guild.id)]["type_used"]["not_ban"] += 1 + + if (dt_now.day == 31 and dt_now.month == 12) or (dt_now.day == 1 and dt_now.month == 1): + cooldown = now + 900 + text_cd = "Команда снова будет доступна через 15 минут" + elif dt_now.day == 30 and dt_now.month == 12: + cooldown = now + 3600 + text_cd = "Команда снова будет доступна через 60 минут" + elif dt_now.day >= 25 and dt_now.month == 12: + cooldown = now + 7200 + text_cd = "Команда снова будет доступна через 2 часа" + elif dt_now.day >= 20 and dt_now.month == 12: + cooldown = now + 10800 + text_cd = "Команда снова будет доступна через 3 часа" + elif dt_now.day >= 15 and dt_now.month == 12: + cooldown = now + 14400 + text_cd = "Команда снова будет доступна через 4 часа" + elif dt_now.day >= 10 and dt_now.month == 12: + cooldown = now + 18000 + text_cd = "Команда снова будет доступна через 5 часов" + elif dt_now.day >= 7 and (dt_now.month == 12 or dt_now.month == 1): + cooldown = now + 21600 + text_cd = "Команда снова будет доступна через 6 часов" + if cooldown >= END_TIME: + text_cd += ". Это будет последний подарок" + else: + cooldown = datetime.datetime(dt_now.year, 12, 7).timestamp() + text_cd = f'Ивент завершён. Статистика за {dt_now.year-1}-{dt_now.year} года:\nВсего было подарков: {NEW_YEAR_STATS[str(ctx.guild.id)]["type_used"]["ban"]+NEW_YEAR_STATS[str(ctx.guild.id)]["type_used"]["not_ban"]}, из которых было молчалок: {NEW_YEAR_STATS[str(ctx.guild.id)]["type_used"]["ban"]}, мешочков с нихуя: {NEW_YEAR_STATS[str(ctx.guild.id)]["type_used"]["not_ban"]}' + NEW_YEAR_STATS[str(ctx.guild.id)]["cooldown"] = cooldown + await ctx.send(f"Держи, {victim.mention}. {text_cd}", embed=emb) + data['bot-data']["new_year_event"] = NEW_YEAR_STATS + with open("data.json", "w") as jsn: + json.dump(data, jsn) + NEW_YEAR_NOTIFYED[str(ctx.guild.id)] = False + else: + await ctx.send(f"Новый сюрприз можно будет устроить через {strfdelta(datetime.datetime.fromtimestamp(NEW_YEAR_STATS[str(ctx.guild.id)]['cooldown']), dt_now, False)}", delete_after=15) + +@slash.slash(name="settings", description="Настройте бота на вашем сервере", options=[ + create_option(name="setting", description="Выберите необходимую настройку", option_type=3, required=True, choices=[ + create_choice(name="Роль доступа к боту", value="access_role"), + create_choice(name="Канал уведомлений", value="notify_channel"), + create_choice(name="Дни в отображении разницы времени", value="days_in_timedelta"), + create_choice(name="Роль мута", value="mute_role"), + create_choice(name="Лимит антифлуд системы", value="flood_limit"), + create_choice(name="Бан командой", value="can_ban"), + create_choice(name="Реагировать на приход/уход участников", value="join_leave"), + create_choice(name="Шаблон приветствия нового пользователя", value="hi_msg"), + create_choice(name="Роль верификации", value="role_for_verification"), + create_choice(name="Канал для лога событий сервера", value="admin_log"), + create_choice(name="Список ролей за актив", value="active_roles"), + create_choice(name="Список требований за роли актива", value="active_for_roles"), + create_choice(name="Канал для счёта", value="counting"), + create_choice(name="Список ролей в канале счёта", value="roles_for_count") + ]), + create_option(name="value", description="Укажите, чтобы изменить настройку, иначе бот отправит значение настройки", option_type=3, required=False) +]) +async def sett(ctx, **kwargs): # sourcery skip: merge-else-if-into-elif + if can_use_it(ctx.author, ctx.guild.id): + if kwargs['setting'] == "access_role": + if "value" in kwargs: + if kwargs['value'].lower() == "clear": + if data['bot-data'][str(ctx.guild.id)].get("role_access"): + del data['bot-data'][str(ctx.guild.id)]['role_access'] + await ctx.send(f"Особый доступ к боту будете иметь только вы") + else: + try: + data['bot-data'][str(ctx.guild.id)]['role_access'] = int(kwargs['value']) + await ctx.send(f"Роль {utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['role_access']).mention} будет использоваться для доступа к боту.") + except Exception as e: + await ctx.send(f"Некорректное значение\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("role_access"): + await ctx.send(f"Особый доступ к боту есть только у {ctx.author}\nЧтобы установить роль с особым доступом, напишите её id в аргументе *value*") + else: + await ctx.send(f"Особый досуп к боту имеют все, у кого есть роль \"{utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['role_access'])}\"\nЧтобы установить роль с особым доступом, напишите её id в аргументе *value*\n Чтобы удалить роль, в аргументе *value* напишите \"clear\"") + elif kwargs["setting"] == "notify_channel": + if "value" in kwargs: + if kwargs["value"].lower() == "clear": + del data['bot-data'][str(ctx.guild.id)]['notify_channel'] + await ctx.send(f"Уведомления о размуте, разбане и повышении роли выключены") + else: + try: + data['bot-data'][str(ctx.guild.id)]['notify_channel'] = int(kwargs['value']) + await ctx.send(f"Канал {utils.get(ctx.guild.channels, id=data['bot-data'][str(ctx.guild.id)]['notify_channel'])} будет использоваться для уведомлений о размуте, разбане и повышении роли") + except Exception as e: + await ctx.send(f"Некорректное значение\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("notify_channel"): + await ctx.send(f"Канал для уведомлений о размуте, разбане и повышении роли не указан\nЧтобы включить, напишите его id в аргументе *value*") + else: + await ctx.send(f"Канал для уведомлений о размуте, разбане и повышении роли: {utils.get(ctx.guild.channels, id=data['bot-data'][str(ctx.guild.id)]['notify_channel'])}\nЧтобы изменить канал для админ-лога, напишите его id в аргументе *value*\nЧтобы выключить админ-лог, в аргументе *value* напишите \"clear\"") + elif kwargs["setting"] == "days_in_timedelta": + if "value" in kwargs: + if kwargs["value"] in STRING_TRUE: + data['bot-data'][str(ctx.guild.id)]['days_in_timedelta'] = True + await ctx.send(f"Формат разницы времени: *дни* дн. *часы*:*минуты*:*секунды*") + elif kwargs["value"] in STRING_FALSE: + data['bot-data'][str(ctx.guild.id)]['days_in_timedelta'] = False + await ctx.send(f"Формат разницы времени: *дни в часах+часы*:*минуты*:*секунды*") + else: + await ctx.send(f"Некорректное значение", delete_after=10) + else: + await ctx.send(f"Флаг формата разницы времени: {data['bot-data'][str(ctx.guild.id)]['days_in_timedelta']}\nЧтобы изменить, укажите в аргументе *value* булево значение (True или False)") + elif kwargs["setting"] == "mute_role": + if "value" in kwargs: + if kwargs['value'].lower() == "clear": + if data['bot-data'][str(ctx.guild.id)].get("mute_role"): + del data['bot-data'][str(ctx.guild.id)]['mute_role'] + await ctx.send(f"Молчалка отключена") + else: + try: + data['bot-data'][str(ctx.guild.id)]['mute_role'] = int(kwargs['value']) + await ctx.send(f"Роль {utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['mute_role']).mention} будет использоваться для молчалки.") + except Exception as e: + await ctx.send(f"Некорректное значение\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("mute_role"): + await ctx.send(f"Молчалка отключена\nЧтобы включить, напишите id роли, которая запрещает отправлять сообщения, в аргументе *value*") + else: + await ctx.send(f"Роль молчалки: \"{utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['mute_role'])}\"\nЧтобы изменить роль молчалки, напишите id роли, которая запрещает отправлять сообщения, в аргументе *value*\n Чтобы удалить роль, в аргументе *value* напишите \"clear\"") + elif kwargs["setting"] == "flood_limit": + if not data['bot-data'][str(ctx.guild.id)].get("mute_role"): + await ctx.send("Молчалка отключена\nОна нужна в качестве наказания за флуд") + elif "value" in kwargs: + try: + if int(kwargs['value']) > 0: + data['bot-data'][str(ctx.guild.id)]['flood_limit'] = int(kwargs['value']) + await ctx.send(f"Лимит на флуд: {data['bot-data'][str(ctx.guild.id)]['flood_limit']} одинаковых/быстро отправленных сообщений подряд") + elif int(kwargs['value']) == 0: + del data['bot-data'][str(ctx.guild.id)]['flood_limit'] + await ctx.send("Лимит на флуд выключен") + else: + await ctx.send("Лимит должен быть больше нуля или ноль, если хотите выключить", delete_after=10) + except Exception as e: + await ctx.send(f"Некорректное значение\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get('flood_limit'): + await ctx.send("Наказание отключено\nЧтобы включить, укажите в аргументе *value*, сколько сообщений флуда можно написать перед полученем наказания") + else: + await ctx.send(f"Лимит анти-флуд системы: {data['bot-data'][str(ctx.guild.id)]['flood_limit']} одинаковых/быстро отправленных сообщений\nЧтобы изменить, укажите в аргументе *value*, сколько сообщений флуда можно написать перед полученем наказания, или 0, чтобы отключить систему") + elif kwargs["setting"] == "can_ban": + if "value" in kwargs: + if kwargs["value"] in STRING_TRUE: + data['bot-data'][str(ctx.guild.id)]['can_ban'] = True + await ctx.send(f"Теперь можно банить людей через команду `/ban`") + elif kwargs["value"] in STRING_FALSE: + data['bot-data'][str(ctx.guild.id)]['can_ban'] = False + await ctx.send(f"Команда `/ban` выключена") + else: + await ctx.send(f"Некорректное значение", delete_after=10) + else: + await ctx.send(f"Можно банить через команду бота: {data['bot-data'][str(ctx.guild.id)]['can_ban']}\nЧтобы изменить, укажите в аргументе *value* булево значение (True или False)") + elif kwargs["setting"] == "join_leave": + if "value" in kwargs: + if kwargs["value"] in STRING_TRUE: + data['bot-data'][str(ctx.guild.id)]['react_on_join_leave_members'] = True + await ctx.send(f"Бот будет реагировать на приход/уход участников") + elif kwargs["value"] in STRING_FALSE: + data['bot-data'][str(ctx.guild.id)]['react_on_join_leave_members'] = False + await ctx.send(f"Бот не будет реагировать на приход/уход участников") + else: + await ctx.send(f"Некорректное значение", delete_after=10) + else: + await ctx.send(f"Реагировать на приход на сервер и уход с него участников: {data['bot-data'][str(ctx.guild.id)]['react_on_join_leave_members']}\nЧтобы изменить, укажите в аргументе *value* булево значение (True или False)") + elif kwargs["setting"] == "hi_msg": + if "value" in kwargs: + if kwargs["value"].lower() == "clear": + del data['bot-data'][str(ctx.guild.id)]['hi_msg'] + await ctx.send("Будет использоваться стандартный шаблон приветствия") + else: + data['bot-data'][str(ctx.guild.id)]['hi_msg'] = kwargs["value"] + await ctx.send(f"Новый шаблон приветствия: {data['bot-data'][str(ctx.guild.id)]['hi_msg']}") + else: + if not data['bot-data'][str(ctx.guild.id)].get('hi_msg'): + await ctx.send(f"Используется стандартный шаблон приветствия\nЧтобы изменить, в аргументе *value* напишите шаблон сообщения. В нём могут быть такие элементы, как `%user_mention` (упоминание новичка) и `%full_username` (полный ник новичка)") + else: + await ctx.send(f"Шаблон приветствия: {data['bot-data'][str(ctx.guild.id)]['hi_msg']}\nЧтобы изменить, в аргументе *value* напишите шаблон сообщения. В нём могут быть такие элементы, как `%user_mention` (упоминание новичка) и `%full_username` (полный ник новичка)\nЕсли он больше не нужен, после аргумента напишите \"clear\"") + elif kwargs["setting"] == "role_for_verification": + if "value" in kwargs: + if kwargs['value'].lower() == "clear": + if data['bot-data'][str(ctx.guild.id)].get("role_for_verification"): + del data['bot-data'][str(ctx.guild.id)]['role_for_verification'] + await ctx.send(f"Верификация отключена") + else: + try: + data['bot-data'][str(ctx.guild.id)]['role_for_verification'] = int(kwargs['value']) + await ctx.send(f"Роль {utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['role_for_verification'])} будет использоваться для верификации.") + except Exception as e: + await ctx.send(f"Некорректное значение\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("role_for_verification"): + await ctx.send(f"Верификация отключена\nЧтобы включить, напишите id роли верификации в аргументе *value*") + else: + await ctx.send(f"Роль верификации: \"{utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['role_for_verification']).name}\"\nЧтобы изменить роль верификации, напишите её id в аргументе *value*\nЧтобы выключить верификацию, в аргументе *value* напишите \"clear\"") + elif kwargs["setting"] == "admin_log": + if "value" in kwargs: + if kwargs["value"].lower() == "clear": + del data['bot-data'][str(ctx.guild.id)]['admin_log'] + await ctx.send(f"Уведомления о размуте, разбане и повышении роли выключены") + else: + try: + data['bot-data'][str(ctx.guild.id)]['admin_log'] = int(kwargs['value']) + await ctx.send(f"Канал {utils.get(ctx.guild.channels, id=data['bot-data'][str(ctx.guild.id)]['admin_log'])} будет использоваться для уведомлений о размуте, разбане и повышении роли") + except Exception as e: + await ctx.send(f"Некорректное значение\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("admin_log"): + await ctx.send(f"Канал админ-лога не указан\nЧтобы включить, напишите id канала для админ-лога в аргументе *value*") + else: + await ctx.send(f"Канал админ-лога: \"{utils.get(ctx.guild.channels, id=data['bot-data'][str(ctx.guild.id)]['admin_log'])}\"\nЧтобы изменить канал для админ-лога, напишите его id в аргументе *value*\nЧтобы выключить админ-лог, в аргументе *value* напишите \"clear\"") + elif kwargs["setting"] == "active_roles": + if "value" in kwargs: + lst = kwargs["value"].split(" ") + try: + for l in lst: + int(l) + data['bot-data'][str(ctx.guild.id)]['roles'] = lst + answer = "Новый порядок ролей:" + for role in data['bot-data'][str(ctx.guild.id)]['roles']: + answer += f" <@&{role}>" + if data['bot-data'][str(ctx.guild.id)].get('active_roles'): + if len(data['bot-data'][str(ctx.guild.id)]['roles']) != len(data['bot-data'][str(ctx.guild.id)]['active_roles']): + answer += "\nРекомендую также изменить требуемый актив за роли, так как кол-во ролей и требований за них не совпадает" + else: + answer += "\nРекомендую также изменить награды за актив, так как они не назначены" + await ctx.send(answer) + except Exception as e: + await ctx.send(f"Ошибка\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("roles"): + await ctx.send("Роли за актив не установлены\nЧтобы это сделать, в аргументе *value* напишите id ролей по очереди, от самой менее значимой до самой крутой роли за актив через пробел") + else: + answer = "Порядок ролей:" + for role in data['bot-data'][str(ctx.guild.id)]['roles']: + answer += f" <@&{role}>" + answer +="\nЧтобы изменить, в аргументе *value* напишите id ролей по очереди, от самой менее значимой до самой крутой роли за актив через пробел\nЛибо после аргумента напишите \"clear\"" + await ctx.send(answer) + elif kwargs["setting"] == "active_for_roles": + if not data['bot-data'][str(ctx.guild.id)].get("roles"): + await ctx.send("Роли за актив не установлены\nИспользуйте `/settings setting:Список ролей за актив` чтобы узнать подробнее") + elif "value" in kwargs: + lst = kwargs["value"].split(" ") + try: + for l in lst: + int(l) + data['bot-data'][str(ctx.guild.id)]['active_roles'] = lst + answer = "Требуемый вами актив:" + for n in data['bot-data'][str(ctx.guild.id)]['active_roles']: + answer += f" {n}" + await ctx.send(answer) + except Exception as e: + await ctx.send(f"Ошибка\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("active_roles"): + await ctx.send("Требования для ролей за актив не установлены\nЧтобы это сделать, напишите в аргументе *value* требуемый актив за роли по очереди через пробел, от самой менее значимой до самой крутой роли за актив") + else: + answer = "Требуемый актив за роли:" + for role in data['bot-data'][str(ctx.guild.id)]['active_roles']: + answer += f" {role}" + answer +="\nЧтобы изменить, напишите в аргументе *value* требуемый актив за роли по очереди через пробел, от самой менее значимой до самой крутой роли за актив\nЛибо после аргумента напишите \"clear\"" + await ctx.send(answer) + elif kwargs["setting"] == "counting": + if "value" in kwargs: + if kwargs["value"].lower() == "clear": + del data['bot-data'][str(ctx.guild.id)]['count_log'] + await ctx.send(f"Бот теперь не следит за счётом в этом канале") + else: + try: + data['bot-data'][str(ctx.guild.id)]['count_log'] = int(kwargs['value']) + await ctx.send(f"Канал {utils.get(ctx.guild.channels, id=data['bot-data'][str(ctx.guild.id)]['count_log'])} будет использоваться для счёта\nСледующее число: {data['bot-data'][str(ctx.guild.id)]['count_channel']}") + except Exception as e: + await ctx.send(f"Некорректное значение\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("count_log"): + await ctx.send(f"Канал для счёта не установлен.\nЧтобы включить, напишите id канала для админ-лога в аргументе *value*") + else: + await ctx.send(f"Канал для счёта: \"{utils.get(ctx.guild.channels, id=data['bot-data'][str(ctx.guild.id)]['count_log'])}\"\nЧтобы изменить канал для админ-лога, напишите его id в аргументе *value*\nЧтобы выключить админ-лог, в аргументе *value* напишите \"clear\"") + elif kwargs["setting"] == "roles_for_count": + if not data['bot-data'][str(ctx.guild.id)].get("count_log"): + await ctx.send("Канал для счёта не установлен.\nИспользуйте `/settings setting:Канал для счёта` чтобы узнать подробнее") + elif "value" in kwargs: + lst = kwargs["value"].split(" ") + try: + for l in lst: + int(l) + data['bot-data'][str(ctx.guild.id)]['roles'] = lst + answer = "Новый порядок ролей:" + for role in data['bot-data'][str(ctx.guild.id)]['roles']: + answer += f" <@&{role}>" + await ctx.send(answer) + except Exception as e: + await ctx.send(f"Ошибка\n\n`{e}`", delete_after=10) + else: + if not data['bot-data'][str(ctx.guild.id)].get("active_roles"): + await ctx.send("Роли для канала счёта не установлны\nЧтобы это сделать, напишите в аргументе *value* id ролей по порядку за число 100, 500, круглое число с 3 нолями на конце и круглое число с 4 нолями на конце") + else: + answer = "Требуемый актив за роли:" + for role in data['bot-data'][str(ctx.guild.id)]['active_roles']: + answer += f" {role}" + answer +="\nЧтобы изменить, напишите в аргументе *value* id ролей по порядку за число 100, 500, круглое число с 3 нолями на конце и круглое число с 4 нолями на конце\nЛибо после аргумента напишите \"clear\"" + await ctx.send(answer) + else: + await ctx.send("Вам нельзя использовать эту команду", delete_after=10) + + +@slash.slash(name="mute", description="Лишить кого-нибудь права отправлять сообщения на определённый срок", options=[ + create_option(name="user", description="Пользователь, в отношении которого будет применена санкция", option_type=6, required=True), + create_option(name="term", description="Либо срок в формате [s, m, h, d, w], либо дата в формате DD.MM.YYYY", option_type=3, required=True), + create_option(name="reason", description="Причина приминения санкции", option_type=3, required=False) +]) +async def mute(ctx, **kwargs): + if not can_use_it(ctx.author, ctx.guild.id): + await ctx.send("Вам нельзя использовать эту команду", delete_after=10) + return + if not data['bot-data'][str(ctx.guild.id)].get("mute_role"): + await ctx.send("Команда отключена", delete_after=10) + return + try: + if re.search(r"\d\d\.\d\d.\d\d\d\d", kwargs["term"]) and datetime.datetime.strptime(kwargs["term"], "%d.%m.%Y") >= datetime.datetime.utcnow(): + mute_time = datetime.datetime.strptime(kwargs["term"], "%d.%m.%Y") + elif ( + kwargs["term"][-1:] in ["s", "m", "h", "d", "w"] + and int(kwargs["term"][:-1]) > 0 + ): + valve_time_ban = int(kwargs["term"][:-1]) + type_time_ban = kwargs["term"][-1:] + now = datetime.datetime.utcnow() + if type_time_ban == "s": + mute = datetime.timedelta(seconds=valve_time_ban) + elif type_time_ban == "m": + mute = datetime.timedelta(minutes=valve_time_ban) + elif type_time_ban == "h": + mute = datetime.timedelta(hours=valve_time_ban) + elif type_time_ban == "d": + mute = datetime.timedelta(days=valve_time_ban) + elif type_time_ban == "w": + mute = datetime.timedelta(weeks=valve_time_ban) + mute_time = now + mute + else: + await ctx.send(f"Неправильное значения аргумента `term`: `{kwargs['term']}`", delete_after=10) + return + reason = kwargs["reason"] if "reason" in kwargs else "Не указана" + for_ban = kwargs["user"] + role = utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['mute_role']) + await for_ban.add_roles(role) + data['bot-data'][str(ctx.guild.id)]['muted'][str(for_ban.id)] = {"timestamp": mute_time.timestamp(), "reason": reason, "by_id": ctx.author.id, "done": datetime.datetime.utcnow().timestamp()} + emb = discord.Embed(description=f"От <@{ctx.author.id}> специально для <@{for_ban.id}>\n**Причина:** {reason}\n**Срок молчалки:** {strfdelta(now+mute, now, data['bot-data'][str(ctx.guild.id)]['days_in_timedelta'])}", title=f"Молчалка", timestamp=mute_time, colour=0x0000ff) + emb.set_author(name=f"{for_ban.name} (Ник: {for_ban.nick})", icon_url=for_ban.avatar_url) + emb.set_footer(text="Действует до") + await ctx.send(embed=emb) + except Exception as e: + await ctx.send(f"Я сломался: `{e}`", delete_after=10) + + +@slash.slash(name="ban", description="Лишить кого-нибудь права быть на этом сервере на определённый срок", options=[ + create_option(name="user", description="Пользователь, в отношении которого будет применена санкция", option_type=6, required=True), + create_option(name="term", description="Либо срок в формате [s, m, h, d, w], либо дата в формате DD.MM.YYYY", option_type=3, required=True), + create_option(name="reason", description="Причина приминения санкции", option_type=3, required=False) +]) +async def ban(ctx, **kwargs): + if not can_use_it(ctx.author, ctx.guild.id): + await ctx.send("Вам нельзя использовать эту команду", delete_after=10) + return + if not data['bot-data'][str(ctx.guild.id)]["can_ban"]: + await ctx.send("Команда отключена", delete_after=10) + return + try: + if re.search(r"\d\d\.\d\d.\d\d\d\d", kwargs["term"]) and datetime.datetime.strptime(kwargs["term"], "%d.%m.%Y") >= datetime.datetime.utcnow(): + mute_time = datetime.datetime.strptime(kwargs["term"], "%d.%m.%Y") + elif ( + kwargs["term"][-1:] in ["s", "m", "h", "d", "w"] + and int(kwargs["term"][:-1]) > 0 + ): + valve_time_ban = int(kwargs["term"][:-1]) + type_time_ban = kwargs["term"][-1:] + now = datetime.datetime.utcnow() + if type_time_ban == "s": + mute = datetime.timedelta(seconds=valve_time_ban) + elif type_time_ban == "m": + mute = datetime.timedelta(minutes=valve_time_ban) + elif type_time_ban == "h": + mute = datetime.timedelta(hours=valve_time_ban) + elif type_time_ban == "d": + mute = datetime.timedelta(days=valve_time_ban) + elif type_time_ban == "w": + mute = datetime.timedelta(weeks=valve_time_ban) + mute_time = now + mute + else: + await ctx.send(f"Неправильное значения аргумента `term`: `{kwargs['term']}`", delete_after=10) + return + reason = kwargs["reason"] if "reason" in kwargs else "Не указана" + for_ban = kwargs["user"] + await for_ban.ban(reason=reason) + data['bot-data'][str(ctx.guild.id)]['banned'][str(for_ban.id)] = {"timestamp": mute_time.timestamp(), "reason": reason, "by_id": ctx.author.id, "done": datetime.datetime.utcnow().timestamp()} + emb = discord.Embed(description=f"<@{ctx.author.id}> бьёт <@{for_ban.id}> банхаммером\n**Причина:** {reason}\n**Срок бана:** {strfdelta(now+mute, now, data['bot-data'][str(ctx.guild.id)]['days_in_timedelta'])}", title="Бан", timestamp=mute_time, colour=0x0000ff) + emb.set_author(name=f"{for_ban.name} (Ник: {for_ban.nick})", icon_url=for_ban.avatar_url) + emb.set_footer(text="Действует до") + await ctx.send(embed=emb) + except Exception as e: + await ctx.send(f"Я сломался: `{e}`", delete_after=10) + + +@slash.slash(name="unmute", description="Вернуть кому-нибудь право отправлять сообщения", options=[ + create_option(name="user", description="Пользователь, в отношении которого будет снята санкция", option_type=6, required=True) +]) +async def unmute(ctx, user): + if not can_use_it(ctx.author, ctx.guild.id): + await ctx.send("Вам нельзя использовать эту команду", delete_after=10) + return + if not data['bot-data'][str(ctx.guild.id)].get("mute_role"): + await ctx.send("Команда отключена", delete_after=10) + return + try: + for u in data['bot-data'][str(ctx.guild.id)]['muted']: + if int(u) == user.id: + role = utils.get(ctx.guild.roles, id=data['bot-data'][str(ctx.guild.id)]['mute_role']) + await user.remove_roles(role) + await ctx.send(f"Молчалка снята с {user.mention}") + del data['bot-data'][str(ctx.guild.id)]['muted'][u] + return + await ctx.send(f"У пользователя нет молчалки", delete_after=10) + except Exception as e: + await ctx.send(f"Я сломался: `{e}`", delete_after=10) + + +@slash.slash(name="unban", description="Вернуть кому-нибудь право быть на этом сервере", options=[ + create_option(name="userid", description="ID пользователя, в отношении которого будет снята санкция", option_type=4, required=True) +]) +async def unban(ctx, userID): + if not can_use_it(ctx.author, ctx.guild.id): + await ctx.send("Вам нельзя использовать эту команду", delete_after=10) + return + if not data['bot-data'][str(ctx.guild.id)]["can_ban"]: + await ctx.send("Команда отключена", delete_after=10) + return + try: + for_ban = str(userID) + for u in data['bot-data'][str(ctx.guild.id)]['banned']: + if u == for_ban: + ban_list = await ctx.guild.bans() + for i in ban_list: + if i.user.id == int(u): + unban = i.user + await ctx.guild.unban(unban) + await ctx.send(f"{unban} разбанен") + del data['bot-data'][str(ctx.guild.id)]['banned'][u] + return + await ctx.send("Пользователь не найден", delete_after=10) + except Exception as e: + await ctx.send(f"Я сломался: `{e}`", delete_after=10) + + +@slash.slash(name="mute_list", description="Посмотреть список активных на сервере молчалок") +async def mute_list(ctx): + if not can_use_it(ctx.author, ctx.guild.id): + await ctx.send("Вам нельзя использовать эту команду", delete_after=10) + return + if not data['bot-data'][str(ctx.guild.id)].get("mute_role"): + await ctx.send("Команда отключена", delete_after=10) + return + if len(data['bot-data'][str(ctx.guild.id)]['muted']) > 0: + desc_text = f"**Активных молчалок**: {len(data['bot-data'][str(ctx.guild.id)]['muted'])}" + if len(data['bot-data'][str(ctx.guild.id)]['muted']) > 25: + desc_text += f"\n**Embed может показать только 25 строк**" + emb = discord.Embed(title="Действующие молчалки", description=desc_text, color=0x0000ff) + for_cout = sorted(data['bot-data'][str(ctx.guild.id)]['muted'], key=lambda i:data['bot-data'][str(ctx.guild.id)]['muted'][i]["timestamp"]) + for u in for_cout: + unmute_time = datetime.datetime.fromtimestamp(data['bot-data'][str(ctx.guild.id)]['muted'][u]["timestamp"]) + mute_time = datetime.datetime.fromtimestamp(data['bot-data'][str(ctx.guild.id)]['muted'][u]["done"]) + strtimediff = strfdelta(unmute_time, datetime.datetime.utcnow(), data["bot-data"][str(ctx.guild.id)]["days_in_timedelta"]) + nick = ctx.guild.get_member(int(u)) + bnick = ctx.guild.get_member(int(data['bot-data'][str(ctx.guild.id)]['muted'][u]['by_id'])) + if nick is None: + try: + member = requests.get(f"https://discordapp.com/api/users/{u}", headers={'Authorization': f'Bot {data["main-data"]["token"]}'}).json() + nick = f"{member['username']}#{member['discriminator']}" + except: + nick = f"id{u}" + else: + nick = nick.display_name + if bnick is None: + try: + muted_by = requests.get(f"https://discordapp.com/api/users/{data['bot-data'][str(ctx.guild.id)]['muted'][u]['by_id']}", headers={'Authorization': f'Bot {data["main-data"]["token"]}'}).json() + bnick = f"{muted_by['username']}#{muted_by['discriminator']}" + except: + bnick = f"id{data['bot-data'][str(ctx.guild.id)]['muted'][u]['by_id']}" + else: + bnick = bnick.display_name + emb.add_field(name=nick, value=f"**От**: {mute_time.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**До**: {unmute_time.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**Ост. срок**: {strtimediff}\n**Причина**: {data['bot-data'][str(ctx.guild.id)]['muted'][u]['reason']}\n**Выдал**: {bnick}") + await ctx.send(embed=emb) + else: + await ctx.send("Нет действующих молчалок") + + +@slash.slash(name="ban_list", description="Посмотреть список активныx на сервере временных банов") +async def ban_list(ctx): + if not can_use_it(ctx.author, ctx.guild.id): + await ctx.send("Вам нельзя использовать эту команду", delete_after=10) + return + if not data['bot-data'][str(ctx.guild.id)]["can_ban"]: + await ctx.send("Команда отключена", delete_after=10) + return + if len(data['bot-data'][str(ctx.guild.id)]['banned']) > 0: + emb = discord.Embed(title="Действующие баны", description=f"**Активных банов**: {len(data['bot-data'][str(ctx.guild.id)]['banned'])}", color=0x0000ff) + for u in data['bot-data'][str(ctx.guild.id)]['banned']: + unmute_time = datetime.datetime.utcfromtimestamp(data['bot-data'][str(ctx.guild.id)]['banned'][u]["timestamp"]) + mute_time = datetime.datetime.fromtimestamp(data['bot-data'][str(ctx.guild.id)]['banned'][u]["done"]) + strtimediff = strfdelta(unmute_time, datetime.datetime.utcnow(), data["bot-data"][str(ctx.guild.id)]["days_in_timedelta"]) + nick = ctx.guild.get_member(int(u)) + bnick = ctx.guild.get_member(int(data['bot-data'][str(ctx.guild.id)]['banned'][u]['by_id'])) + if nick is None: + try: + member = requests.get(f"https://discordapp.com/api/users/{u}", headers={'Authorization': f'Bot {data["main-data"]["token"]}'}).json() + nick = f"{member['username']}#{member['discriminator']}" + except: + nick = f"id{u}" + else: + nick = nick.display_name + if bnick is None: + try: + muted_by = requests.get(f"https://discordapp.com/api/users/{data['bot-data'][str(ctx.guild.id)]['banned'][u]['by_id']}", headers={'Authorization': f'Bot {data["main-data"]["token"]}'}).json() + bnick = f"{muted_by['username']}#{muted_by['discriminator']}" + except: + bnick = f"id{data['bot-data'][str(ctx.guild.id)]['banned'][u]['by_id']}" + else: + bnick = bnick.display_name + emb.add_field(name=nick, value=f"**От**: {mute_time.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**До**: {unmute_time.strftime('%d.%m.%Y %H:%M:%S UTC')}\n**Ост. срок**: {strtimediff}\n**Причина**: {data['bot-data'][str(ctx.guild.id)]['banned'][u]['reason']}\n**Выдал**: {bnick}") + await ctx.send(embed=emb) + else: + await ctx.send("Нет действующих банов") + + +async def time_unmute(member_id, guild_id): + response = requests.delete(f'https://discordapp.com/api/guilds/{guild_id}/members/{member_id}/roles/{data["bot-data"][guild_id]["mute_role"]}', headers={'Authorization': f'Bot {data["main-data"]["token"]}'}) + log(f"Discord API answer: {response.status_code} {response.reason}") + if response.ok and data['bot-data'][guild_id].get('notify_channel'): + await client.get_channel(data['bot-data'][guild_id]['notify_channel']).send(f"Молчалка снята с <@{member_id}>: Срок молчалки истёк") + +async def ny_notif(g): + await client.get_channel(data['bot-data'][g]['notify_channel']).send(f"Новый сюрприз готов. Жду того, кто готов его вручить (/gift)") + +async def time_unban(member_id, guild_id): + try: + ban_list = await client.get_guild(guild_id).bans() + for i in ban_list: + if i.user.id == int(member_id): + unban = i.user + await client.get_guild(int(guild_id)).unban(unban) + if data['bot-data'][guild_id].get('notify_channel'): + await client.get_channel(data['bot-data'][guild_id]['notify_channel']).send(f"{unban} разбанен: Срок бана истёк") + except: + member = requests.get(f"https://discordapp.com/api/users/{member_id}", headers={'Authorization': f'Bot {data["main-data"]["token"]}'}) + if member.ok: + member = member.json() + if data['bot-data'][guild_id].get('notify_channel'): + await client.get_channel(data['bot-data'][guild_id]['notify_channel']).send( + f"{member['username']}#{member['discriminator']} вычеркнут из бан-листа: срок бана истёк") + else: + if data['bot-data'][guild_id].get('notify_channel'): + await client.get_channel(data['bot-data'][guild_id]['notify_channel']).send(f"id{member_id} вычеркнут из бан-листа: срок бана истёк") + +vk = vk_api.VkApi(token="redacted") +vk_mda = vk_api.VkApi(token="redacted") +longpoll = VkBotLongPoll(vk, 190322075) +vkcall = vk.get_api() + +async def new_post(vk_event): + group = vkcall.groups.getById(group_id=str(vk_event.object.from_id)[1:], fields='photo_200') + emb = discord.Embed(description=vk_event.object.text, url=f"https://vk.com/{group[0]['screen_name']}?w=wall-{group[0]['id']}_{vk_event.object.id}", title="Вышел новый пост", timestamp=datetime.datetime.fromtimestamp(vk_event.object.date), colour=0xF0A020) + emb.set_author(name=group[0]['name'], url=f"https://vk.com/{group[0]['screen_name']}", icon_url=group[0]['photo_200']) + if vk_event.object.attachments: + photo = False + attachments = "" + for i in vk_event.object.attachments: + try: + if i['type'] == 'photo': + attachments += f"🖼 {i['type']}{i[i['type']]['owner_id']}_{i[i['type']]['id']}\n" + if not photo: + emb.set_image(url=i['photo']['sizes'][-1]['url']) + photo = True + elif i['type'] == 'wall': + attachments += f"📰 {i['type']}{i[i['type']]['from_id']}_{i[i['type']]['id']}\n" + elif i['type'] == 'link': + attachments += f"🔗 {i['type']} {i[i['type']]['title']}\n" + elif i['type'] == 'doc': + attachments += f"📄 {i['type']}{i[i['type']]['owner_id']}_{i[i['type']]['id']}\n" + elif i['type'] == 'poll': + attachments += f"📊 {i['type']}{i[i['type']]['owner_id']}_{i[i['type']]['id']}\n" + elif i['type'] == 'album': + attachments += f"🎨 {i['type']}{i[i['type']]['owner_id']}_{i[i['type']]['id']}\n" + elif i['type'] == 'video': + attachments += f"🎞 {i['type']}{i[i['type']]['owner_id']}_{i[i['type']]['id']}\n" + elif i['type'] == 'audio': + attachments += f"🎵 {i['type']}{i[i['type']]['owner_id']}_{i[i['type']]['id']}\n" + else: + attachments += f"🎩 {i['type']}{i[i['type']]['owner_id']}_{i[i['type']]['id']}\n" + except: + attachments += f"🎩 {i['type']}\n" + emb.add_field(name="Прикреплено", value=attachments) + emb.set_footer(text=f"wall{str(vk_event.object['from_id'])}_{str(vk_event.object['id'])}") + await client.get_channel(747588017543446568).send(embed=emb) + +def send(to, message=None, attachment=None): + try: + random_id = random.randint(-9223372036854775808, + 9223372036854775807) + message = vk.method('messages.send', + {'peer_id': int(to), 'message': message, 'random_id': random_id, + 'attachment': attachment}) + counters.incriment("vk-s") + except Exception as e: + counters.incriment("e") + logging.error(f'Failed to send VK message: {str(e)}') + +def vk_longpull(): + for event in MyVkLongPoll.listen(longpoll): + try: + if event.type == VkBotEventType.MESSAGE_NEW: + counters.incriment("vk-r") + if event.message.text.lower() == "бот дай денег": + send(event.message.peer_id, "Иди нахуй") + if event.message.text.lower() == "бот дай денег": + send(event.message.peer_id, "Иди нахуй") + if event.message.text.lower() == "бот дай": + send(event.message.peer_id, "Мне денег") + if event.message.text.lower() == "бот иди": + send(event.message.peer_id, "Ты нахуй") + if event.message.text.lower() == "бот нахуй": + send(event.message.peer_id, "Ты иди") + if event.message.text.lower() == "бот деньги": + send(event.message.peer_id, "Мне дай") + if event.message.text.lower() == "бот иди нахуй": + from_obj = vk.method("users.get", {'user_ids': event.message.from_id}) + send(event.message.peer_id, f"{from_obj[0]['first_name']}, дай денег") + if event.message.text == "!debug" and event.message.from_id == 276193568: + send(event.message.peer_id, debug()) + elif event.type == VkBotEventType.WALL_POST_NEW: + if event.object.post_type == "post": + log(f"New post! id{event.object['id']}") + post = f"wall{str(event.object['from_id'])}_{str(event.object['id'])}" + send(2000000003, f"Вышел новый пост", post) + coro = new_post(event) + fut = asyncio.run_coroutine_threadsafe(coro, client.loop) + try: + fut.result() + except: + counters.incriment("e") + continue + except Exception as kek: + log(f"Беды с ботом: {str(kek)}") + continue + +def bot_status(): + while True: + try: + time.sleep(1) + l = round(client.latency*1000) + up = strfdelta(datetime.datetime.utcnow(), START_TIME, False, "%Hч%Mм") + asyncio.run_coroutine_threadsafe(client.change_presence(activity=discord.Game(name=f"{num4(ACTIVE_TOTAL)} актива на {len(client.guilds)} серверах (пинг {l} мс, проработал {up})")), client.loop).result() + if datetime.datetime.utcnow().second == 0: + vk_widget = {"title": "Discord бот dan63047Bot#0924", + "title_counter": ACTIVE_TOTAL, + "text": widget_temp(data['main-data']['vk_widget_text']), + "descr": f"Обслуживает серверов: {len(client.guilds)}\nАптайм: {up}\nПинг до Discord API: {l} мс."} + vk_mda.method("appWidgets.update", {"type": "text", "code": f'return {json.dumps(vk_widget, ensure_ascii=False)};'}) + global NEW_YEAR_NOTIFYED + global NEW_YEAR_STATS + for g in NEW_YEAR_NOTIFYED: + if NEW_YEAR_NOTIFYED[g] is False and datetime.datetime.utcnow().timestamp() >= NEW_YEAR_STATS[g]["cooldown"]: + # coro = ny_notif(g) + # fut = asyncio.run_coroutine_threadsafe(coro, client.loop) + try: + # fut.result() + NEW_YEAR_NOTIFYED[g] = True + except: + counters.incriment("e") + continue + except Exception as e: + logging.error(f"Some activity tread shit {e}") + time.sleep(1) + continue + counters.incriment_uptime() + +def bot_bans(): + while True: + try: + time.sleep(1) + now = datetime.datetime.utcnow() + for g in data['bot-data']: + if g != "counters" and g != "new_year_event": + for b in data['bot-data'][g]['banned']: + if data['bot-data'][g]['banned'][b]['timestamp'] < now.timestamp(): + try: + asyncio.run_coroutine_threadsafe(time_unban(b, g), client.loop).result() + del data['bot-data'][g]['banned'][b] + except: + log("Time unban shit") + except: + logging.error("Some unban tread shit") + time.sleep(1) + continue + +def bot_muts(): + while True: + try: + time.sleep(1) + now = datetime.datetime.utcnow() + for g in data['bot-data']: + if g != "counters" and g != "new_year_event": + for m in data['bot-data'][g]['muted']: + if data['bot-data'][g]['muted'][m]['timestamp'] < now.timestamp(): + try: + asyncio.run_coroutine_threadsafe(time_unmute(m, g), client.loop).result() + del data['bot-data'][g]['muted'][m] + except: + log("Time unmute shit") + except: + logging.error("Some unmute tread shit") + time.sleep(1) + continue + +try: + threading.Thread(target=vk_longpull, name=vk_longpull, daemon=True).start() + threading.Thread(target=bot_status, name=bot_status, daemon=True).start() + threading.Thread(target=bot_bans, name=bot_bans, daemon=True).start() + threading.Thread(target=bot_muts, name=bot_muts, daemon=True).start() + client.run(data['main-data']['token']) +finally: + with open("data.json", "w") as jsn: + json.dump(data, jsn) + up = strfdelta(datetime.datetime.utcnow(), START_TIME, True, "%D:%H:%M:%S") + log(f"Data saved, script ended, uptime: {up}") \ No newline at end of file diff --git a/midnight.py b/midnight.py new file mode 100644 index 0000000..f9f954f --- /dev/null +++ b/midnight.py @@ -0,0 +1,228 @@ +import datetime +import config +import aprint +import json +import requests +import random +from urllib.request import urlopen + +aprint.info("Script started") + +try: + fwords = json.loads(open("fword.json", "r").read()) +except: + fwords = {"vk_fwords": {}, "ds_fwords": {}, "vk_last_fword": 0, "ds_last_fword": 0, "created": datetime.datetime.utcnow().timestamp()} + +ds_delete_fword = requests.delete(f"https://discordapp.com/api/guilds/{config.DISCORD_GUILD}/members/{fwords['ds_last_fword']}/roles/{config.DISCORD_ROLE_fword}", headers={'Authorization': f'Bot {config.DISCORD_TOKEN}'}) +if ds_delete_fword.ok: + aprint.info(f"Discord remove role: {ds_delete_fword.status_code} {ds_delete_fword.reason}") +else: + aprint.error(f"Discord remove role: {ds_delete_fword.status_code} {ds_delete_fword.reason}") + +midnight_text = ["Миднайт!", + "Полночь!", + "Midnight!", + "миднигхт", + "Середина ночи", + "Смена даты!", + "00:00", + "Пора ложиться спать!", + "12 ночи", + "Медьникель!", + "Мёднугхт!", + "Ночное рандеву", + "Опівночі!", + "Арабская ночь", + "Конец смены"] +role_names = ["F-word дня", + "Лох дня", + "Приговорить к расстрелу", + "Малолетний дебил", + "Он отправится в Бразилию", + "Пропустил много стрелок пока битбоксил с пикой точеной", + "В дурку отправляется", + "Поставил крейзифрог на рингтон", + "Попросил наступить на себя еще раз", + "ААХАААА ОН ДОЛБОЁБ ПОСМОТРИТЕ НА НЕГООО!!", + "парннь ты долбоёб", + "Быдло дня", + "Фоткаю лучшего подписчика. Улыбнись", + "Восхваляет солнце"] + +imgvkrequest = requests.post("https://api.vk.com/method/photos.get", data={'owner_id': "-" + str(config.VK_GROUP_ID), + 'album_id': config.VK_ALBUM_MIDNIGHT_ID, + 'count': 1000, + 'access_token': config.VK_SERVICE_TOKEN, + 'v': '5.124'}) +if imgvkrequest.ok: + try: + random_images_query = json.loads(imgvkrequest.text) + random_number = random.randint(0, random_images_query['response']['count']-1) + aprint.info(f"VK method 'photos.get': {imgvkrequest.status_code} {imgvkrequest.reason}, returned photos: {random_images_query['response']['count']}, selected: {random_number}") + image = random_images_query['response']['items'][random_number] + image_request = urlopen(image["sizes"][-1]['url']).read() + image_file = open("md.jpg", "wb") + image_file.write(image_request) + image_file.close() + MIDNIGHT_IMAGE = True + except Exception as e: + MIDNIGHT_IMAGE = False + aprint.error(f"Error while getting image: {str(e)}") +else: + MIDNIGHT_IMAGE = False + aprint.error(f"VK method 'photos.get': {imgvkrequest.status_code} {imgvkrequest.reason}") +selected = random.choice(midnight_text) +now = datetime.datetime.utcnow() +new_date = now + datetime.timedelta(hours=3, minutes=3) +to_ny = datetime.datetime(year=2022, month=1, day=1) - now +if 30 >= to_ny.days > 1: + if MIDNIGHT_IMAGE: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\nДо нового года осталось {to_ny.days} дн.\nКартинка дня:" + else: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\nДо нового года осталось {to_ny.days} дн.\nОшибка при получении картинки дня" +elif to_ny.days == 1: + if MIDNIGHT_IMAGE: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\nДо нового года осталось 24 часа.\nКартинка дня:" + else: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\nДо нового года осталось 24 часа.\nОшибка при получении картинки дня" +elif to_ny.days == 0: + if MIDNIGHT_IMAGE: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\nC новым, 2022 годом!!!\nКартинка дня:" + else: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\nC новым, 2022 годом!!!\nОшибка при получении картинки дня" +else: + if MIDNIGHT_IMAGE: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\nКартинка дня:" + else: + text = f"{selected}\nНаступило {new_date.strftime('%d.%m.%Y')}\n\nОшибка при получении картинки дня" +aprint.info(f"All ready. Waiting...") + +MIDNIGHT = False +while not MIDNIGHT: + now = datetime.datetime.utcnow() + if now.hour == 21 and now.minute == 0 and now.second == 1: + aprint.info("Midnight") + if MIDNIGHT_IMAGE: + dcrequest = requests.post(f"https://discordapp.com/api/channels/{config.DISCORD_CHANNEL_ID}/messages", + headers={'Authorization': f'Bot {config.DISCORD_TOKEN}', + 'Content-Disposition': 'form-data; filename="md.jpg"'}, + data={'payload_json': json.dumps({'content': text}), }, + files={'file': open("md.jpg", 'rb')}) + vkrequest = requests.post("https://api.vk.com/method/messages.send", + data={'peer_id': int(config.VK_DIALOG_ID), 'message': text, + 'random_id': random.randint(-9223372036854775808, 9223372036854775807), + 'attachment': f"photo{image['owner_id']}_{image['id']}", + 'access_token': config.VK_TOKEN, 'v': '5.124'}) + else: + dcrequest = requests.post(f"https://discordapp.com/api/channels/{config.DISCORD_CHANNEL_ID}/messages", + headers={'Authorization': f'Bot {config.DISCORD_TOKEN}'}, + data={'payload_json': json.dumps({'content': text}), }) + vkrequest = requests.post("https://api.vk.com/method/messages.send", + data={'peer_id': int(config.VK_DIALOG_ID), 'message': text, + 'random_id': random.randint(-9223372036854775808, 9223372036854775807), + 'access_token': config.VK_TOKEN, 'v': '5.124'}) + if dcrequest.ok: + aprint.info(f"Message to Discord: {dcrequest.status_code} {dcrequest.reason}") + else: + aprint.error(f"Message to Discord: {dcrequest.status_code} {dcrequest.reason}") + if vkrequest.ok: + aprint.info(f"Message to VK: {vkrequest.status_code} {vkrequest.reason}") + else: + aprint.error(f"Message to VK: {vkrequest.status_code} {vkrequest.reason}") + vk_users_request = requests.post("https://api.vk.com/method/messages.getConversationMembers", + data={'peer_id': config.VK_DIALOG_ID, 'fields': config.VK_ALBUM_MIDNIGHT_ID, + 'group_id': config.VK_GROUP_ID, 'access_token': config.VK_TOKEN, + 'v': '5.124'}) + if vk_users_request.ok: + aprint.info(f"VK method 'messages.getConversationMembers': {vk_users_request.status_code} {vk_users_request.reason}") + random_members_query = json.loads(vk_users_request.text) + if not fwords.get('vk_fword_story_3'): + fwords['vk_fword_story_3'] = 0 + if not fwords.get('vk_fword_story_2'): + fwords['vk_fword_story_2'] = 0 + if not fwords.get('vk_fword_story_1'): + fwords['vk_fword_story_1'] = 0 + new_vk_fword = False + while not new_vk_fword: + random_member = random.choice(random_members_query['response']['profiles']) + black_list = [292394871, 32272927] + white_list = [260168666] + debug_text = f"VK random person: {random_member['first_name']} {random_member['last_name']} - " + if (random_member['id'] != fwords['vk_last_fword'] and random_member['id'] != fwords['vk_fword_story_1'] and random_member['id'] != fwords['vk_fword_story_2'] and random_member['id'] != fwords['vk_fword_story_3'] and random_member['id'] not in white_list) or random_member['id'] in black_list: + aprint.debug(debug_text + "selected") + fwords['vk_fword_story_3'] = fwords['vk_fword_story_2'] + fwords['vk_fword_story_2'] = fwords['vk_fword_story_1'] + fwords['vk_fword_story_1'] = fwords['vk_last_fword'] + fwords['vk_last_fword'] = random_member['id'] + if str(random_member['id']) in fwords["vk_fwords"]: + fwords["vk_fwords"][str(random_member['id'])] += 1 + else: + fwords["vk_fwords"][str(random_member['id'])] = 1 + text = f"{random.choice(role_names)}: @id{random_member['id']} ({random_member['first_name']} {random_member['last_name']})" + vkrequest = requests.post("https://api.vk.com/method/messages.send", + data={'peer_id': config.VK_DIALOG_ID, 'message': text, + 'random_id': random.randint(-9223372036854775808, 9223372036854775807), + 'access_token': config.VK_TOKEN, 'v': '5.124'}) + new_vk_fword = True + if vkrequest.ok: + aprint.info(f"Message to VK: {vkrequest.status_code} {vkrequest.reason}") + else: + aprint.error(f"Message to VK: {vkrequest.status_code} {vkrequest.reason}") + else: + aprint.debug(debug_text + "not selected") + else: + aprint.error(f"VK method 'messages.getConversationMembers': {vk_users_request.status_code} {vk_users_request.reason}") + ds_members_r = requests.get(f"https://discordapp.com/api/guilds/{config.DISCORD_GUILD}/members?limit=1000", + headers={'Authorization': f'Bot {config.DISCORD_TOKEN}'}) + if ds_members_r.ok: + aprint.info(f"Discord guild member list request: {ds_members_r.status_code} {ds_members_r.reason}") + ds_members = json.loads(ds_members_r.text) + if not fwords.get('ds_fword_story_3'): + fwords['ds_fword_story_3'] = 0 + if not fwords.get('ds_fword_story_2'): + fwords['ds_fword_story_2'] = 0 + if not fwords.get('ds_fword_story_1'): + fwords['ds_fword_story_1'] = 0 + new_ds_fword = False + black_list = [235010902640820225, 611931400094089269] + white_list = [324555734115418112] + while not new_ds_fword: + member_fword = random.choice(ds_members) + debug_text = f"DS random person: {member_fword['user']['username']}#{member_fword['user']['discriminator']} - " + if (member_fword['user']['id'] != fwords['ds_last_fword'] and member_fword['user']['id'] != fwords['ds_fword_story_1'] and member_fword['user']['id'] != fwords['ds_fword_story_2'] and member_fword['user']['id'] != fwords['ds_fword_story_3'] and member_fword['user']['id'] not in white_list) or member_fword['user']['id'] in black_list: + aprint.debug(debug_text + "selected") + fwords['ds_fword_story_3'] = fwords['ds_fword_story_2'] + fwords['ds_fword_story_2'] = fwords['ds_fword_story_1'] + fwords['ds_fword_story_1'] = fwords["ds_last_fword"] + fwords["ds_last_fword"] = member_fword['user']['id'] + new_ds_fword = True + random_role = random.choice(role_names) + ds_req_rename_role = requests.patch(f"https://discordapp.com/api/guilds/{config.DISCORD_GUILD}/roles/{config.DISCORD_ROLE_fword}", headers={'Authorization': f'Bot {config.DISCORD_TOKEN}'}, json={'name': random_role, 'color':random.randint(0x000000, 0xffffff)}) + if ds_req_rename_role.ok: + aprint.info(f"Discord rename role: {ds_req_rename_role.status_code} {ds_req_rename_role.reason}") + else: + aprint.error(f"Discord rename role: {ds_req_rename_role.status_code} {ds_req_rename_role.reason}") + text = f"{random_role}: <@{member_fword['user']['id']}>" + ds_send_message_r = requests.post(f"https://discordapp.com/api/channels/{config.DISCORD_CHANNEL_ID}/messages", headers={'Authorization': f'Bot {config.DISCORD_TOKEN}'}, data={'payload_json': json.dumps({'content': text})}) + if ds_send_message_r.ok: + aprint.info(f"Message to Discord: {dcrequest.status_code} {dcrequest.reason}") + else: + aprint.error(f"Message to Discord: {dcrequest.status_code} {dcrequest.reason}") + ds_set_fword = requests.put(f"https://discordapp.com/api/guilds/{config.DISCORD_GUILD}/members/{member_fword['user']['id']}/roles/{config.DISCORD_ROLE_fword}", headers={'Authorization': f'Bot {config.DISCORD_TOKEN}'}) + if ds_set_fword.ok: + aprint.info(f"Discord set role: {ds_set_fword.status_code} {ds_set_fword.reason}") + else: + aprint.error(f"Discord set role: {ds_set_fword.status_code} {ds_set_fword.reason}") + if str(member_fword['user']['id']) in fwords['ds_fwords']: + fwords['ds_fwords'][str(member_fword['user']['id'])] += 1 + else: + fwords['ds_fwords'][str(member_fword['user']['id'])] = 1 + else: + aprint.debug(debug_text + "not selected") + else: + aprint.error(f"Discord guild member list request: {ds_members_r.status_code} {ds_members_r.reason}") + with open("fword.json", "w") as jsn: + json.dump(fwords, jsn) + MIDNIGHT = True + +aprint.info("Done") diff --git a/stats.png b/stats.png new file mode 100644 index 0000000..effdcc6 Binary files /dev/null and b/stats.png differ diff --git a/stats_all.png b/stats_all.png new file mode 100644 index 0000000..8702529 Binary files /dev/null and b/stats_all.png differ diff --git a/stats_all_guilds.png b/stats_all_guilds.png new file mode 100644 index 0000000..d91e92f Binary files /dev/null and b/stats_all_guilds.png differ