From 627c2202da9a107372f9e8940037288ca0ecea33 Mon Sep 17 00:00:00 2001 From: ParzivalEugene Date: Sun, 30 May 2021 20:08:53 +0300 Subject: [PATCH] version 1.6 migrate to postgresql and fix some mistakes in words --- .gitignore | 11 +- README-RU.md | 23 +- README.md | 10 +- main/Samurai.py | 4 +- main/cogs/birthdays.py | 712 +++++++++++------------------ main/cogs/chatting.py | 24 +- main/cogs/commands.py | 4 +- main/cogs/database_connector.py | 46 ++ main/cogs/level_system.py | 530 ++++++++------------- main/cogs/mini_cogs.py | 2 +- main/cogs/on_events_checker.py | 160 +++++++ main/cogs/translator.py | 11 +- main/cogs/wiki_pedia.py | 10 + main/database/samurai_database.png | Bin 0 -> 43792 bytes main/database/samurai_database.sql | 162 +++++++ 15 files changed, 858 insertions(+), 851 deletions(-) create mode 100644 main/cogs/database_connector.py create mode 100644 main/cogs/on_events_checker.py create mode 100644 main/database/samurai_database.png create mode 100644 main/database/samurai_database.sql diff --git a/.gitignore b/.gitignore index 9019de8..55e7cbf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,6 @@ -/main/database/ -/main/keep_alive.py +/main/database/samurai.db /main/cogs/__init__.py -/demo/ -/main/cogs/utils.py -/main/cogs/demo/search.py -/main/cogs/demo/player.py -/main/cogs/music.py /main/cogs/config.py -/main/test.py /main/cogs/test_cog.py +/main/test.py +/main/keep_alive.py \ No newline at end of file diff --git a/README-RU.md b/README-RU.md index addb84e..aa39316 100644 --- a/README-RU.md +++ b/README-RU.md @@ -1,7 +1,7 @@ # Samurai Discord Bot ## Описание -Невероятный дискорд бот по имени Самурай. Он просто незаменим на любом сервере. Самурай может создавать систему уровней, модерировать участников, поздравлять их с днем рождения. Также самурай может играть в простые игры (крестики нолики и четыре в ряд) с разными тактиками. Может проигрывать композиции с ютуб, создавать очереди и плейлисты. Поразительно точно реагирует на обращения к нему и всегда поддержит беседу. Рекомендуется к установке на каждый сервер +Невероятный дискорд бот по имени Самурай. Он просто незаменим на любом сервере. Самурай может создавать систему уровней, модерировать участников, поздравлять их с днем рождения. Также самурай может играть в простые игры (крестики нолики и четыре в ряд) с разными тактиками. Может проигрывать композиции с ютуб, создавать очереди и плейлисты. Поразительно точно реагирует на обращения к нему и всегда поддержит беседу. Рекомендуется к установке на каждый сервер ## Функции Самурай изо всех старается быть живым. Вот список его способностей с командами discord. @@ -23,14 +23,14 @@ | **remove \** | Удалит трек под номером ***number*** | | **play** | Начнет воспроизведение музыки | | **pause** | Остановит воспроизведение музыки | -| **resume** | Возобновит воспроизвеление музыки | +| **resume** | Возобновит воспроизведение музыки | | **stop** | Удалит трек из очереди и остановит воспроизведение музыки | ### Модуль уровней > Модуль Самурая, отвечающий за систему уровней на сервере. Позволяет создавать конкурентную среду и систему прокачки в вашей компании. -| command | decription | +| command | description | | --- | --- | |**level_help** | Выводит помощь по командам, связанных с уровнями. | |**level_add \ \** | Создаст уровень с ***role*** и ***xp*** для ее получения. | @@ -60,7 +60,7 @@ ### Tic Tac Toe > Модуль Самурая, отвечающий за "крестики-нолики". -Позволяет участникам играть в крестики-нолики. Участники могут соревноваться между с собой или попыть свои силы против самурая, с адаптивным уровнем сложности игры и разными типами стратегий. +Позволяет участникам играть в крестики-нолики. Участники могут соревноваться между собой или попытать свои силы против самурая, с адаптивным уровнем сложности игры и разными типами стратегий. Поможет решить споры или потренировать мозг. | command | description | @@ -87,25 +87,25 @@ ### Переводчик > Модуль Самурая, отвечающий за иностранные языки. -Всегда под рукой, необходимо что-то перевести - Самурай тут как тут, не знаешь на каком языке написанно сообщение - Самурай всегда поможет. Когда скучно, можно попытаться угадать язык сообщения. В общем чудо, а не модуль! +Всегда под рукой, необходимо что-то перевести — Самурай тут как тут, не знаешь на каком языке написанно сообщение — Самурай всегда поможет. Когда скучно, можно попытаться угадать язык сообщения. В общем чудо, а не модуль! | command | description | | --- | --- | | **tr_help** | Выводит помощь по командам, связанных с модулем переводчик | | **tr_list** | Выведет список кратких обозначений языков | -| **tr_trans \ \ \** | Переведет ***message*** с ***source lang*** на ***taget lang*** | -| **tr_trans \ \** | Определит язык исходного сообщения и переведет ***message*** на ***taget lang*** | +| **tr_trans \ \ \** | Переведет ***message*** с ***source lang*** на ***target lang*** | +| **tr_trans \ \** | Определит язык исходного сообщения и переведет ***message*** на ***target lang*** | | **tr_detect_lang \** | Выводит язык ***message*** | | **tr_game** | Начнет игру ***"угадай язык"*** | ### Полезные модули > Модули, которые я не смог отнести в отдельную категорию, но они все еще такие же классные. -Прогноз погоды по всему миру, вдохновление на ближайший день, рассказы о твоем будущем и решение судьбы броском монетки. Да-да все в этом модуле. +Прогноз погоды по всему миру, вдохновение на ближайший день, рассказы о твоем будущем и решение судьбы броском монетки. Да-да все в этом модуле. | command | description | | --- | --- | | **toss** | Подкинет монетку | -| **8ball \** | Спроси вопрос у магического шара, и он поведует тебе судьбу | +| **8ball \** | Спроси вопрос у магического шара, и он поведает тебе судьбу | | **forecast \** | Вернет прогноз погоды в выбранном месте | | **inspire** | Вдохновит тебя на великие свершения | @@ -113,10 +113,11 @@ > Первый старт -- день рождения Самурая - **24 марта 2021** В пятнадцатилетнем возрасте, имея свой дискорд сервер (на то время сервер имел аудиторию <100 человек), я стал задумываться о разработке своего бота. Я знал о существовании других ботов и меня поражало, насколько классными они могут быть. Замечательный [Groovy](https://groovy.bot/), удобный [Carl](https://carl.gg/) и незаменимый [KD](https://top.gg/bot/414925323197612032) являлись неотъемлемой частью моего сервера. Но со временем, в каждый из этих ботов, пришла система подписок, а мы, будучи школьниками, не могли отдать даже 5$ за groovy. -Имея небольшие навыки программирования на [python](https://en.wikipedia.org/wiki/Python_(programming_language)), я захотел создать свой первый масштабный проект, в котором я бы мог связать все, что я умею. В моем видении, это должен был быть проект, где я могу полностью реализовать свой потенциал: использовать базу данных и [SQL](https://ru.wikipedia.org/wiki/SQL), обрабатывать https запросы, писать красивый и структурированный код, использовать различные api и модули. WEB приложения в этом плане наиболее рентабельны, ведь они многогранны и могут нести в себе любую функцию. Я стал интересоваться ботами и сайтами, пытался создавать ботов в [telegram](https://tlgrm.ru/), но это меня абсолютно не затянуло, я не видел как бы я смог развить своего бота на этой платформе. Но одним вечером, сидя с подругой в дискорде, я задумался как было бы круто, если бы бот реагировал на мои сообщения. Например я пишу как мне грустно, а он подбодрит меня. Звучало как мечта, но для меня это стало целью. Покопавшись неделю в [Discord Api](https://discordpy.readthedocs.io/en/latest/api.html) и посмотрев кучу туториалов, я написал самую простенькую версию бота. Он реагировал на пару-тройку сообщений лишь одной фразой и мог присылать стикеры. Но проведя месяц за кодингом, мой бот научился многому, как можно увидеть по главе функции. Для меня это лишь первая ступень и я планирую дальше развивать бота. +Имея небольшие навыки программирования на [python](https://en.wikipedia.org/wiki/Python_(programming_language)), я захотел создать свой первый масштабный проект, в котором я бы мог связать все, что я умею. В моем видении, это должен был быть проект, где я могу полностью реализовать свой потенциал: использовать базу данных и [SQL](https://ru.wikipedia.org/wiki/SQL), обрабатывать https запросы, писать красивый и структурированный код, использовать различные api и модули. WEB приложения в этом плане наиболее рентабельны, ведь они многогранны и могут нести в себе любую функцию. Я стал интересоваться ботами и сайтами, пытался создавать ботов в [telegram](https://tlgrm.ru/), но это меня абсолютно не затянуло, я не видел как бы я смог развить своего бота на этой платформе. Но одним вечером, сидя с подругой в дискорде, я задумался как было бы круто, если бы бот реагировал на мои сообщения. Например, я пишу как мне грустно, а он подбодрит меня. Звучало как мечта, но для меня это стало целью. Покопавшись неделю в [Discord Api](https://discordpy.readthedocs.io/en/latest/api.html) и посмотрев кучу туториалов, я написал самую простенькую версию бота. Он реагировал на пару-тройку сообщений лишь одной фразой и мог присылать стикеры. Но проведя месяц за кодингом, мой бот научился многому, как можно увидеть по главе функции. Для меня это лишь первая ступень и я планирую дальше развивать бота. ## Автор Автором этого малыша являюсь я - [Parzival](https://github.com/ParzivalEugene), 15 летний челик из Москвы. Увлекаюсь программированием на языке [python](https://en.wikipedia.org/wiki/Python_(programming_language)), могу склепать десктопное приложение на [PyQT](https://en.wikipedia.org/wiki/PyQt), написать игру на [pygame](https://en.wikipedia.org/wiki/Pygame), знаю азы [html](https://en.wikipedia.org/wiki/HTML) и [css](https://en.wikipedia.org/wiki/CSS), короче говоря самые базовые знания питониста. Мой [дискорд сервер](https://discord.gg/WuTaFrker6). +![test](https://www.codewars.com/users/ParzivalEugene/badges) ## License -На данный момент не особо шарю, как можно юзать мой код, но пока он открыт всем желающим для некомерческого использования. +На данный момент не особо шарю, как можно юзать мой код, но пока он открыт всем желающим для некоммерческого использования. diff --git a/README.md b/README.md index 3f33a51..65f0193 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,8 @@ Always at hand, something needs to be translated - Samurai is right there, you d | --- | --- | | **tr_help** | Displays help for commands related to the translator module. | | **tr_list** | Displays a list of language abbreviations. | -| **tr_trans \ \ \** | Translate ***message*** from ***source lang*** to ***taget lang***. | -| **tr_trans \ \** | Determines the language of the original message and translates ***message*** into ***taget lang***. | +| **tr_trans \ \ \** | Translate ***message*** from ***source lang*** to ***target lang***. | +| **tr_trans \ \** | Determines the language of the original message and translates ***message*** into ***target lang***. | | **tr_detect_lang \** | Displays language ***message***. | | **tr_game** | Start the game ***"guess the language"***. | @@ -113,13 +113,13 @@ Weather forecast around the world, inspiration for the coming day, stories about ## History of creation > First start - Samurai's birthday - **March 24, 2021** -At the age of fifteen, having my own discord server (at that time the server had an audience of <100 people), I began to think about developing my own bot. I knew about other bots and it amazed me how cool they can be. The wonderful [Groovy](https://groovy.bot/), the handy [Carl](https://carl.gg/) and the irreplaceable [KD](https://top.gg/bot/414925323197612032) were an integral part of my server. But over time, a subscription system came to each of these bots, and we, being schoolchildren, could not give even $5 for groovy. -Having a little programming skills in [python](https://en.wikipedia.org/wiki/Python_ (programming_language)), I wanted to create my first large-scale project in which I could link everything I can. In my vision, this was supposed to be a project where I can fully realize my potential: use a database and [SQL](https://ru.wikipedia.org/wiki/SQL), handle https requests, write beautiful and structured code , use different api and modules. In this regard, WEB applications are the most cost-effective, because they are multifaceted and can carry any function. I became interested in bots and sites, tried to create bots in [telegram](https://tlgrm.ru/), but this did not drag me out at all, I did not see how I could develop my bot on this platform. But one evening, sitting with a friend in discord, I thought how cool it would be if the bot responded to my messages. For example, I write how sad I am, and he will cheer me up. It sounded like a dream, but it became a goal for me. After digging in [Discord Api](https://discordpy.readthedocs.io/en/latest/api.html) for a week and looking at a bunch of tutorials, I wrote the simplest version of the bot. He reacted to a couple of messages with just one phrase and could send stickers. But after spending a month coding, my bot has learned a lot, as you can see from the function chapter. For me, this is only the first step and I plan to further develop the bot. +At the age of fifteen, having my own discord server (at that time the server had an audience of <100 people), I began to think about developing my own bot. I knew about other bots, and it amazed me how cool they can be. The wonderful [Groovy](https://groovy.bot/), the handy [Carl](https://carl.gg/) and the irreplaceable [KD](https://top.gg/bot/414925323197612032) were an integral part of my server. But over time, a subscription system came to each of these bots, and we, being schoolchildren, could not give even $5 for groovy. +Having a little programming skills in [python](https://en.wikipedia.org/wiki/Python_ (programming_language)), I wanted to create my first large-scale project in which I could link everything I can. In my vision, this was supposed to be a project where I can fully realize my potential: use a database and [SQL](https://ru.wikipedia.org/wiki/SQL), handle https requests, write beautiful and structured code , use different api and modules. In this regard, WEB applications are the most cost-effective, because they are multifaceted and can carry any function. I became interested in bots and sites, tried to create bots in [telegram](https://tlgrm.ru/), but this did not drag me out at all, I did not see how I could develop my bot on this platform. But one evening, sitting with a friend in discord, I thought how cool it would be if the bot responded to my messages. For example, I write how sad I am, and he will cheer me up. It sounded like a dream, but it became a goal for me. After digging in [Discord Api](https://discordpy.readthedocs.io/en/latest/api.html) for a week and looking at a bunch of tutorials, I wrote the simplest version of the bot. He reacted to a couple of messages with just one phrase and could send stickers. But after spending a month coding, my bot has learned a lot, as you can see from the function chapter. For me, this is only the first step, and I plan to further develop the bot. > It was written on April 22nd, 2021. ## Author -The author of this kid is me - [Parzival](https://github.com/ParzivalEugene), 15 year old man from Moscow. I am fond of programming in the [python](https://en.wikipedia.org/wiki/Python_ (programming_language)), I can create a desktop application on [PyQT](https://en.wikipedia.org/wiki/PyQt), write a game on [pygame](https://en.wikipedia.org/wiki/Pygame), I know the basics of [html](https://en.wikipedia.org/wiki/HTML) and [css](https://en.wikipedia.org/wiki/CSS), in short, the most basic knowledge of a pythonist. My [discord server](https://discord.gg/WuTaFrker6). +The author of this kid is me - [Parzival](https://github.com/ParzivalEugene), 15-year-old man from Moscow. I am fond of programming in the [python](https://en.wikipedia.org/wiki/Python_ (programming_language)), I can create a desktop application on [PyQT](https://en.wikipedia.org/wiki/PyQt), write a game on [pygame](https://en.wikipedia.org/wiki/Pygame), I know the basics of [html](https://en.wikipedia.org/wiki/HTML) and [css](https://en.wikipedia.org/wiki/CSS), in short, the most basic knowledge of a pythonist. My [discord server](https://discord.gg/WuTaFrker6). ## License At the moment, I'm not really looking at how my code can be used, but so far it is open to everyone for non-commercial use. \ No newline at end of file diff --git a/main/Samurai.py b/main/Samurai.py index 0c894a8..c5d22f9 100644 --- a/main/Samurai.py +++ b/main/Samurai.py @@ -11,6 +11,7 @@ from cogs.click_to_roles import ClickToRoles from cogs.level_system import LevelSystem from cogs.wiki_pedia import Wikipedia +from cogs.on_events_checker import OnEventsChecker from keep_alive import keep_alive @@ -24,7 +25,7 @@ async def on_ready(): type=discord.ActivityType.listening, name=f"{prefix}help - INVINCIBLE WARRIORS") ) - await Birthdays(bot).check_birthday.start() + await Birthdays(bot).check_birthdays.start() bot.add_cog(Birthdays(bot)) @@ -37,5 +38,6 @@ async def on_ready(): bot.add_cog(ClickToRoles(bot)) bot.add_cog(Wikipedia(bot)) bot.add_cog(LevelSystem(bot)) +bot.add_cog(OnEventsChecker(bot)) keep_alive() bot.run(token_for_bot) diff --git a/main/cogs/birthdays.py b/main/cogs/birthdays.py index d1d87fb..dda0571 100644 --- a/main/cogs/birthdays.py +++ b/main/cogs/birthdays.py @@ -1,10 +1,10 @@ import discord +from discord.utils import get from discord.ext import commands, tasks -from datetime import date -import sqlite3 -import os +import datetime from random import choice from cogs.commands import commands_names +from cogs.database_connector import Database from cogs.config import * @@ -12,47 +12,8 @@ class Birthdays(commands.Cog): def __init__(self, bot): self.bot = bot - @tasks.loop(hours=12) - async def check_birthday(self): - today = "-".join(date.today().isoformat().split("-")[1:]) - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_users = cursor.execute("SELECT * FROM user").fetchall() - check_birthdays_users = cursor.execute("SELECT * FROM birthdays").fetchall() - check_birthdays_servers = cursor.execute("SELECT * FROM birthdays_chats").fetchall() - check_server = cursor.execute("SELECT * FROM server").fetchall() - check_connect = cursor.execute("SELECT * FROM connect").fetchall() - - if not (check_users and check_birthdays_users and check_birthdays_servers and check_server and check_connect): - conn.commit() - conn.close() - return - - for db_server_id, chat_id in cursor.execute("SELECT * FROM birthdays_chats").fetchall(): - db_users_id = cursor.execute("SELECT user_id FROM connect WHERE server_id = ?", (db_server_id,)).fetchone() - db_user_id = db_users_id[0] if isinstance(db_users_id, tuple) else db_users_id - for db_user_id in db_users_id: - user_id = cursor.execute("SELECT user_id FROM user WHERE id = ?", (db_user_id,)).fetchone() - user_id = user_id[0] if isinstance(user_id, tuple) else user_id - user = self.bot.get_user(user_id) - user_date = cursor.execute("SELECT date FROM birthdays WHERE id = ?", (db_user_id,)).fetchone() - user_date = user_date[0][5:] if isinstance(user_date, tuple) else user_date[5:] - channel = self.bot.get_channel(chat_id) - if user_date == today: - embed = discord.Embed( - title=f"День рождения {user.name}!!!", - description=f"С днем рождения моя сладенькая булочка {user.mention}!!! Желаю тебе всего самого наилучшего в этот прекрасный день. Бля а лицо то у тебя какое прекрасное, " - f"ебать а умеешь то ты сколько, ебать будь я живой я бы тебя трахнул реально {discord.utils.get(self.bot.emojis, name='kavo')}. В общем зайка моя, не расстраивай " - f"маму, веди себя хорошо и никогда не забывай про старичка {self.bot.user.mention}, я тебя всегда буду любить!!!", - colour=discord.Colour.purple() - ) - await channel.send(embed=embed) - conn.commit() - conn.close() - @commands.command(name=commands_names["birthdays"]["help"]) - async def rules_birthday(self, ctx): + async def birthday_help(self, ctx): embed = discord.Embed( title="Информация о календаре дней рождений", description=":calendar_spiral: :calendar:", @@ -72,470 +33,317 @@ async def rules_birthday(self, ctx): inline=False) await ctx.send(embed=embed) - @commands.has_permissions(manage_channels=True) - @commands.command(name=commands_names["birthdays"]["set chat"]) - async def set_chat_birthday(self, ctx, chat: discord.TextChannel): - user_id = ctx.message.author.id - guild_id = ctx.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server - db_user_id = check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) + @tasks.loop(hours=12) + async def check_birthdays(self): + today = "-".join(datetime.date.today().isoformat().split("-")[1:]) + with Database() as db: + db.execute('SELECT * FROM "default".users') + check_users = db.fetchall() + db.execute('SELECT * FROM "default".birthdays') + check_birthdays = db.fetchall() + db.execute('SELECT info_chat FROM "default".servers_chats') + check_chats = db.fetchall() + db.execute('SELECT * FROM "default".servers') + check_servers = db.fetchall() + db.execute('SELECT * FROM "default".connect') + check_connect = db.fetchall() + if not (check_users and check_birthdays and check_chats and check_servers and check_connect): + return + + for db_server_id, info_chat_id in db.execute('SELECT server_id, info_chat FROM "default".servers_chats').fetchall(): + info_chat_id = info_chat_id[0] if isinstance(info_chat_id, tuple) else info_chat_id + if not info_chat_id: + continue + db_users_ids = db.execute('SELECT user_id FROM "default".connect WHERE server_id = %s', [db_server_id]).fetchall() + db_users_ids = db_users_ids[0] if isinstance(db_users_ids, tuple) else db_users_ids + for db_user_id in db_users_ids: + user_id = db.execute('SELECT discord_user_id FROM "default".users WHERE id = %s', [db_user_id]).fetchone() + user_id = user_id[0] if isinstance(user_id, tuple) else user_id + user = self.bot.get_user(user_id) + user_date = db.execute('SELECT date FROM "default".birthdays WHERE user_id = %s', [db_user_id]).fetchone() + user_date = user_date[0] if isinstance(user_date, tuple) else user_date + if not user_date: + continue + user_date = "-".join(user_date.isoformat().split("-")[1:]) + if user_date == today: + channel = self.bot.get_channel(info_chat_id) + embed = discord.Embed( + title=f':tada: День рождения {user.name} :tada:', + description=f"С днем рождения моя сладенькая булочка {user.mention}!!! Желаю тебе всего самого наилучшего в этот прекрасный день. Бля а лицо то у тебя какое прекрасное, " + f"ебать а умеешь то ты сколько, ебать будь я живой я бы тебя трахнул реально {get(self.bot.emojis, name='kavo')}. В общем зайка моя, не расстраивай маму, веди " + f"себя хорошо и никогда не забывай про старичка {self.bot.user.mention}, я тебя всегда буду любить!!!", + colour=discord.Colour.purple() + ) + embed.set_thumbnail(url=user.avatar_url) + await channel.send(embed=embed) - db_chat_id = cursor.execute("SELECT chat_id FROM birthdays_chats WHERE id = ?", (db_server_id,)).fetchone() - db_chat_id = db_chat_id[0] if isinstance(db_chat_id, tuple) else db_chat_id - if db_chat_id: - up, delete = commands_names["birthdays"]["up chat"], commands_names["birthdays"]["del chat"] - cur_chat = self.bot.get_channel(db_chat_id).mention - conn.commit() - conn.close() + @commands.command(name=commands_names["birthdays"]["add"]) + async def add_birthday(self, ctx, year: int, month: int, day: int): + if not (0 < year < datetime.date.today().year and 0 < month < 13 and 0 < day < 32): return await ctx.send(choice([ - f"Зайка, я уже сюда пишу {cur_chat}, юзай либо **.{up}**, либо **.{delete}**", f"Пупсик, у меня уже есть чат, юзай **.{up}** или **.{delete}**", - f"Сынок я вас тут поздравлять буду {cur_chat}, юзай **.{up}** или **.{delete}**" + "Некорректная дата", "Внучок, неправильно ввел дату!", "Не обманывай старого, ты не мог тогда родиться", "Ошибся, молодой, некорректные данные" ])) + user_id = ctx.message.author.id + user_date = datetime.date(year, month, day) + month, day = user_date.month, user_date.day + with Database() as db: + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + db.execute('SELECT date FROM "default".birthdays WHERE user_id = %s', [db_user_id]) + db_date = db.fetchone() + if db_date: + db_date = str(db_date[0]).replace("-", ".") + return await ctx.send(choice([ + f"Внучок я уже сохранил дату твоего дня рождения - {db_date}. Если хочешь изменить внесенные данные, воспользуйся **.{commands_names['birthdays']['up']}**", + f"Малыш, я сохранил информацию, что ты родился {db_date}. Если хочешь изменить внесенные данные, воспользуйся **.{commands_names['birthdays']['up']}**", + f"Зайка, я запомнил, что ты родился {db_date}. Если хочешь изменить внесенные данные, воспользуйся **.{commands_names['birthdays']['up']}**" + ])) + db.execute('INSERT INTO "default".birthdays(user_id, date) VALUES (%s, %s)', [db_user_id, user_date.isoformat()]) - cursor.execute("INSERT INTO birthdays_chats(id,chat_id) VALUES(?,?)", (db_server_id, chat.id)) - conn.commit() - conn.close() - await ctx.send(choice([ - f"Все сынок ебать запомнил куда вам поздравления писать {discord.utils.get(self.bot.emojis, name='giggle')}", f"Ждите сюда {chat.mention} поздравления малышки", - f"Все лапапуськи ждите сюда {chat.mention} поздравления" - ])) + await ctx.send(choice([ + "Запомнил, сладенький. Жди поздравления :sparkling_heart:", "Обязательно поздравлю тебя, калапуська", + f"Кажется я стал любить {month}.{day} еще больше. Обещаю, что поздравлю тебя сладенький!", f"Муська моя, жди {month}.{day} самые теплые слова от меня :sparkling_heart:" + ])) - @set_chat_birthday.error - async def set_chat_birthday_error(self, ctx, error): + @add_birthday.error + async def add_birthday_error(self, ctx, error): if isinstance(error, commands.MissingRequiredArgument): return await ctx.send(choice([ - "Молодой ты не указал чат", "Внучок неполные данные", "Данные не полны ©️ Магистр Йода" + "Молодой ты указал дату не полностью", "Внучок неполные данные", "Данные не полны ©️ Магистр Йода" ])) if isinstance(error, commands.BadArgument): await ctx.send(choice([ - "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, аргументом должен быть текстовый канал", - "Внучок проверь ввод, это должен быть текстовый чат" + "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, все три параметра - числа", "Внучок проверь ввод, все три аргумента - числа" ])) - @commands.has_permissions(manage_channels=True) - @commands.command(name=commands_names["birthdays"]["up chat"]) - async def update_chat_birthday(self, ctx, chat: discord.TextChannel): - user_id = ctx.message.author.id - guild_id = ctx.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server - db_user_id = check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) - - db_chat_id = cursor.execute("SELECT chat_id FROM birthdays_chats WHERE id = ?", (db_server_id,)).fetchone() - db_chat_id = db_chat_id[0] if isinstance(db_chat_id, tuple) else db_chat_id - if not db_chat_id: - add = commands_names["birthdays"]["set chat"] - conn.commit() - conn.close() + @commands.command(name=commands_names["birthdays"]["up"]) + async def update_birthdays(self, ctx, year: int, month: int, day: int): + if not (0 < year < datetime.date.today().year and 0 < month < 13 and 0 < day < 32): return await ctx.send(choice([ - f"Малыш, я еще не ебу какой у тебя был первоначальный канал, юзай **.{add}**", f"Зайка, мне бы узнать, что обновлять, сначала юзай **.{add}**" + "Некорректная дата", "Внучок, неправильно ввел дату!", "Не обманывай старого, ты не мог тогда родиться", "Ошибся, молодой, некорректные данные" ])) - if db_chat_id == chat.id: - conn.commit() - conn.close() - return await ctx.send(choice([ - "Внучок, ты указал такой же чат, что у меня уже есть", "Данные ничем не отличаются от базы данных", "Такие же данные как и в базе" + user_id = ctx.message.author.id + user_date = datetime.date(year, month, day) + month, day = user_date.month, user_date.day + with Database() as db: + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + db.execute('SELECT date FROM "default".birthdays WHERE user_id = %s', [db_user_id]) + db_date = db.fetchone() + if db_date is None: + return await ctx.send(choice([ + f"Сладенький, я к сожалению не знаю когда ты родился, поэтому не могу обновить отсутствующую информацию. Чтобы внести данные, используй **{commands_names['birthdays']['add']}**", + f"Лапочка, мне нечего обновлять, я к сожалению не знаю когда ты родился. Используй **{commands_names['birthdays']['add']}**, если хочешь, чтобы я поздравил тебя {month}.{day}" + ])) + if db_date[0] == user_date: + return await ctx.send(choice([ + "Зайка, я и так запомнил такую же дату", "Малышка, я прекрасно помню что ты родилась в этот день, не обязательно его обновлять", + "Сладенький, я и так помню, что ты родился в этот день" + ])) + db.execute('UPDATE "default".birthdays SET date = %s WHERE user_id = %s', [user_date, db_user_id]) + await ctx.send(choice([ + "Обновил базу данных", f"Теперь придется заново запоминать, когда ты родился {get(self.bot.emojis, name='kavo')}", "Я отлично запомню и эту дату)" ])) - cursor.execute("UPDATE birthdays_chats SET chat_id = ? WHERE id = ?", (chat.id, db_server_id)) - conn.commit() - conn.close() - await ctx.send(choice([ - f"Все сынок ебать запомнил куда вам поздравления писать {discord.utils.get(self.bot.emojis, name='giggle')}", f"Ждите сюда {chat.mention} поздравления малышки", - f"Все лапапуськи ждите сюда {chat.mention} поздравления" - ])) - @update_chat_birthday.error - async def update_chat_birthday_error(self, ctx, error): + @update_birthdays.error + async def update_birthday_error(self, ctx, error): if isinstance(error, commands.MissingRequiredArgument): return await ctx.send(choice([ - "Молодой ты не указал чат", "Внучок неполные данные", "Данные не полны ©️ Магистр Йода" + "Молодой ты указал дату не полностью", "Внучок неполные данные", "Данные не полны ©️ Магистр Йода" ])) if isinstance(error, commands.BadArgument): await ctx.send(choice([ - "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, аргументом должен быть текстовый канал", - "Внучок проверь ввод, это должен быть текстовый чат" + "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, все три параметра - числа", "Внучок проверь ввод, все три аргумента - числа" ])) - @commands.has_permissions(manage_channels=True) - @commands.command(name=commands_names["birthdays"]["del chat"]) - async def delete_chat_birthday(self, ctx): + @commands.command(name=commands_names["birthdays"]["delete"]) + async def delete_birthdays(self, ctx): user_id = ctx.message.author.id - guild_id = ctx.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server - db_user_id = check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) - - db_chat_id = cursor.execute("SELECT chat_id FROM birthdays_chats WHERE id = ?", (db_server_id,)).fetchone() - db_chat_id = db_chat_id[0] if isinstance(db_chat_id, tuple) else db_chat_id - if not db_chat_id: - add = commands_names["birthdays"]["set chat"] - conn.commit() - conn.close() - return await ctx.send(choice([ - f"Малыш, у меня вообще нет данных о чате, создай новый **.{add}**", f"Зайка, мне бы узнать, что удалять, сначала юзай **.{add}**" + with Database() as db: + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + db.execute('SELECT date FROM "default".birthdays WHERE user_id = %s', [db_user_id]) + db_date = db.fetchone() + if db_date is None: + return await ctx.send(choice([ + "Малыш, я еще не знаю когда ты родился, поэтому мне нечего забывать", "Сладенький, ты еще не вносил данные, мне нечего удалять", + "Лапочка, я к сожалению и не знал, когда ты родился, поэтому мне нечего забывать" + ])) + db.execute('DELETE FROM "default".birthdays WHERE user_id = %s', [db_user_id]) + await ctx.send(choice([ + "Ладно зайка, если ты не хочешь, чтобы я тебя поздравлял, то так мне и надо :sob: :sob: :sob:", "Ты не хочешь, чтобы я тебя поздравлял??? :sob: :sob: :sob:", + "Тебе не нужны мои поздравления :cry:", ":cry: Видишь эти слезы? Ты доволен?" ])) - cursor.execute("DELETE FROM birthdays_chats WHERE id = ?", (db_server_id,)) - conn.commit() - conn.close() - await ctx.send(choice([ - "не хотите вы от меня поздравления получать :sob: :sob: :sob:", "НЕ НУЖНЫ ВАМ МОИ ПОЗДРАВЛЕНИЯ!!!! :sob: :sob: :sob: :sob: ", "От старого бота даже поздравления получать не хотят :sob: " - ])) - - @commands.command(name=commands_names["birthdays"]["show chat"]) - async def show_chat_birthdays(self, ctx): + @commands.command(name=commands_names["birthdays"]["show bd"]) + async def show_birthday(self, ctx): user_id = ctx.message.author.id - guild_id = ctx.guild.id - guild = ctx.guild - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server - db_user_id = check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) + user = ctx.message.author + with Database() as db: + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + db.execute('SELECT date FROM "default".birthdays WHERE user_id = %s', [db_user_id]) + db_date = db.fetchone() + if db_date is None: + return await ctx.send(choice([ + f"Малыш, я еще не знаю когда ты родился, используй **{commands_names['birthdays']['add']}**, чтобы внести данные", + "Сладенький, ты еще не вносил данные, поэтому я не знаю, когда ты родился :confused:", "Лапочка, я к сожалению и не знал, когда ты родился, поэтому мне нечего выводить" + ])) + db_date = str(db_date[0]).replace("-", ".") + embed = discord.Embed( + title=f"День рождения {user.name}", + description=f"""{choice([ + f"Этот сладенький пупсик {user.mention} родился", f"Эта лапопуличка {user.mention} родилась", f"Этот экстра зайчик {user.mention} родился", + f"Эта сладенькая зайка {user.mention} родилась", f"Эта муська {user.mention} родилась" + ])} **{db_date}**""", + colour=discord.Colour.purple() + ) + embed.set_thumbnail(url=user.avatar_url) + await ctx.send(embed=embed) - db_chat_id = cursor.execute("SELECT chat_id FROM birthdays_chats WHERE id = ?", (db_server_id,)).fetchone() - db_chat_id = db_chat_id[0] if isinstance(db_chat_id, tuple) else db_chat_id - if not db_chat_id: - add = commands_names["birthdays"]["set chat"] - conn.commit() - conn.close() + @commands.command(name=commands_names["birthdays"]["show bds"]) + async def show_birthdays(self, ctx): + server = ctx.guild + with Database() as db: + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server.id]) + db_server_id = db.fetchone()[0] + db.execute('SELECT user_id FROM "default".connect WHERE server_id = %s', [db_server_id]) + db_users_ids = db.fetchall() + output_embed = [] + count = 1 + for db_user_id in db_users_ids: + db.execute('SELECT date FROM "default".birthdays WHERE user_id = %s', [db_user_id]) + db_date = db.fetchone() + if db_date is None: + continue + db_date = str(db_date[0]).replace("-", ".") + db.execute('SELECT discord_user_id FROM "default".users WHERE id = %s', [db_user_id]) + user_id = db.fetchone()[0] + output_embed.append(f"{count}. {get(server.members, id=user_id).mention} - **{db_date}**") + count += 1 + if not output_embed: return await ctx.send(choice([ - f"Внучок у меня пока нет чата, добавь его командой **.{add}**", f"Сладенький мой, у меня нет инфы про чат, поэтому ты можешь добавить его командой **.{add}**", - f"Пупсик заюзай **.{add}**, потому что у меня нет чата(" + "Сладкий, пока никто на сервере не рассказал мне, когда он родился :cry:", "К сожалению, еще никто из вас не рассказал мне, когда родился :cry:", "Я про вас еще ничего не знаю :cry:" ])) - cur_chat = self.bot.get_channel(db_chat_id).mention - conn.commit() - conn.close() embed = discord.Embed( - title=f"{guild.name} congrats chat", - description=f"Вы будете получать поздравления в этот чат: {cur_chat}", + title=f"Дни рождения сервера {server.name}", + description="\n".join(output_embed), colour=discord.Colour.purple() ) + embed.set_thumbnail(url=server.icon_url) await ctx.send(embed=embed) - @commands.command(name=commands_names["birthdays"]["add"]) - async def add_birthday(self, ctx, year: int, month: int, day: int): - user_id = ctx.message.author.id - guild_id = ctx.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server - db_user_id = check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) - check_date = cursor.execute("SELECT date FROM birthdays WHERE id = ?", (db_user_id,)).fetchone() - check_date = check_date[0] if isinstance(check_date, tuple) else check_date - if check_date: - conn.commit() - conn.close() - return await ctx.send(choice([ - f"Внучок, твоя дата уже есть, юзай **{commands_names['birthdays']['up']}**", f"Молодой, ты уже есть в базе, юзай **{commands_names['birthdays']['up']}**", - f"Старичок, ты уже вносил данные, юзай **{commands_names['birthdays']['up']}** или **{commands_names['birthdays']['delete']}**", "Внучок, по тебе уже есть инфа" - ])) - if not (0 < year < 2020 and 0 < month < 13 and 0 < day < 32): - conn.commit() - conn.close() + @commands.has_permissions(manage_channels=True) + @commands.command(name=commands_names["birthdays"]["set chat"]) + async def set_chat_birthday(self, ctx, chat: discord.TextChannel): + server_id = ctx.guild.id + with Database() as db: + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]) + db_server_id = db.fetchone()[0] + db.execute('SELECT info_chat FROM "default".servers_chats WHERE server_id = %s', [db_server_id]) + info_chat_id = db.fetchone() + info_chat_id = info_chat_id[0] if isinstance(info_chat_id, tuple) else info_chat_id + if info_chat_id: + up, delete = commands_names["birthdays"]["up chat"], commands_names["birthdays"]["del chat"] + info_chat = self.bot.get_channel(info_chat_id).mention + return await ctx.send(choice([ + f"Зайка, поздравления уже приходят в {info_chat}. Используй **.{up}** или **.{delete}** для обновления или удаления информации соответственно", + f"Сладенький, я поздравляю вас в {info_chat}. Используй **.{up}** или **.{delete}** для обновления или удаления информации соответственно" + ])) + db.execute('UPDATE "default".servers_chats SET info_chat = %s WHERE server_id = %s', [chat.id, db_server_id]) return await ctx.send(choice([ - "Внучок, не обманывай старого, неверный ввод", "Молодежь над дедом издевается, неправильный ввод", "Сынок, неправильно данные ввел", "Внучок, ты не мог тогда родиться" + f"Ждите поздравления в {chat.mention} мои хорошие {get(self.bot.emojis, name='wowcry')}", f"Сладкие мои, ждите поздравления в {chat.mention}", + f"Обязательно поздравлю вас в {chat.mention}", f"Поздравлю всех-всех-всех в этом чате {chat.mention} {get(self.bot.emojis, name='wowcry')}" ])) - user_date = date(year, month, day).isoformat() - cursor.execute("INSERT INTO birthdays(id,date) VALUES(?,?)", (db_user_id, user_date)) - conn.commit() - conn.close() - await ctx.send(choice([ - "Внучок, успешно добавил дату, жди поздравления :sparkling_heart:", "Сынок, дату добавил, поздравлю тебя в этот день!", "Ну все, можешь ждать от меня поздравления!", - "Обязательно поздравлю тебя в этот день!" - ])) - - @add_birthday.error - async def add_birthday_error(self, ctx, error): + @set_chat_birthday.error + async def set_chat_birthday_error(self, ctx, error): if isinstance(error, commands.MissingRequiredArgument): return await ctx.send(choice([ - "Молодой ты указал дату неполностью", "Внучок неполные данные", "Данные не полны ©️ Магистр Йода" + "Зайка, ты не указал чат", "Малыш неполные данные", "Данные не полны ©️ Магистр Йода" ])) if isinstance(error, commands.BadArgument): await ctx.send(choice([ - "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, все три параметра числа", "Внучок проверь ввод, все три аргемента числа" - ])) - - @commands.command(name=commands_names["birthdays"]["up"]) - async def update_birthdays(self, ctx, year: int, month: int, day: int): - user_id = ctx.message.author.id - guild_id = ctx.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server[0] if isinstance(check_server, tuple) else check_server - db_user_id = check_user[0] if isinstance(check_user, tuple) else check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) - check_date = cursor.execute("SELECT date FROM birthdays WHERE id = ?", (db_user_id,)).fetchone() - check_date = check_date[0] if isinstance(check_date, tuple) else check_date - if not check_date: - return await ctx.send(choice([ - f"Внучок, твоей даты еще нет, юзай **{commands_names['birthdays']['add']}**", f"Молодой, тебя еще нет в базе, юзай **{commands_names['birthdays']['add']}**", - f"Старичок, ты еще не вносил данные, юзай **{commands_names['birthdays']['add']}**", - f"Внучок, по тебе еще нет есть инфы, обновлять нечего {discord.utils.get(self.bot.emojis, name='ahuet')}" - ])) - if not (0 < year < 2020 and 0 < month < 13 and 0 < day < 32): - conn.commit() - conn.close() - return await ctx.send(choice([ - "Внучок, не обманывай старого, неверный ввод", "Молодежь над дедом издевается, неправильный ввод", "Сынок, неправильно данные ввел", "Внучок, ты не мог тогда родиться" + "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, аргументом должен быть текстовый канал", + "Внучок проверь ввод, это должен быть текстовый чат" ])) - current_date = cursor.execute("SELECT date FROM birthdays WHERE id = ?", (db_user_id,)).fetchone() - current_date = current_date[0] if isinstance(current_date, tuple) else current_date - user_date = date(year, month, day).isoformat() - if current_date == user_date: - conn.commit() - conn.close() - return await ctx.send(choice([ - "Внучок, ты ввел те же данные, что у меня есть", "Апдейт ничем не отличается от текущих данных", "У меня в базе такие же данные", - "Апдейта не будет, он принял ислам. Внучок, данные те же что и в базе" - ])) - cursor.execute("UPDATE birthdays SET date = ? WHERE id = ?", (user_date, db_user_id)) - conn.commit() - conn.close() + @commands.has_permissions(manage_channels=True) + @commands.command(name=commands_names["birthdays"]["up chat"]) + async def update_chat_birthday(self, ctx, chat: discord.TextChannel): + server_id = ctx.guild.id + with Database() as db: + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]) + db_server_id = db.fetchone()[0] + db.execute('SELECT info_chat FROM "default".servers_chats WHERE server_id = %s', [db_server_id]) + info_chat_id = db.fetchone() + info_chat_id = info_chat_id[0] if isinstance(info_chat_id, tuple) else info_chat_id + if info_chat_id is None: + add = commands_names["birthdays"]["set chat"] + return await ctx.send(choice([ + f"Зайка, мне нечего обновлять, используй **.{add}**", f"Солнце, у меня нет информации, что обновлять, используй **.{add}**", + f"Малыш, сначала задай чат командой **.{add}**, а потом уже обновляй данные" + ])) + if info_chat_id[0] == chat.id: + return await ctx.send(choice([ + "Зайка, я сохранил точно такой же канал", "Солнышко, я запомнил такой же чат", "Малыш, в моей базе данных такие же данные" + ])) + db.execute('UPDATE "default".servers_chats SET info_chat = %s WHERE server_id = %s', [chat.id, db_server_id]) await ctx.send(choice([ - f"Внучок, успешно обновил данные с {current_date} на {user_date}", f"Обновил данные с {current_date} на {user_date}", f"Апдейт прошел успешно с {current_date} на {user_date}", + f"Ждите поздравления в {chat.mention} мои хорошие {get(self.bot.emojis, name='wowcry')}", f"Сладкие мои, ждите поздравления в {chat.mention}", + f"Обязательно поздравлю вас в {chat.mention}", f"Поздравлю всех-всех-всех в этом чате {chat.mention} {get(self.bot.emojis, name='wowcry')}" ])) - @update_birthdays.error - async def update_birthday_error(self, ctx, error): + @update_chat_birthday.error + async def update_chat_birthday_error(self, ctx, error): if isinstance(error, commands.MissingRequiredArgument): return await ctx.send(choice([ - "Молодой ты указал дату неполностью", "Внучок неполные данные", "Данные не полны ©️ Магистр Йода" + "Молодой ты не указал чат", "Внучок неполные данные", "Данные не полны ©️ Магистр Йода" ])) if isinstance(error, commands.BadArgument): await ctx.send(choice([ - "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, все три параметра числа", "Внучок проверь ввод, все три аргемента числа" - ])) - - @commands.command(name=commands_names["birthdays"]["delete"]) - async def delete_birthdays(self, ctx): - user_id = ctx.message.author.id - guild_id = ctx.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server[0] if isinstance(check_server, tuple) else check_server - db_user_id = check_user[0] if isinstance(check_user, tuple) else check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) - check_date = cursor.execute("SELECT date FROM birthdays WHERE id = ?", (db_user_id,)).fetchone() - check_date = check_date[0] if isinstance(check_date, tuple) else check_date - if not check_date: - conn.commit() - conn.close() - return await ctx.send(choice([ - f"Внучок, твоей даты еще нет, юзай **{commands_names['birthdays']['add']}**", f"Молодой, тебя еще нет в базе, юзай **{commands_names['birthdays']['add']}**", - f"Старичок, ты еще не вносил данные, юзай **{commands_names['birthdays']['add']}**", - f"Внучок, по тебе еще нет есть инфы, удалять нечего {discord.utils.get(self.bot.emojis, name='ahuet')}" + "Внучок, не балуйся, неверный формат ввода", "Ошибочные аргументы", "Плохой ввод сынок, аргументом должен быть текстовый канал", + "Внучок проверь ввод, это должен быть текстовый чат" ])) - cursor.execute("DELETE FROM birthdays WHERE id = ?", (db_user_id,)) - conn.commit() - conn.close() + @commands.has_permissions(manage_channels=True) + @commands.command(name=commands_names["birthdays"]["del chat"]) + async def delete_chat_birthday(self, ctx): + server_id = ctx.guild.id + with Database() as db: + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]) + db_server_id = db.fetchone()[0] + db.execute('SELECT info_chat FROM "default".servers_chats WHERE server_id = %s', [db_server_id]) + info_chat_id = db.fetchone() + info_chat_id = info_chat_id[0] if isinstance(info_chat_id, tuple) else info_chat_id + if info_chat_id is None: + add = commands_names["birthdays"]["set chat"] + return await ctx.send(choice([ + f"Зайка, мне нечего удалять, используй **.{add}**", f"Солнце, у меня нет информации, что удалять, используй **.{add}**", + f"Малыш, сначала задай чат командой **.{add}**, а потом уже удаляй данные" + ])) + db.execute('UPDATE "default".servers_chats SET info_chat = null WHERE server_id = %s', [db_server_id]) await ctx.send(choice([ - "Все сынок, удалил тебя из базы", "Успешно удалил данные", "Ты не хочешь чтобы я тебя поздравлял?((", "" + "Ладно зайки, если вы не хотите, чтобы я вас поздравлял, то так мне и надо :sob: :sob: :sob:", "Вы не хотите, чтобы я вас поздравлял??? :sob: :sob: :sob:", + "Вам не нужны мои поздравления :cry:", ":cry: Видите эти слезы? Вы довольны?" ])) - @commands.command(name=commands_names["birthdays"]["show bd"]) - async def show_birthday(self, ctx): - user_id = ctx.message.author.id - user = ctx.message.author - guild_id = ctx.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server[0] if isinstance(check_server, tuple) else check_server - db_user_id = check_user[0] if isinstance(check_user, tuple) else check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) - check_date = cursor.execute("SELECT date FROM birthdays WHERE id = ?", (db_user_id,)).fetchone() - check_date = check_date[0] if isinstance(check_date, tuple) else check_date - if not check_date: - conn.commit() - conn.close() - return await ctx.send(choice([ - f"Внучок, твоей даты еще нет, юзай **{commands_names['birthdays']['add']}**", f"Молодой, тебя еще нет в базе, юзай **{commands_names['birthdays']['add']}**", - f"Старичок, ты еще не вносил данные, юзай **{commands_names['birthdays']['add']}**", - f"Внучок, по тебе еще нет инфы, удалять нечего {discord.utils.get(self.bot.emojis, name='ahuet')}" - ])) - - current_date = cursor.execute("SELECT date FROM birthdays WHERE id = ?", (db_user_id,)).fetchone() - current_date = current_date[0] if isinstance(current_date, tuple) else current_date - embed = discord.Embed( - title=f"{user.name} - Date of Birth", - description=f"""{choice([ - f"Этот сладенький пупсик {user.mention} родился", f"Эта лапопуличка {user.mention} родилась", f"Этот экстра зайчик {user.mention} родился", - f"Эта сладенькая зайка {user.mention} родилась", f"Эта муська {user.mention} родилась" - ])} **{current_date}**""", - colour=discord.Colour.purple() - ) - embed.set_thumbnail(url=user.avatar_url) - conn.commit() - conn.close() - await ctx.send(embed=embed) - - @commands.command(name=commands_names["birthdays"]["show bds"]) - async def show_birthdays(self, ctx): - user_id = ctx.message.author.id - user = ctx.message.author - guild_id = ctx.guild.id - guild = ctx.guild - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - db_server_id = check_server[0] if isinstance(check_server, tuple) else check_server - db_user_id = check_user[0] if isinstance(check_user, tuple) else check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id, user_id) VALUES(?,?)", (db_server_id, db_user_id)) - - check_users = cursor.execute("SELECT user_id FROM connect WHERE server_id = (SELECT id FROM server WHERE server_id = ?)", (guild_id,)).fetchall() - db_users_id = [i[0] for i in check_users] - dates = cursor.execute(f"SELECT id,date FROM birthdays WHERE id IN ({','.join(['?'] * len(db_users_id))})", db_users_id).fetchall() - db_user_id = [i[0] for i in dates] - users = cursor.execute(f"SELECT user_id FROM user WHERE id IN ({','.join(['?'] * len(db_user_id))})", db_user_id).fetchall() - dates = [i[1] for i in dates] - users = [i[0] for i in users] - conn.commit() - conn.close() - output = [] - for i in range(len(dates)): - output.append(f"**{discord.utils.get(guild.members, id=users[i]).mention}** - **{dates[i]}**") - if not output: - output = "Пока данных нет" - embed = discord.Embed( - title=f"{guild.name} Dates of Birth", - description="\n".join(output) if isinstance(output, list) else output, - colour=discord.Colour.purple() - ) - embed.set_thumbnail(url=guild.icon_url) - await ctx.send(embed=embed) + @commands.command(name=commands_names["birthdays"]["show chat"]) + async def show_chat_birthdays(self, ctx): + server_id = ctx.guild.id + with Database() as db: + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]) + db_server_id = db.fetchone()[0] + db.execute('SELECT info_chat FROM "default".servers_chats WHERE server_id = %s', [db_server_id]) + info_chat_id = db.fetchone() + info_chat_id = info_chat_id[0] if isinstance(info_chat_id, tuple) else info_chat_id + if info_chat_id is None: + add = commands_names["birthdays"]["set chat"] + return await ctx.send(choice([ + f"Зайка, я не знаю куда отправлять поздравления, используй **.{add}**", f"Солнце, у меня нет информации, используй **.{add}**", + f"Малыш, сначала задай чат командой **.{add}**" + ])) + info_chat = self.bot.get_channel(info_chat_id).mention + await ctx.send(choice([ + f"Мои хорошие, вы сможете увидеть мои поздравления в {info_chat}", f"Ждите своих дней рождений и получайте самые лучшие слова от меня в {info_chat}" + ])) diff --git a/main/cogs/chatting.py b/main/cogs/chatting.py index 6766aaf..cb6090d 100644 --- a/main/cogs/chatting.py +++ b/main/cogs/chatting.py @@ -27,7 +27,6 @@ def get_emoji(self, name): @staticmethod def embed_cat(author): - """Return the embed with random kitty""" response = requests.get("https://aws.random.cat/meow") data = response.json() embed = discord.Embed( @@ -148,7 +147,7 @@ async def my_help(self, ctx): ) embed.add_field( name="Система уровней :bar_chart:", - value=f"""Модуль, созданный для создания классового неравенста на сервере, повышения активности в чатах и конкурентности. + value=f"""Модуль, созданный для создания классового неравенства на сервере, повышения активности в чатах и конкурентности. **{prefix}{commands_names["level"]["help"]}** - поможет тебе понять как устроен модуль LevelSystem. **{prefix}{commands_names["level"]["add"]} ** - внос в базу данных с указанием роли и количество опыта для ее получения. **{prefix}{commands_names["level"]["up"]} ** - обновит данную роль. @@ -174,25 +173,6 @@ async def my_help(self, ctx): ) await ctx.send(embed=embed) - @commands.Cog.listener() - async def on_command_error(self, ctx, error): - if isinstance(error, commands.CommandNotFound): - await ctx.send(choice([ - "Внучок, я таких команд не знаю", "Боевой это чо за команда", "*\Бип\* - *\Боп\* неизвестная мне команда" - ])) - if error: - raise error - - @commands.Cog.listener() - async def on_member_join(self, member): - channel = self.bot.get_channel(783742051556655174) # участники - await channel.send(member.mention, "присоединлся к серверу") - - @commands.Cog.listener() - async def on_member_remove(self, member): - channel = self.bot.get_channel(783742051556655174) # участники - await channel.send(member.mention, "покинул сервер") - @commands.Cog.listener() async def on_message(self, message): """Reacting on messages and send request by phrase""" @@ -272,7 +252,7 @@ async def test(self, ctx): async def greet(self, ctx): embed = discord.Embed( title="ДОБРО ПОЖАЛОВАТЬ В INVINCIBLE WARRIORS", - description=f"**Wassup samurai!** Приветствуем тебя на великолемном сервере {self.get_emoji('bulka')}\nЗдесь ты найдешь все что необходимо: **друзей, общение и голые сиськи** " + description=f"**Wassup samurai!** Приветствуем тебя на великолепном сервере {self.get_emoji('bulka')}\nЗдесь ты найдешь все что необходимо: **друзей, общение и голые сиськи** " f"{self.get_emoji('giggle')}\n\nПо всем интересующим тебя вопросам ты можешь обращаться к {discord.utils.get(ctx.guild.roles, name='SHOGUNS').mention} или к пожилому " f"{self.bot.get_user(414105456907386886).mention}\n\nОбщая информация сервера:", colour=discord.Colour.purple() diff --git a/main/cogs/commands.py b/main/cogs/commands.py index 5ae04b4..dd38aeb 100644 --- a/main/cogs/commands.py +++ b/main/cogs/commands.py @@ -55,8 +55,8 @@ "level": { "help": "level_help", "add": "level_add", - "up": "level_update", - "delete": "level_delete", + "up": "level_up", + "delete": "level_del", "show levels": "level_show", "show level": "level", "dashboard": "level_dashboard" diff --git a/main/cogs/database_connector.py b/main/cogs/database_connector.py new file mode 100644 index 0000000..31a199a --- /dev/null +++ b/main/cogs/database_connector.py @@ -0,0 +1,46 @@ +import psycopg2 +from cogs.config import * + + +class Database: + def __init__(self): + name, user, password, port = database_config + self._conn = psycopg2.connect( + dbname=name, + user=user, + password=password, + port=port + ) + self._cursor = self._conn.cursor() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + @property + def connection(self): + return self._conn + + @property + def cursor(self): + return self._cursor + + def commit(self): + self.connection.commit() + + def close(self, commit=True): + if commit: + self.commit() + self.connection.close() + + def execute(self, sql, params=None): + self.cursor.execute(sql, params or ()) + return self.cursor + + def fetchall(self): + return self.cursor.fetchall() + + def fetchone(self): + return self.cursor.fetchone() diff --git a/main/cogs/level_system.py b/main/cogs/level_system.py index 72e4ca2..2b7a234 100644 --- a/main/cogs/level_system.py +++ b/main/cogs/level_system.py @@ -1,89 +1,16 @@ import discord +from discord.utils import get from discord.ext import commands from cogs.commands import commands_names -from cogs.config import prefix -import sqlite3 +from cogs.config import * +from cogs.database_connector import Database from random import choice -import os - - -""" -------------------------------------------------------------------------------SOON I WILL SWITCH FROM SQLITE TO POSTGRESQL------------------------------------------------------------------------------ -""" class LevelSystem(commands.Cog): def __init__(self, bot): self.bot = bot - @commands.Cog.listener("on_message") - async def check_level_up(self, message): - if message.author.bot: - return - user_id = message.author.id - user = message.author - guild_id = message.guild.id - guild = message.guild - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - check_user_id = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user_id = check_user_id[0] if isinstance(check_user_id, tuple) else check_user_id - if not check_user_id: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - db_user_id = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - db_user_id = db_user_id[0] if isinstance(db_user_id, tuple) else db_user_id - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - db_server_id = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - db_server_id = db_server_id[0] if isinstance(db_server_id, tuple) else db_server_id - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id,user_id) VALUES(?,?)", (db_server_id, db_user_id)) - - check_xp = cursor.execute("SELECT xp FROM user_levels WHERE user_id = ? and server_id = ?", (db_user_id, db_server_id)).fetchone() - check_xp = check_xp[0] if isinstance(check_xp, tuple) else check_xp - if not check_xp: - cursor.execute("INSERT INTO user_levels(user_id,server_id,xp) VALUES(?,?,?)", (db_user_id, db_server_id, 0)) - xp = cursor.execute("SELECT xp FROM user_levels WHERE user_id = ? and server_id = ?", (db_user_id, db_server_id)).fetchone() - xp = xp[0] if isinstance(xp, tuple) else xp - cursor.execute("UPDATE user_levels SET xp = ? WHERE user_id = ? and server_id = ?", (xp + 1, db_user_id, db_server_id)) - - check_level = cursor.execute("SELECT level FROM user_levels WHERE user_id = ? and server_id = ?", (db_user_id, db_server_id)).fetchone() - check_level = check_level[0] if isinstance(check_level, tuple) else check_level - if not check_level: - cursor.execute("UPDATE user_levels SET level = ? WHERE user_id = ? and server_id = ?", (0, db_user_id, db_server_id)) - level = cursor.execute("SELECT level FROM user_levels WHERE user_id = ? and server_id = ?", (db_user_id, db_server_id)).fetchone() - level = level[0] if isinstance(level, tuple) else level - - check_levels = cursor.execute("SELECT * FROM levels WHERE id = (SELECT id FROM server WHERE server_id = ?)", (guild_id,)).fetchall() - if not check_levels: - conn.commit() - conn.close() - return - - xp += 1 - levels = cursor.execute("SELECT level_id, level_xp FROM levels WHERE id = ? ORDER BY level_xp DESC", (db_server_id,)).fetchall() - for db_level, db_xp in levels: - if xp < db_xp and discord.utils.get(guild.roles, id=db_level) in user.roles: - cursor.execute("UPDATE user_levels SET level = ?, xp = ? WHERE user_id = ? and server_id = ?", (db_level, db_xp + 1, db_user_id, db_server_id)) - conn.commit() - conn.close() - return - elif xp == db_xp: - cursor.execute("UPDATE user_levels SET level = ? WHERE user_id = ? and server_id = ?", (db_level, db_user_id, db_server_id)) - conn.commit() - conn.close() - mention, current_role = user.mention, discord.utils.get(guild.roles, id=db_level).mention - await user.add_roles(discord.utils.get(guild.roles, id=db_level)) - return await message.channel.send(choice([ - f"Поздравляю {mention}, ты заслужил повышение! Текущий уровень {current_role}", f"Пожилой {mention} повысился до {current_role}, мои поздравления!", - f"Эль негр {mention} апнулся до {current_role}!!! супер пупер пупсик" - ])) - conn.commit() - conn.close() - @commands.command(name=commands_names["level"]["help"]) async def level_help(self, ctx): embed = discord.Embed( @@ -102,163 +29,121 @@ async def level_help(self, ctx): inline=False) await ctx.send(embed=embed) + @commands.Cog.listener("on_message") + async def check_level_up(self, message): + if message.author.bot: + return + user = message.author + user_id = user.id + server = message.guild + server_id = server.id + with Database() as db: + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]).fetchone()[0] + db_user_id = db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]).fetchone()[0] + levels = db.execute('SELECT level_id, level_xp FROM "default".servers_levels WHERE server_id = %s ORDER BY level_xp DESC ', [db_server_id]).fetchall() + if not levels: + return + user_xp = db.execute('SELECT xp FROM "default".users_levels WHERE user_id = %s and server_id = %s', [db_user_id, db_server_id]).fetchone()[0] + user_xp += 1 + db.execute('UPDATE "default".users_levels SET xp = %s WHERE user_id = %s and server_id = %s', [user_xp, db_user_id, db_server_id]) + for db_level, db_xp in levels: + if user_xp == db_xp: + db.execute('UPDATE "default".users_levels SET level = %s WHERE user_id = %s and server_id = %s', [db_level, db_user_id, db_server_id]) + mention = user.mention + role = get(server.roles, id=db_level) + await user.remove_roles(*[_role for _role in user.roles if _role in [_db_role for _db_role, _xp in levels]]) + await user.add_roles(role) + embed = discord.Embed( + title=":chart_with_upwards_trend: LEVEL UP :chart_with_upwards_trend:", + description=choice([ + f"Сладкий {mention} заслужил повышение!!!", f"Лапочка {mention} стала еще более уважаемой", f"Поздравляю с повышением, {mention}" + ]) + f"\n**level - {role.mention}\nxp - {user_xp}**", + colour=discord.Colour.purple() + ) + embed.set_thumbnail(url=user.avatar_url) + info_chat = db.execute('SELECT info_chat FROM "default".servers_chats WHERE server_id = %s', [db_server_id]).fetchone()[0] + if info_chat: + return await self.bot.get_channel(info_chat).send(embed=embed) + return await message.channel.send(embed=embed) + @commands.command(name=commands_names["level"]["add"]) @commands.has_permissions(manage_roles=True) async def level_add(self, ctx, role: discord.Role, level_xp: int): - role_id = role.id - user_id = ctx.message.author.id - guild_id = role.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - server = server[0] if isinstance(server, tuple) else server - if not server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - server = server[0] if isinstance(server, tuple) else server - database_role = cursor.execute("SELECT id FROM levels WHERE level_id = ?", (role_id,)).fetchall() - if database_role: - conn.commit() - conn.close() - return await ctx.send(choice([ - "Старичок, уровень уже есть, юзай команду **.level_update**", "Ебен бобен уже есть такой уровень", "Молодой я тут **добавляю** уровни, а не обновляю" - ])) - cursor.execute("INSERT INTO levels(id,level_id,level_xp) VALUES(?,?,?)", (server[0], role_id, level_xp)) - check_name = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_name = check_name[0] if isinstance(check_name, tuple) else check_name - if not check_name: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_name = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_name = check_name[0] if isinstance(check_name, tuple) else check_name - db_server_id, db_user_id = server, check_name - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id,user_id) VALUES(?,?)", (db_server_id, db_user_id)) - conn.commit() - conn.close() - await ctx.send(choice([ - f"Успешно добавил уровень {role.mention} в базу данных", f"Все внучок, внес {role.mention} в базу данных", f"Записал пожилую роль {role.mention} в базу данных" - ])) - - @level_add.error - async def level_add_error(self, ctx, error): - if isinstance(error, commands.MissingPermissions): + level_id = role.id + server_id = role.guild.id + with Database() as db: + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]).fetchone()[0] + db_level_id = db.execute('SELECT level_id FROM "default".servers_levels WHERE server_id = %s and level_id = %s', [db_server_id, level_id]).fetchone() + db_level_id = db_level_id[0] if isinstance(db_level_id, tuple) else db_level_id + if db_level_id: + return await ctx.send(choice([ + "Сладкий, такой уровень уже есть.", "Лапочка, в базе уже есть такой уровень.", "Малыш, у меня уже есть такой уровень." + ]) + f" Используй **.{commands_names['level']['up']}** чтобы обновить уровень или **.{commands_names['level']['delete']}** чтобы удалить роль") + db.execute('INSERT INTO "default".servers_levels(server_id, level_id, level_xp) VALUES (%s, %s, %s)', [db_server_id, level_id, level_xp]) await ctx.send(choice([ - "Внучок, у тебя нет прав на редактирование уровней", "Братик ты не можешь менять уровни", "Молодой, тебе нельзя менять уровни" - ])) - elif isinstance(error, commands.MissingRequiredArgument): - await ctx.send(choice([ - "Старичок мне нужна роль и количество очков, чтобы ее получить", "пожилой сюда надо роль и количество очков" - ])) - elif isinstance(error, commands.BadArgument): - await ctx.send(choice([ - "Бля ты какую то хуйню мне скормил, я сломался", "Не пихай сюда нихуя кроме ролей и уровня", "Внучок ебать не то в меня пихаешь, смотри **.help**" + f"Успешно добавил уровень {role.mention} в базу данных", f"Сладенький, добавил уровень {role.mention} в базу данных", f"Записал уровень {role.mention} в базу данных" ])) @commands.has_permissions(manage_roles=True) @commands.command(name=commands_names["level"]["up"]) async def level_update(self, ctx, role: discord.Role, level_xp: int): - role_id = role.id - user_id = ctx.message.author.id - guild_id = role.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - server = server[0] if isinstance(server, tuple) else server - if not server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - conn.commit() - conn.close() - return await ctx.send(choice([ - "Пожилой я блять еще даже твой сервер в базу не внес, а ты команды апать просишь", "Дедуля блять я еще про этот сервер нихуя не знаю, не то что про команды" - ])) - database_role = cursor.execute("SELECT id FROM levels WHERE level_id = ?", (role_id,)).fetchone() - database_role = database_role[0] if isinstance(database_role, tuple) else database_role - if not database_role: - conn.commit() - conn.close() - return await ctx.send(choice([ - "Внучок таких ролей еще нет", "Пожилой мне нехуй апдейтить, еще нет такого уровня", "Бля сынок первый раз вижу этот уровень, юзай **.level_add**" - ])) - current_xp = cursor.execute("SELECT level_xp FROM levels WHERE level_id = ?", (role_id,)).fetchone() - current_xp = current_xp[0] if isinstance(current_xp, tuple) else current_xp - if current_xp == level_xp: - conn.commit() - conn.close() - return await ctx.send(choice([ - f"Внучок, у этого уровня {role.mention} и так такое же количество опыта", f"Чтобы получить этот уровень {role.mention} нужно такое же количество опыта, в чем апдейт", - "А в чем апдейт то, данные те же" - ])) - cursor.execute("UPDATE levels SET level_xp = ? WHERE level_id = ?", (level_xp, role_id)) - check_name = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_name = check_name[0] if isinstance(check_name, tuple) else check_name - if not check_name: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_name = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_name = check_name[0] if isinstance(check_name, tuple) else check_name - db_server_id, db_user_id = server, check_name - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id,user_id) VALUES(?,?)", (db_server_id, db_user_id)) - conn.commit() - conn.close() - await ctx.send(choice([ - f"Успешно обновил уровень {role.mention} с **{current_xp} xp** на **{level_xp} xp**", f"Внучок проапдейтил роль {role.mention}", f"Обновил уровень {role.mention} на **{current_xp} xp**" - ])) + level_id = role.id + server_id = role.guild.id + with Database() as db: + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]).fetchone()[0] + db_level_id = db.execute('SELECT level_id FROM "default".servers_levels WHERE server_id = %s and level_id = %s', [db_server_id, level_id]).fetchone() + db_level_id = db_level_id[0] if isinstance(db_level_id, tuple) else db_level_id + if not db_level_id: + return await ctx.send(choice([ + "Малыш, чтобы обновлять роль, ее сначала нужно создать.", "Сладенький, мне нечего обновлять.", "Пупсик, я не знаю такой уровень." + ]) + f" Используй **.{commands_names['level']['add']}**, чтобы создать роль") + db_level_xp = db.execute('SELECT level_xp FROM "default".servers_levels WHERE server_id = %s and level_id = %s', [db_server_id, level_id]).fetchone()[0] + if db_level_xp == level_xp: + return await ctx.send(choice([ + f"Муська, у этого уровня {role.mention} и так такое же количество опыта", f"В базе данных те же значения" + ])) + db.execute('UPDATE "default".servers_levels SET level_xp = %s WHERE server_id = %s and level_id = %s', [level_xp, db_server_id, level_id]) + await ctx.send(choice([ + "Сладенький, успешно обновил уровень.", "Проапдейтил уровень, зайка.", "Обновил данные." + ]) + f" Теперь для получения {role.mention} необходимо **{level_xp}**") @level_update.error - async def level_update_error(self, ctx, error): + @level_add.error + async def level_add_error(self, ctx, error): if isinstance(error, commands.MissingPermissions): await ctx.send(choice([ "Внучок, у тебя нет прав на редактирование уровней", "Братик ты не можешь менять уровни", "Молодой, тебе нельзя менять уровни" ])) elif isinstance(error, commands.MissingRequiredArgument): await ctx.send(choice([ - "Старичок мне нужна роль и количество очков, чтобы ее получить", "пожилой сюда надо роль и количество очков" + "Старичок мне нужна роль и количество опыта, чтобы ее получить", "Малыш сюда надо роль и количество опыта" + ])) + elif isinstance(error, commands.RoleNotFound): + await ctx.send(choice([ + "Сладенький, для уровня нужна роль, а не человек.", "Пупсик, мне нужна роль, а не юзер" ])) elif isinstance(error, commands.BadArgument): await ctx.send(choice([ - "Бля ты какую то хуйню мне скормил, я сломался", "Не пихай сюда нихуя кроме ролей и уровня", "Внучок ебать не то в меня пихаешь, смотри **.help**" + "Неверные типы данных", "Зайка, тебе нужно ввести только роль и количество опыта", "Неверные аргументы" ])) @commands.has_permissions(manage_roles=True) @commands.command(name=commands_names["level"]["delete"]) async def level_delete(self, ctx, role: discord.Role): - role_id = role.id - user_id = ctx.message.author.id - guild_id = role.guild.id - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - server = server[0] if isinstance(server, tuple) else server - if not server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - conn.commit() - conn.close() - return await ctx.send(choice([ - "Пожилой я блять еще даже твой сервер в базу не внес, а ты команды апать просишь", "Дедуля блять я еще про этот сервер нихуя не знаю, не то что про команды" - ])) - database_role = cursor.execute("SELECT id FROM levels WHERE level_id = ?", (role_id,)).fetchone() - check_name = server[0] if isinstance(server, tuple) else server - if not database_role: - conn.commit() - conn.close() - return await ctx.send(choice([ - "Внучок таких ролей еще нет", "Пожилой мне нехуй удалять, еще нет такого уровня", f"Бля сынок первый раз вижу этот уровень, юзай **.{commands_names['level']['add']}**" + level_id = role.id + server_id = role.guild.id + with Database() as db: + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]).fetchone()[0] + db_level_id = db.execute('SELECT level_id FROM "default".servers_levels WHERE server_id = %s and level_id = %s', [db_server_id, level_id]).fetchone() + db_level_id = db_level_id[0] if isinstance(db_level_id, tuple) else db_level_id + if not db_level_id: + return await ctx.send(choice([ + "Малыш, чтобы удалять роль, ее сначала нужно создать.", "Сладенький, мне нечего удалять.", "Пупсик, я не знаю такой уровень." + ]) + f" Используй **.{commands_names['level']['add']}**, чтобы создать роль") + db.execute('DELETE FROM "default".servers_levels WHERE server_id = %s and level_id = %s', [db_server_id, level_id]) + await ctx.send(choice([ + "Успешно удалил уровень", f"Удалил {role.mention} из базы ролей" ])) - cursor.execute("DELETE FROM levels WHERE level_id = ?", (role_id,)) - check_name = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_name = check_name[0] if isinstance(check_name, tuple) else check_name - if not check_name: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_name = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_name = check_name[0] if isinstance(check_name, tuple) else check_name - db_server_id, db_user_id = server, check_name - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id,user_id) VALUES(?,?)", (db_server_id, db_user_id)) - conn.commit() - conn.close() - await ctx.send(choice([ - f"Успешно удалил уровень {role.mention}", f"Внучок удалил роль {role.mention}", f"Удалил уровень {role.mention}" - ])) @level_delete.error async def level_delete_error(self, ctx, error): @@ -268,161 +153,112 @@ async def level_delete_error(self, ctx, error): ])) elif isinstance(error, commands.MissingRequiredArgument): await ctx.send(choice([ - "Старичок мне нужна только роль", "пожилой сюда надо роль пихать" + "Старичок мне нужна роль", "Малыш сюда надо роль пихать" ])) elif isinstance(error, commands.RoleNotFound): await ctx.send(choice([ - "Внучок блять, мне нужна роль а не юзер", "Сынок ты сюда роли пихай, а не пользователей", "Внучок блять это место для ролей, а не для людей" + "Сладенький, для уровня нужна роль, а не человек.", "Пупсик, мне нужна роль, а не юзер" ])) elif isinstance(error, commands.BadArgument): await ctx.send(choice([ - "Бля ты какую то хуйню мне скормил, я сломался", "Не пихай сюда нихуя кроме ролей и уровня", "Внучок ебать не то в меня пихаешь, смотри **.help**" + "Неверные типы данных", "Зайка, тебе нужно ввести только роль", "Неверные аргументы" ])) @commands.command(name=commands_names["level"]["show levels"]) async def level_show(self, ctx): - guild_id = ctx.guild.id - guild = ctx.guild - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - levels = cursor.execute("SELECT level_id, level_xp FROM levels WHERE id = (SELECT id FROM server WHERE server_id = ?) ORDER BY level_xp DESC", (guild_id,)).fetchall() - if not levels: - data = "Информации пока нет(" - else: - data = "Информация об уровнях сервера и количества опыта для их получения" - for line in levels: - role = discord.utils.get(guild.roles, id=line[0]) - xp = line[1] - data += f"\n**{role.mention}** - необходимо для получения: {xp} xp" - data += "\n**1 xp = 1 сообщение**" - embed = discord.Embed( - title="Уровни на сервере", - description=data, - colour=discord.Colour.purple() - ) - await ctx.send(embed=embed) + server = ctx.guild + server_id = server.id + with Database() as db: + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]).fetchone()[0] + levels = db.execute('SELECT level_id, level_xp FROM "default".servers_levels WHERE server_id = %s ORDER BY level_xp DESC', [db_server_id]).fetchall() + if not levels: + data = choice(["Информации пока нет :pensive:", "Информации отсутсвует :pensive:"]) + else: + data = "" + for level, xp in levels: + role = get(server.roles, id=level) + data += f"\n**{role.mention}** - **{xp} xp**" + embed = discord.Embed( + title=choice(["LEVELS!!!", f"Уровни {get(self.bot.emojis, name='peepohappy')}"]) + " - " + server.name, + description=data, + colour=discord.Colour.purple() + ) + embed.set_thumbnail(url=server.icon_url) + embed.set_footer(text="1 xp = 1 сообщение") + await ctx.send(embed=embed) @commands.command(name=commands_names["level"]["show level"]) async def level(self, ctx): - user_id = ctx.message.author.id + server = ctx.guild + server_id = server.id user = ctx.message.author - guild_id = ctx.guild.id - guild = ctx.guild - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - # -----------------------------------Check-info---------------------------------------------- - db_user_id = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - db_user_id = db_user_id[0] if isinstance(db_user_id, tuple) else db_user_id - if not db_user_id: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - conn.commit() - conn.close() - return await ctx.send(choice([ - "Сынок я тебя первый раз вижу, какой лвл", "Внучок о тебе информации в базе данных нет", "Сынок я тебя не знаю, а лвл уж точно" - ])) - db_server_id = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - db_server_id = db_server_id[0] if isinstance(db_server_id, tuple) else db_server_id - if not db_server_id: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - conn.commit() - conn.close() - return await ctx.send(choice([ - "Пожилой я на этом сервере еще не освоился, поэтому о ролях ничего не знаю", "В моей базе данных отсутствует инфа о лвлах этого сервера", "Внучок в душе не ебу какие здесь лвла" - ])) - - level, xp = cursor.execute("SELECT level, xp FROM user_levels WHERE user_id = ? and server_id = ?", (db_user_id, db_server_id)).fetchone() - current_level = discord.utils.get(guild.roles, id=level) - embed = discord.Embed( - title=f"{user.name} - DASHBOARD", - description=f"""{choice(["Пуси цунами от этой зайки!!!", "Он пиздатый, ахуенный, самый первый, не въебенный!", "Супер экстра пупсик!"])} {user.mention} -**level - {current_level.mention if current_level else current_level}** -**xp - {xp}**""", - colour=discord.Colour.purple() - ) - embed.set_thumbnail(url=user.avatar_url) - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id,user_id) VALUES(?,?)", (db_server_id, db_user_id)) - conn.commit() - conn.close() - await ctx.send(embed=embed) + user_id = user.id + with Database() as db: + db_user_id = db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]).fetchone()[0] + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]).fetchone()[0] + db_server_levels = db.execute('SELECT level_id FROM "default".servers_levels WHERE server_id = %s', [db_server_id]).fetchall() + if not db_server_levels: + return await ctx.send(choice([ + f"Малыш, на сервере {server.name} к сожалению пока нет системы уровней", "Зайка, на этом сервере нет системы уровней", "Система уровней отсутствует" + ])) + db_user_level, db_user_xp = db.execute('SELECT level, xp FROM "default".users_levels WHERE server_id = %s and user_id = %s', [db_server_id, db_user_id]).fetchone() + print(db_user_level, get(server.roles, id=db_user_level), server.roles) + db_user_level = 'отсутствует' if not get(server.roles, id=db_user_level) else get(server.roles, id=db_user_level).mention + embed = discord.Embed( + title=user.name + " - LEVEL", + description=f"""{choice(["Пуси цунами от этой зайки!!!", "Супер экстра пупсик!"])} {user.mention} + **level - {db_user_level}** + **xp - {db_user_xp}**""", + colour=discord.Colour.purple() + ) + embed.set_thumbnail(url=user.avatar_url) + await ctx.send(embed=embed) @commands.command(name=commands_names["level"]["dashboard"]) - async def level_dashboard(self, ctx): - user_id = ctx.message.author.id - user = ctx.message.author - guild_id = ctx.guild.id - guild = ctx.guild - conn = sqlite3.connect(os.path.abspath("database/samurai.db")) - cursor = conn.cursor() - - # -----------------------------------Check-info---------------------------------------------- - check_user = cursor.execute("SELECT id FROM user WHERE user_id = ?", (user_id,)).fetchone() - check_user = check_user[0] if isinstance(check_user, tuple) else check_user - if not check_user: - cursor.execute("INSERT INTO user(user_id) VALUES(?)", (user_id,)) - check_server = cursor.execute("SELECT id FROM server WHERE server_id = ?", (guild_id,)).fetchone() - check_server = check_server[0] if isinstance(check_server, tuple) else check_server - if not check_server: - cursor.execute("INSERT INTO server(server_id) VALUES(?)", (guild_id,)) - conn.commit() - conn.close() - return await ctx.send(choice([ - "Сынок у меня пока вообще нет инфы об этом сервере", "Внучок я пока об этом сервере ничего не знаю", "Пожилой, пока ноль инфы об этом сервере" - ])) - check_users = cursor.execute("SELECT user_id FROM connect WHERE server_id = (SELECT id FROM server WHERE server_id = ?)", (guild_id,)).fetchall() - if not check_users: - conn.commit() - conn.close() - return await ctx.send(choice([ - "Я пока не получил ни одного сообщения на сервере", "Пока ноль инфы об уровнях участников", "Еще не получил ни одного сообщения", "0 инфы о вас ребятки" - ])) - db_server_id, db_user_id = check_server, check_user - if not cursor.execute("SELECT id FROM connect WHERE server_id = ? and user_id = ?", (db_server_id, db_user_id)).fetchall(): - cursor.execute("INSERT INTO connect(server_id,user_id) VALUES(?,?)", (db_server_id, db_user_id)) - # ------------------------------------------------------------------------------------------- - - check_users = [i[0] for i in check_users] - users = cursor.execute(f"SELECT user_id FROM user WHERE id IN ({','.join(['?'] * len(check_users))})", check_users).fetchall() - levels = cursor.execute(f"SELECT level, xp FROM user_levels WHERE user_id IN ({','.join(['?'] * len(check_users))}) and server_id = ?", check_users + [db_server_id]).fetchall() - info = [] - for i in range(len(check_users)): - info.append(users[i] + levels[i]) - info.sort(key=lambda x: x[2], reverse=True) - embed = discord.Embed( - title=f"{guild.name} DASHBOARD", - colour=discord.Colour.purple() - ) - embed.set_thumbnail(url=guild.icon_url) - phrases_three_winners = { - 1: choice(['Мега распиздатый на первом месте!', 'Первый на первом!', 'Секси юзер на первом месте!']), - 2: choice(['Сладкий пупсик на втором!', 'Второй, потому что дал фору!', 'Мега негр на втором!']), - 3: choice(['Мега зайка на третьем!', 'Сладкий малипуська на третьем!', 'Третий, потому что 1 и 2 слишком мало!']) - } - for i in range(min(3, len(info))): - cur_user = discord.utils.get(guild.members, id=info[i][0]) - cur_level = discord.utils.get(guild.roles, id=info[i][1]) - cur_xp = info[i][2] - embed.add_field( - name=f"{i + 1}. {cur_user.name}", - value=f"{phrases_three_winners[i + 1]} {cur_user.mention}\n**level: {str(cur_level) if not cur_level else cur_level.mention}\nxp: {cur_xp}**", - inline=False + async def level_dashboard(self, ctx, limit=10): + server = ctx.guild + server_id = server.id + with Database() as db: + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]).fetchone()[0] + db_server_levels = db.execute('SELECT level_id FROM "default".servers_levels WHERE server_id = %s', [db_server_id]).fetchall() + if not db_server_levels: + return await ctx.send(choice([ + f"Малыш, на сервере {server.name} к сожалению пока нет системы уровней", "Зайка, на этом сервере нет системы уровней", "Система уровней отсутствует" + ])) + embed = discord.Embed( + title=server.name + " - DASHBOARD", + colour=discord.Colour.purple() ) - info = info[min(3, len(info)):] - output = [] - for num, line in enumerate(info): - cur_user = discord.utils.get(guild.members, id=line[0]) - cur_level = discord.utils.get(guild.roles, id=line[1]) - cur_xp = line[2] - output.append(f"**{num + 4}.{cur_user.mention if cur_user else str(cur_user)}** - {' '.join([str(cur_level) if not cur_level else cur_level.mention, f'**{cur_xp} xp**'])}") - if output: - embed.add_field( - name=choice([ - "Не менее крутые пупсики", "Не менее сладкие зайки", "Сопоставимые по пиздатости", "А также не менее мощные мужчины" + embed.set_thumbnail(url=server.icon_url) + top_three_phrases = { + 1: choice(['Мега классный на первом месте!', 'Первый на первом!', 'Секси юзер на первом месте!']), + 2: choice(['Сладкий пупсик на втором!', 'Второй, потому что дал фору!', 'Мега солнышко на втором!']), + 3: choice(['Мега зайка на третьем!', 'Сладкий малипуська на третьем!', 'Третий, потому что 1 и 2 слишком мало!']) + } + db.execute('SELECT user_id, level, xp FROM "default".users_levels WHERE server_id = %s ORDER BY xp DESC LIMIT %s', [db_server_id, limit]) + data = [] + for db_user_id, db_user_level, db_user_xp in db.fetchall(): + user_id = db.execute('SELECT discord_user_id FROM "default".users WHERE id = %s', [db_user_id]).fetchone()[0] + user = get(server.members, id=user_id) + level = 'отсутствует' if not get(server.roles, id=db_user_level) else get(server.roles, id=db_user_level).mention + data.append([user, level, db_user_xp]) + for i in range(min(3, len(data))): + user, level, xp = data[i] + embed.add_field( + name=f"{i + 1}. {user.name}", + value=f"{top_three_phrases[i + 1]} {user.mention}\n**level: {level}\nxp: {xp}**", + inline=False + ) + data = data[min(3, len(data)):] + output = [] + for pos, line in enumerate(data): + user, level, xp = line + output.append(f"**{pos + 4}.{user.mention}** - {' '.join([level, f'**{xp} xp**'])}") + if output: + embed.add_field(name=choice([ + "Не менее крутые пупсики", "Не менее сладкие зайки", "Сопоставимые по крутости", "А также не менее мощные мужчины" ]), - value="\n".join(output), - inline=False - ) - conn.commit() - conn.close() - await ctx.send(embed=embed) + value="\n".join(output), + inline=False + ) + await ctx.send(embed=embed) diff --git a/main/cogs/mini_cogs.py b/main/cogs/mini_cogs.py index 3b5b2bb..b6ebba3 100644 --- a/main/cogs/mini_cogs.py +++ b/main/cogs/mini_cogs.py @@ -68,7 +68,7 @@ async def get_quote(self, ctx): async def get_forecast_error(self, ctx, error): if isinstance(error, commands.MissingRequiredArgument): await ctx.send(choice([ - "Молодой я ебу в каком городе ты хочешь узнать прогоз", "Внучок а место то какое", "Сынок а где город то" + "Молодой я ебу в каком городе ты хочешь узнать прогноз", "Внучок а место то какое", "Сынок а где город то" ])) if isinstance(error, commands.BadArgument): await ctx.send("Вы неверно указали место") diff --git a/main/cogs/on_events_checker.py b/main/cogs/on_events_checker.py new file mode 100644 index 0000000..24d38bd --- /dev/null +++ b/main/cogs/on_events_checker.py @@ -0,0 +1,160 @@ +import discord +from discord.utils import get +from discord.ext import commands +from cogs.database_connector import Database +from random import choice + + +class OnEventsChecker(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_command_error(self, ctx, error): + if isinstance(error, commands.CommandNotFound): + await ctx.send(choice([ + "Внучок, я таких команд не знаю", "Боевой, это чо за команда", "*\Бип\* - *\Боп\* неизвестная мне команда" + ])) + elif isinstance(error, commands.MissingPermissions): + await ctx.send(choice([ + "Малыш, у меня нет прав на выполнение этого действия.", "Сладкий, я не могу это сделать, потому что у меня нет прав." + ])) + " Попробуй добавить мне роль с правами администратора" + elif error: + raise error + + @commands.Cog.listener() + async def on_member_update(self, before, after): + if before.roles != after.roles: + with Database() as db: + db_user_id = db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [after.id]).fetchone()[0] + db_server_id = db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [after.guild.id]).fetchone()[0] + db_levels = db.execute('SELECT level_id, level_xp FROM "default".servers_levels WHERE server_id = %s', [db_server_id]).fetchall() + if not db_levels: + return + before_roles, after_roles = set(before.roles), set(after.roles) + role = before_roles.symmetric_difference(after_roles).pop() + if role.id not in [level for level, xp in db_levels]: + return + user_level, user_xp = db.execute('SELECT level, xp FROM "default".users_levels WHERE server_id = %s and user_id = %s', [db_server_id, db_user_id]).fetchone() + db_xp = db.execute('SELECT level_xp FROM "default".servers_levels WHERE server_id = %s and level_id = %s', [db_server_id, role.id]).fetchone()[0] + info_chat = self.bot.get_channel(db.execute('SELECT info_chat FROM "default".servers_chats WHERE server_id = %s', [db_server_id]).fetchone()[0]) + if not info_chat: + return + if db_xp > user_xp: + async for event in before.guild.audit_logs(limit=1, action=discord.AuditLogAction.member_role_update): + if event.user.bot: + return + if event.target.id != before.id: + continue + levels_to_remove = [] + for _level, _xp in db_levels: + _level = get(after.guild.roles, id=_level) + if _level in after.roles and _level != role: + levels_to_remove.append(_level) + db.execute('UPDATE "default".users_levels SET level = %s, xp = %s WHERE server_id = %s and user_id = %s', [role.id, db_xp, db_server_id, db_user_id]) + embed = discord.Embed( + title=":chart_with_upwards_trend: LEVEL UP :chart_with_upwards_trend:", + description=f"{after.mention} был повышен модератором {event.user.mention} до уровня {role.mention}. Мои поздравления!\n**level - {role.mention}\nxp - {db_xp}**", + colour=discord.Colour.purple() + ) + embed.set_thumbnail(url=after.avatar_url) + await after.remove_roles(*levels_to_remove) + return await info_chat.send(embed=embed) + elif db_xp <= user_xp: + async for event in before.guild.audit_logs(limit=1, action=discord.AuditLogAction.member_role_update): + if event.user.bot: + return + if event.target.id != before.id: + continue + db.execute('UPDATE "default".users_levels SET level = %s, xp = %s WHERE server_id = %s and user_id = %s', [0, 0, db_server_id, db_user_id]) + embed = discord.Embed( + title=":chart_with_downwards_trend: LEVEL DOWN :chart_with_downwards_trend:", + description=f"{after.mention} был понижен модератором {event.user.mention}.\n**level - 0\nxp - 0**", + colour=discord.Colour.purple() + ) + embed.set_thumbnail(url=after.avatar_url) + return await info_chat.send(embed=embed) + + async def leave_join_message(self, member, message): + with Database() as db: + server_id = member.guild.id + db.execute('SELECT join_leave_chat FROM "default".servers_chats WHERE server_id = (SELECT id FROM "default".servers WHERE discord_server_id = %s)', [server_id]) + chat_id = db.fetchone() + if chat_id: + channel = self.bot.get_channel(chat_id[0]) + if channel: + await channel.send(member.mention, message) + + @commands.Cog.listener() + async def on_member_join(self, member): + if member == self.bot.user: + return + with Database() as db: + user_id = member.id + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + if db.fetchone() is None: + db.execute('INSERT INTO "default".users(discord_user_id) VALUES(%s) RETURNING id', [user_id]) + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + server_id = member.guild.id + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]) + db_server_id = db.fetchone()[0] + db.execute('SELECT id FROM "default".connect WHERE server_id = %s and user_id = %s', [db_server_id, db_user_id]) + if db.fetchone() is None: + db.execute('INSERT INTO "default".connect(server_id, user_id) VALUES(%s, %s)', [db_server_id, db_user_id]) + db.execute('SELECT level FROM "default".users_levels WHERE user_id = %s and server_id = %s', [db_user_id, db_server_id]) + if db.fetchone() is None: + db.execute('INSERT INTO "default".users_levels(server_id, user_id, level, xp) VALUES (%s, %s, %s, %s)', [db_server_id, db_user_id, 0, 0]) + await self.leave_join_message(member, "присоединился к серверу") + + @commands.Cog.listener() + async def on_member_remove(self, member): + if member == self.bot.user: + return + with Database() as db: + user_id = member.id + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + server_id = member.guild.id + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]) + db_server_id = db.fetchone()[0] + db.execute('DELETE FROM "default".connect WHERE server_id = %s and user_id = %s', [db_server_id, db_user_id]) + db.execute('DELETE FROM "default".users_levels WHERE user_id = %s and server_id = %s', [db_user_id, db_server_id]) + await self.leave_join_message(member, "покинул сервер") + + @commands.Cog.listener() + async def on_guild_join(self, guild): + members = guild.members + members_ids = [member.id for member in members] + server_id = guild.id + with Database() as db: + db.execute('INSERT INTO "default".servers(discord_server_id) VALUES(%s) RETURNING id', [server_id]) + db_server_id = db.fetchone()[0] + for user_id in members_ids: + if user_id == self.bot.user.id: + continue + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + if db.fetchone() is None: + db.execute('INSERT INTO "default".users(discord_user_id) VALUES(%s) RETURNING id', [user_id]) + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + db.execute('INSERT INTO "default".connect(server_id, user_id) VALUES(%s, %s)', [db_server_id, db_user_id]) + db.execute('INSERT INTO "default".users_levels(server_id, user_id, level, xp) VALUES (%s, %s, %s, %s)', [db_server_id, db_user_id, 0, 0]) + db.execute('INSERT INTO "default".servers_chats(server_id) VALUES (%s)', [db_server_id]) + + @commands.Cog.listener() + async def on_guild_remove(self, guild): + members = guild.members + members_ids = [member.id for member in members] + server_id = guild.id + with Database() as db: + db.execute('SELECT id FROM "default".servers WHERE discord_server_id = %s', [server_id]) + db_server_id = db.fetchone()[0] + for user_id in members_ids: + db.execute('SELECT id FROM "default".users WHERE discord_user_id = %s', [user_id]) + db_user_id = db.fetchone()[0] + db.execute('DELETE FROM "default".connect WHERE server_id = %s and user_id = %s', [db_server_id, db_user_id]) + db.execute('DELETE FROM "default".users_levels WHERE server_id = %s and user_id = %s', [db_server_id, db_user_id]) + db.execute('DELETE FROM "default".servers_levels WHERE server_id = %s', [db_server_id]) + db.execute('DELETE FROM "default".servers_chats WHERE server_id = %s', [db_server_id]) + db.execute('DELETE FROM "default".servers WHERE id = %s', [db_server_id]) diff --git a/main/cogs/translator.py b/main/cogs/translator.py index f34aa3d..dad0170 100644 --- a/main/cogs/translator.py +++ b/main/cogs/translator.py @@ -129,8 +129,15 @@ async def translator_help(self, ctx): ) embed.add_field( name="Команды", - value=f"""{prefix}{commands_names}""" + value=f"""Я смогу перевести все что ты захочешь с любого на любой язык, определить язык тоже не проблема. Также можем поиграть в игру угадай язык. +**{prefix}{commands_names["translator"]["help"]}** - помогу тебе отдельным эмбедом со всеми командами +**{prefix}{commands_names["translator"]["list of languages"]}** - в следующих командах и играх ты должен будешь вводить язык и для упрощения я ввел систему кратких обозначений, эта команда выведет их +**{prefix}{commands_names["translator"]["translate"]} ** - переведу фразу с исходного языка на итоговый +**{prefix}{commands_names["translator"]["translate"]} ** - сам определю исходный язык и переведу тебе все на указанный язык (иногда могу ошибаться, тогда юзай команду выше) +**{prefix}{commands_names["translator"]["detect language"]} ** - выведу тебе язык исходного сообщения +**{prefix}{commands_names["translator"]["game detect languages"]}** - начну игру "угадай язык" и буду ждать твоего ответа""" ) + await ctx.send(embed=embed) @commands.command(name=commands_names["translator"]["translate"]) async def translate(self, ctx, *words): @@ -146,7 +153,7 @@ async def translate(self, ctx, *words): ])) if len(words) == 1: return await ctx.send(choice([ - "Ахуенное слово, жалко не ебу на какой язык переводить", "Бля слово топ, только куда сука переводить", "Сука на какой язык то переводить твое слово ебучее" + "Ахуенное слово, жалко не ебу на какой язык переводить", "Бля слово топ, только куда сука переводить", "Сука на какой язык то переводить твоё слово ебучее" ])) if len(words) == 2 and all(i in self.google_translator_keys for i in words): return await ctx.send(choice([ diff --git a/main/cogs/wiki_pedia.py b/main/cogs/wiki_pedia.py index 0344177..63d3d44 100644 --- a/main/cogs/wiki_pedia.py +++ b/main/cogs/wiki_pedia.py @@ -9,11 +9,21 @@ class Wikipedia(commands.Cog): def __init__(self, bot): self.bot = bot + self.site = "https://samuraibot.brizy.site/" wikipedia.set_lang("ru") @commands.command(name="wp") async def wikipedia_search(self, ctx, *message): try: + if message[0] == "<@!825433682205606000>": + embed = discord.Embed( + description=choice([ + f"Внучок, не льсти мне, про меня еще нет википедии, зато ты можешь рассказать обо мне своим друзьям, вот [сайтик про меня]({self.site})", + f"Зайка не льсти мне, про меня еще нет вики, зато ты можешь посмотреть на [сайт про меня]({self.site}) и рассказать друзьям" + ]), + colour=discord.Colour.purple() + ) + return await ctx.send(embed=embed) await ctx.send(wikipedia.summary(message)) except Exception: await ctx.send(choice([ diff --git a/main/database/samurai_database.png b/main/database/samurai_database.png new file mode 100644 index 0000000000000000000000000000000000000000..0c31abd0f922b85cbc4dbd682b1de851f5b63cb6 GIT binary patch literal 43792 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