From d390d2ebe9504616f02011b61dcfd49ceead70c0 Mon Sep 17 00:00:00 2001 From: hok7z Date: Sun, 6 Nov 2022 10:48:10 +0200 Subject: [PATCH] Some fix database and etc --- .env.dist | 2 + .gitignore | 1 + README.md | 3 +- app.py | 9 +- config/config.py | 17 +- database.py | 87 ++++++ database/__init__.py | 1 - database/database.py | 109 ------- database/models.py | 46 --- filters/avaible_roles.py | 12 +- handlers/channels/channels_handler.py | 2 +- handlers/groups/moderator.py | 413 ++++++++++++-------------- handlers/groups/service.py | 20 +- handlers/groups/user.py | 23 +- handlers/users/user.py | 50 ++-- keyboards/inline/callback_data.py | 2 +- load.py | 6 - media/photo.jpg | Bin 19730 -> 0 bytes utils/__init__.py | 4 +- utils/arguments_parser.py | 81 +++++ utils/update_user_data.py | 27 -- 21 files changed, 437 insertions(+), 478 deletions(-) create mode 100644 database.py delete mode 100644 database/__init__.py delete mode 100644 database/database.py delete mode 100644 database/models.py delete mode 100644 media/photo.jpg create mode 100644 utils/arguments_parser.py delete mode 100644 utils/update_user_data.py diff --git a/.env.dist b/.env.dist index e45e215..d0aeb36 100644 --- a/.env.dist +++ b/.env.dist @@ -1,5 +1,7 @@ bot_token = "" +limit_of_warns = 5 + api_id = "" api_hash = "" diff --git a/.gitignore b/.gitignore index 3bc836f..ddc51ea 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ modules/__pycache__ pyrightconfig.json session.session session.session-journal +venv diff --git a/README.md b/README.md index 3a36d99..b2784fc 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ Logging admin command actions in database. - [ ] Analys file for malware 🔎 - [ ] Paste text to PasteBin or PrivNote 📋 - [ ] Site for group moderator 🌍 -- [ ] Fix database errors ForeignKeys +- [ ] Change PeeWee to SQLAlchemy +- [x] Some fix in database ## Support Every investition helps in maintaining this project and making it better. diff --git a/app.py b/app.py index 8402e54..6c12fce 100755 --- a/app.py +++ b/app.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 import logging from aiogram import executor -from database import models +from database import build + from load import dp, bot import filters @@ -12,7 +13,6 @@ dp.filters_factory.bind(filters.ReplayMessageFilter) import handlers import config - logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) WEBAPP_HOST = '127.0.0.1' @@ -43,9 +43,9 @@ async def on_shutdown(dp): await dp.storage.wait_closed() def main() -> None: - models.build() + build() - if config.use_webhook: + if config.USE_WEBHOOK: executor.start_webhook( dispatcher=dp, webhook_path=WEBHOOK_PATH, @@ -58,7 +58,6 @@ def main() -> None: else: executor.start_polling(dp,skip_updates=True) - if __name__ == '__main__': main() diff --git a/config/config.py b/config/config.py index 5edc18c..792572b 100644 --- a/config/config.py +++ b/config/config.py @@ -4,13 +4,15 @@ from environs import Env env = Env() env.read_env() -use_webhook = True +USE_WEBHOOK = True # bot token token = env.str("bot_token") -group_id = env.str("group_id") -second_group_id = env.str("second_group_id") +group_id = env.int("group_id") +second_group_id = env.int("second_group_id") + +limit_of_warns = 5 # Telegram Application api_id = env.int("api_id") @@ -19,6 +21,7 @@ api_hash = env.str("api_hash") # Virus Total API vt_api = env.str("vt_api") + group_permissions = { "can_send_messages":True, "can_send_media_messages":False, @@ -32,11 +35,5 @@ group_permissions = { db_url = env.str("db_url") -# telegram-bot-api-service telegram_api_server = env.str("telegram_api_server").split(":") -telegram_api_server = { - "ip":telegram_api_server[0], - "port":telegram_api_server[1] -} - -telegram_api_server = f"http://{telegram_api_server['ip']}:{telegram_api_server['port']}" +telegram_api_server = f"http://{telegram_api_server[0]}:{telegram_api_server[1]}" diff --git a/database.py b/database.py new file mode 100644 index 0000000..627e825 --- /dev/null +++ b/database.py @@ -0,0 +1,87 @@ +from peewee import Field, Model, BigIntegerField, CharField, DateField, DateTimeField, ForeignKeyField + +import config +from playhouse.db_url import connect + +from datetime import datetime, date + +from enum import Enum +class MemberRoles(Enum): + OWNER = "owner" + ADMIN = "admin" + HELPER = "helper" + MEMBER = "member" + + +db = connect(config.db_url) + +class Member(Model): + user_id = BigIntegerField() + first_name = CharField() + username = CharField(null=True) + + warns = BigIntegerField(default=0) + + role = CharField(default="member") + + joined = DateField(default=date.today()) + + + @staticmethod + def exists(fieldname, value) -> bool | None: + """Check if data exists in db""" + query = Member.select().where(fieldname == value) + + if (query is None): + return None + + return query.exists() + + @staticmethod + def search(fieldname:Field, value): + if (not Member.exists(fieldname, value)): + return None + + return Member.get(fieldname == value) + + @staticmethod + def report(delete=False): + """If the user exists, returns number reports. Gives the user a warning or retrieves it.""" + count = Member.warns + + if delete:count -= 1 + else:count += 1 + + Member.update(warns = count).execute() + + return count + + class Meta: + db_table = "members" + database = db + +class Restriction(Model): + # TODO: not forget rename all operation to action + action = CharField() + + from_user = ForeignKeyField(Member, lazy_load=True) + to_user = ForeignKeyField(Member, lazy_load=True) + + reason = CharField(null=True) + timestamp = DateTimeField(default=datetime.now().replace(microsecond=0)) + + @staticmethod + def search(to_user=None,id=None): + if (id): + query = Restriction.get(Restriction.id == id) + if (to_user): + query = Restriction.select().where(Restriction.to_user == to_user) + + return query + + class Meta: + db_table = "auditlog" + database = db + +def build() -> None: + db.create_tables([Member,Restriction]) diff --git a/database/__init__.py b/database/__init__.py deleted file mode 100644 index ef3f969..0000000 --- a/database/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .database import Database diff --git a/database/database.py b/database/database.py deleted file mode 100644 index 03031e8..0000000 --- a/database/database.py +++ /dev/null @@ -1,109 +0,0 @@ -from .models import Member,Restriction -from peewee import Field - -class Database: - def check_data_exists(self, fieldname:Field, value) -> bool | None: - """Check if data exists in db""" - query = Member.select().where(fieldname == value) - - if (query is None): - return None - - return query.exists() - - def register_user(self, user_id, first_name, user_name=None, role:str='member') -> bool: - """If the user doesn't exist, returns true. Registers a user in the db.""" - - if self.check_data_exists(Member.user_id,user_id): - return False - - Member.create( - user_id = user_id, - first_name = first_name, - user_name = user_name, - - role = role, - - reports = 0, - ) - - return True - - def search_single_member(self,fieldname:Field,value) -> Member | None: - """If the user is found, returns dataclass. Returns user info.""" - exists = self.check_data_exists(fieldname,value) - - if not (exists): - return None - - user = Member.get(fieldname == value) - - return user - - def create_restriction(self, from_user_id, to_user_id, operation, reason): - from_admin = self.search_single_member(Member.user_id,to_user_id) - to_user = self.search_single_member(Member.user_id,from_user_id) - - if not (from_admin) or not (to_user): - return None - - Restriction.create( - operation = operation, - - from_admin = from_admin, - to_user = to_user, - - reason = reason, - ) - - def search_user_restriction(self, user_id) -> list[Restriction] | None: - user = Member.get(Member.user_id == user_id) - - query = Restriction.select().join(Member,on=Restriction.to_user) - - if (query is None): - return None - - return query.where(Restriction.to_user == user) - - def delete_user(self,user_id) -> bool: - """If the user exists, returns true. Deletes the user from the db.""" - - exists = self.check_data_exists(Member.user_id,user_id) - - if not (exists): - return False - - Member.delete().where(Member.user_id == user_id) - - return True - - def update_member_data(self, user_id, fieldnames:list[Field], newvalues:list) -> bool: - """Update member data.""" - exists = self.check_data_exists(Member.user_id,user_id) - - if (not exists): - return False - - for i in range(len(newvalues)): - query = Member.update({fieldnames[i]:newvalues[i]}).where(Member.user_id == user_id).execute() - if (query is None): - return False - - return True - - def change_reports(self,user_id,delete=False) -> int | None: - """If the user exists, returns number reports. Gives the user a warning or retrieves it.""" - exists = self.check_data_exists(Member.user_id,user_id) - - if not (exists): - return False - - count = Member.get(Member.user_id == user_id).reports - - if delete:count += 1 - else:count -= 1 - - query = Member.update(reports = count).where(Member.user_id == user_id).execute() - - return count diff --git a/database/models.py b/database/models.py deleted file mode 100644 index 100d737..0000000 --- a/database/models.py +++ /dev/null @@ -1,46 +0,0 @@ -from peewee import Model, BigIntegerField, CharField, DateField, DateTimeField, ForeignKeyField - -import config -from playhouse.db_url import connect - -from datetime import datetime, date - -from enum import Enum -class MemberRoles(Enum): - OWNER = "owner" - ADMIN = "admin" - HELPER = "helper" - MEMBER = "member" - - -db = connect(config.db_url) - -class Member(Model): - user_id = BigIntegerField() - first_name = CharField() - user_name = CharField(null=True) - role = CharField() - - join_date = DateField(default=date.today()) - - reports = BigIntegerField() - - class Meta: - db_table = "members" - database = db - -class Restriction(Model): - operation = CharField() - - from_admin = ForeignKeyField(Member,lazy_load=False) - to_user = ForeignKeyField(Member,lazy_load=False) - - reason = CharField(null=True) - date = DateTimeField(default=datetime.now) - - class Meta: - db_table = "restrictions" - database = db - -def build() -> None: - db.create_tables([Member,Restriction]) diff --git a/filters/avaible_roles.py b/filters/avaible_roles.py index 8581eeb..b2ad3be 100644 --- a/filters/avaible_roles.py +++ b/filters/avaible_roles.py @@ -1,10 +1,7 @@ from aiogram import types from aiogram.dispatcher.filters import BoundFilter -from database.database import Member -from database.models import MemberRoles - -from load import database +from database import Member, MemberRoles class AvaibleRolesFilter(BoundFilter): """Filter accessed roles""" @@ -15,7 +12,12 @@ class AvaibleRolesFilter(BoundFilter): self.avaible_roles = available_roles async def check(self,message:types.Message): - member = database.search_single_member(Member.user_id,message.from_user.id) + member = Member.search(Member.user_id,message.from_user.id) + + if (member is None): + return False + + # member = database.search_single_member(Member.user_id,message.from_user.id) if (member.role == "owner"): return True diff --git a/handlers/channels/channels_handler.py b/handlers/channels/channels_handler.py index 787a508..45e554e 100644 --- a/handlers/channels/channels_handler.py +++ b/handlers/channels/channels_handler.py @@ -3,4 +3,4 @@ from load import dp,types # TODO: channel post forward in chat @dp.channel_post_handler() async def channel_handler(message:types.Message): - print(message.text) + pass diff --git a/handlers/groups/moderator.py b/handlers/groups/moderator.py index 95d0d93..d3a6490 100644 --- a/handlers/groups/moderator.py +++ b/handlers/groups/moderator.py @@ -1,248 +1,197 @@ -from load import bot, database, dp, types +from load import bot, dp, types from aiogram.types.chat_permissions import ChatPermissions import config import utils -from database.models import Member -import re +from database import Member, Restriction +from database import MemberRoles -from dataclasses import dataclass +from utils import getCommandArgs, getArgument, checkArg, parse_duration, delete_substring_from_string -from database.models import MemberRoles -def getArgument(arguments:list,index:int=0) -> str | None: - """ Get element from a list.If element not exist return None """ - if not (arguments): - return None - - if (len(arguments) > index): - return arguments[index] - else: - return None +# Filters +# is_admin=True - Check admin permission, if user is admin, continue. +# replied=True - If message is answer, continue. +# accessed_roles - list roles. -@dataclass -class CommandArguments: - user:Member | None - arguments:list - -async def getCommandArgs(message:types.Message) -> CommandArguments: - """ Describe user data and arguments from message """ - - """ - !command (@username/id) reason=None - """ - - arguments_list = message.text.split()[1:] - - is_reply = message.reply_to_message - - member = None - arguments = [] - - if (is_reply): - member = database.search_single_member(Member.user_id,message.reply_to_message) - arguments = arguments_list - else: - first_word = getArgument(arguments_list) - if (first_word): - if (first_word.isdigit()): - member = database.search_single_member(Member.user_id,first_word) - - if (first_word[0] == "@") : - member = database.search_single_member(Member.user_name,first_word) - - arguments = arguments_list[1:] - else: - arguments = arguments_list - - if (member is None) and (first_word): - await message.answer(f"❌ User {first_word} not exist.") - - return CommandArguments(member,arguments) - -def checkArg(message:str) -> bool | None: - """ Check if first argument in ["enable","on","true"] then return true """ - if (not message): - return None - - argument = message.split() - argument = getArgument(message.split(),1) - - if (argument is None): - return None - - on = ['enable','on','true'] - off = ['disable','off','false'] - - if (argument in on): - return True - if (argument in off): - return False - - -def delete_substring_from_string(string:str,substring:str) -> str: - string_list = string.split(substring) - return "".join(string_list).lstrip() - -# Filters: -# is_admin=True - Check admin permission, if user is admin, continue. -# replied=True - If message is answer, continue. -# accessed_roles - list roles. - -@dp.message_handler(commands=["ban"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) +@dp.message_handler(commands=["ban"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN]) async def ban_user(message: types.Message): - """ - !ban (@username/id) reason=None - """ command = await getCommandArgs(message) reason = getArgument(command.arguments) - user = command.user - admin = message.from_user + to_user = command.to_user + from_user = command.from_user # If can't descibe user data - if (user is None): + if (not to_user) or (not from_user): await message.answer(( - "Usage:!ban (@username|id) reason=None.\n" - "Reply to a message or use with a username.") + "Usage: !ban (@username|id) reason=None\n" + "Reply to a message or use with a username") ) return # Ban user and save (bool) - status = await bot.kick_chat_member(chat_id=message.chat.id, user_id=user.user_id, until_date=None) + status = await bot.kick_chat_member(chat_id=message.chat.id, user_id=to_user.user_id, until_date=None) if status: - await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been banned.", - parse_mode="Markdown") - - # Delete user from database - database.delete_user(user.user_id) - + await message.answer(f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has banned [{to_user.first_name}](tg://user?id={to_user.user_id})",parse_mode="Markdown") + + # Open restrict - database.create_restriction(admin.id, user.user_id, "ban", reason) + Restriction.create( + from_user=from_user, + to_user=to_user, + action="Ban user", + reason=reason, + ) - -@dp.message_handler(commands=["unban"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) +@dp.message_handler(commands=["unban"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN]) async def unban_user(message: types.Message): - """ - !unban (@username/id) reason=None - """ command = await getCommandArgs(message) - user = command.user + + to_user = command.to_user + from_user = command.from_user # If can't descibe user data - if (user is None): + if (not to_user) or (not from_user): await message.answer(( - "Usage:!unban (@username|id) reason=None.\n" - "Reply to a message or use with username/id.") + "Usage: !unban (@username|id) reason=None\n" + "Reply to a message or use with username/id") ) return # Unban user and set status (bool) - status = await bot.unban_chat_member(chat_id=message.chat.id, user_id=user.user_id) - - # add user to database - database.register_user(user.user_id, user.first_name) + status = await bot.unban_chat_member(chat_id=message.chat.id, user_id=to_user.user_id) + if status: - await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been unbaned.", - parse_mode="Markdown") - - -@dp.message_handler(commands=["kick"],commands_prefix="!", - available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN]) -async def kick_user(message:types.Message): - """ - !kick (@username/id) reason=None - """ + await message.answer(f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has unbanned [{to_user.first_name}](tg://user?id={to_user.user_id})",parse_mode="Markdown") + Member.create( + user_id = to_user.user_id, + first_name = to_user.first_name, + username = to_user.username + ) + +@dp.message_handler(commands=["info"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN]) +async def info_user(message: types.Message): + command = await getCommandArgs(message) + + to_user = command.to_user + + if (not to_user): + await message.answer(( + "Usage: !info (@username|id)\n" + "Reply to a message or use with username/id") + ) + return + + await message.answer(( + f"[{to_user.first_name}](tg://user?id={to_user.user_id}) ({to_user.role})\n" + f"Warns: {to_user.warns}/{config.limit_of_warns}"), + parse_mode="Markdown" + ) + +@dp.message_handler(commands=["kick"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN]) +async def kick_user(message:types.Message): command = await getCommandArgs(message) arguments = command.arguments - user = command.user - admin = message.from_user + to_user = command.to_user + from_user = command.from_user reason = getArgument(arguments) - if (user is None): + if (not to_user) or (not from_user): await message.answer(( - "Usage:!kick (@username|id) reason=None.\n" - "Reply to a message or use with a username/id.") + "Usage: !kick (@username|id) reason=None\n" + "Reply to a message or use with a username/id") ) return - status1 = await bot.kick_chat_member(chat_id=message.chat.id, user_id=user.user_id, until_date=None) - status2 = await bot.unban_chat_member(chat_id=message.chat.id, user_id=user.user_id) + status1 = await bot.kick_chat_member(chat_id=message.chat.id, user_id=to_user.user_id, until_date=None) + status2 = await bot.unban_chat_member(chat_id=message.chat.id, user_id=to_user.user_id) - if (status1 and status2): - await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been kicked.", - parse_mode="Markdown") + if (not status1 and status2): + await message.answer(f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has kicked [{to_user.first_name}](tg://user?id={to_user.user_id})",parse_mode="Markdown") - database.create_restriction(admin.id,user.user_id,"kick",reason) + + Restriction.create( + from_user=from_user, + to_user=to_user, + action="Kick user", + reason=reason, + ) -@dp.message_handler(commands=["mute"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN]) +@dp.message_handler(commands=["mute"],commands_prefix="!",available_roles=[MemberRoles.ADMIN]) async def mute_user(message:types.Message): - """ - !mute (@username/id) reason=None - """ - command = await getCommandArgs(message) arguments = command.arguments - user = command.user - admin = message.from_user + to_user = command.to_user + from_user = command.from_user - if (user is None): + if (not to_user) or (not from_user): await message.answer(( - "Usage:!mute (@username|id) [duration].\n" - "Reply to a message or use with a username/id.") + "Usage:!mute (@username|id) (duration)\n" + "Reply to a message or use with a username/id") ) return - - duration = re.findall(r"(\d+d|\d+h|\d+m|\d+s)",''.join(arguments)) - duration = " ".join(duration) - reason = delete_substring_from_string(" ".join(arguments),duration) - duration_timedelta = utils.parse_timedelta(duration) - - if not duration: - await message.answer(f"Error: \"{duration}\" — неверный формат времени. Examles: 3ч, 5м, 4h30s.") - return + + duration_string = parse_duration(arguments) + duration = None + reason = None + + if (duration_string): + duration = utils.parse_timedelta(duration_string) + + if (not duration): + await message.answer(f"Error: \"{duration}\" — неверный формат времени. Examles: 3ч, 5м, 4h30s.") + return + + reason = delete_substring_from_string(" ".join(arguments),duration_string) + + if (not duration_string): + duration_string = "forever" + + if (arguments): + reason = " ".join(arguments) permissions = ChatPermissions(can_send_messages=False) status = await bot.restrict_chat_member( chat_id=message.chat.id, - user_id=user.user_id, - until_date=duration_timedelta, + user_id=to_user.user_id, + until_date=duration, permissions=permissions ) - + if status: - await message.answer(f"User **{user.first_name}** has been muted.", - parse_mode="Markdown") + await message.answer(f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has muted [{to_user.first_name}](tg://user?id={to_user.user_id}) for {duration_string}",parse_mode="Markdown") + - database.create_restriction(user.user_id, admin.id, "mute", reason) + Restriction.create( + from_user=from_user, + to_user=to_user, + action="Mute user", + reason=reason, + ) -@dp.message_handler(commands=["umute"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN]) +@dp.message_handler(commands=["unmute"],commands_prefix="!",available_roles=[MemberRoles.ADMIN]) async def umute_user(message: types.Message): - """ - !umute (@username/id) reason=None - """ # Get information command = await getCommandArgs(message) - user = command.user + + to_user = command.to_user + from_user = command.from_user # If can't - if (user is None): + if (not to_user) or (not from_user): await message.answer(( "Usage:!unmute (@username|id) reason=None.\n" "Reply to a message or use with a username/id.") @@ -267,29 +216,23 @@ async def umute_user(message: types.Message): # Restrict user and save status = await bot.restrict_chat_member( chat_id=message.chat.id, - user_id=user.user_id, + user_id=to_user.user_id, permissions=permissions ) if status: - await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been unmuted.", - parse_mode="Markdown") + await message.answer(f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has unmuted [{to_user.first_name}](tg://user?id={to_user.user_id})",parse_mode="Markdown") -@dp.message_handler(commands=["pin"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) +@dp.message_handler(commands=["pin"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN]) async def pin_message(message:types.Message): await bot.pin_chat_message(message.chat.id, message.reply_to_message.message_id) -@dp.message_handler(commands=["readonly","ro"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN]) +@dp.message_handler(commands=["readonly","ro"],commands_prefix="!",available_roles=[MemberRoles.ADMIN]) async def readonly_mode(message:types.Message): - """ - !ro/!readonly (@username/id) - """ check = checkArg(message.text) - if (check is None): - await message.answer("!ro on/off alias:disable,enable,start,stop.") + if (not check): + await message.answer("Usage:!ro on,enable,start/off,disable,off\n") return # Get chat permissions @@ -318,13 +261,12 @@ async def readonly_mode(message:types.Message): await message.answer(f"readonly - {check}") -@dp.message_handler(commands=["media"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) +@dp.message_handler(commands=["media"],commands_prefix="!",available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) async def media_content(message: types.Message): check = checkArg(message.text) - if (check is None): - await message.answer("!media on/off alias:disable,enable,start,stop.") + if (not check): + await message.answer("Usage: !media on,enable,start/off,disable,off") return # Get chat permissions @@ -346,17 +288,16 @@ async def media_content(message: types.Message): status = await bot.set_chat_permissions(chat_id=message.chat.id, permissions=chat_permissions) if status: - await message.answer(f"media - {check}.") + await message.answer(f"media - {check}") -@dp.message_handler(commands=["stickers"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) +@dp.message_handler(commands=["stickers"],commands_prefix="!",available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) async def send_stickers(message: types.Message): # Get arguments check = checkArg(message.text) - if (check is None): - await message.answer("!stickers on/off alias:disable,enable,start,stop") + if (not check): + await message.answer("Usage: !stickers on,enable,start/off,disable,off") return # Get chat permissions @@ -378,38 +319,71 @@ async def send_stickers(message: types.Message): status = await bot.set_chat_permissions(chat_id=message.chat.id, permissions=chat_permissions) if status: - await message.answer(f"stickes - {check}.") + await message.answer(f"stickes - {check}") -@dp.message_handler(commands=["w","warn"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) +@dp.message_handler(commands=["warn","w"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN]) async def warn_user(message: types.Message): # Get information command = await getCommandArgs(message) reason = getArgument(command.arguments) - - user = command.user - admin = message.from_user - if (user is None): + to_user = command.to_user + from_user = command.from_user + + if (not to_user) or (not from_user): await message.answer(( - "Usage:!warn (@username/id) reason=None.\n" - "Reply to a message or use with username/id.") + "Usage: !warn (@username|id) reason=None\n" + "Reply to a message or use with username/id") ) return - # Add warning - database.change_reports(user.user_id, delete=True) + to_user.warns += 1 + to_user.save() - await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has gotten a warning.", - parse_mode="Markdown") + await message.answer(f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has warned [{to_user.first_name}](tg://user?id={to_user.user_id}) ({to_user.warns}/{config.limit_of_warns})",parse_mode="Markdown") - database.create_restriction(user.user_id, admin.id, "warn", reason) + + if (to_user.warns == config.limit_of_warns): + await message.answer(f"[{to_user.first_name}](tg://user?id={to_user.user_id}) has been banned!",parse_mode="Markdown") + await bot.kick_chat_member(chat_id=message.chat.id, user_id=to_user.user_id, until_date=None) + + Restriction.create( + to_user=to_user, + from_user=from_user, + action="Warn user", + reason=reason, + ) -@dp.message_handler(commands=["reload"],commands_prefix="!",available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) +@dp.message_handler(commands=["reload"],commands_prefix="!") async def reload(message:types.Message): - await utils.check_user_data() + from load import tgc + + if (not Member.search(Member.role,"owner")): + Member.create( + user_id = message.from_user.id, + first_name = message.from_user.first_name, + username = message.from_user.username, + role="owner", + ) + # TODO: do this every 1 hours + members = await tgc.members_list(config.group_id) + + for member in members: + user = Member.search(Member.user_id,member["id"]) + + if (not user): + Member.create( + user_id=member["id"], + first_name=member["first_name"], + username=member["username"], + ) + else: + user.first_name = member["first_name"] + user.username = member["username"] + user.save() + group = await bot.get_chat(message.chat.id) group_permissions = dict(group["permissions"]) @@ -417,33 +391,34 @@ async def reload(message:types.Message): for permission in group_permissions.keys(): config.group_permissions[permission] = group_permissions[permission] - await message.answer(f"✅ The synchronization was successful.") + await message.answer("Reloaded!") -@dp.message_handler(commands=["set_role"],commands_prefix="!", - available_roles=[MemberRoles.ADMIN]) +@dp.message_handler(commands=["setrole"],commands_prefix="!",available_roles=[MemberRoles.ADMIN]) async def set_role(message:types.Message): command = await getCommandArgs(message) new_role = getArgument(command.arguments) - user = command.user - admin = database.search_single_member(Member.user_id,message.from_user) + to_user = command.to_user + from_user = command.from_user - if (user is None) or (new_role is None): + if (not to_user) or (not from_user) or (not new_role): await message.answer(( - "!srole (@username|id) role(owner,admin,helper,member).\n" + "!setrole (@username|id) role(owner,admin,helper,member).\n" "Reply to a message or use with username." )) return if not (new_role in [member.value for member in MemberRoles]): - await message.answer(f"Role {new_role} not exists.") + await message.answer(f"Role {new_role} not exists") return - if (admin.user_id == user.user_id): - await message.answer("❌ You can't set role yourself.") + if (from_user.user_id == to_user.user_id): + await message.answer("❌ You can't set role yourself") return + + to_user.role = new_role + to_user.save() - database.update_member_data(user.user_id,[Member.role],[new_role]) - - await message.answer(f"{new_role.capitalize()} role set for [{user.first_name}](tg://user?id={user.user_id}).",parse_mode="Markdown") + await message.answer(f"{new_role.capitalize()} role set for [{to_user.first_name}](tg://user?id={to_user.user_id})", + parse_mode="Markdown") diff --git a/handlers/groups/service.py b/handlers/groups/service.py index b8c7c88..0b89cf1 100644 --- a/handlers/groups/service.py +++ b/handlers/groups/service.py @@ -1,6 +1,5 @@ -from load import dp, database, types -from database.models import Member - +from load import dp, types +from database import Member # TODO: fix it # import utils @@ -21,13 +20,18 @@ async def welcome_message(message:types.Message): # User user = message.from_user - exists = database.check_data_exists(Member.user_id,user.id) - + exists = Member.exists(Member.user_id,user.id) + if (exists): await message.answer("Спасибо что вы с нами.") if not (exists): - database.register_user(user.id,user.first_name,user.username) + Member.create( + user_id = user.id, + first_name = user.first_name, + username = user.username, + ) + # TODO: translate it await message.answer(( f"Привет,{user.first_name}\n" @@ -48,8 +52,6 @@ async def welcome_message(message:types.Message): # for user_message in message.text.lower().split(): # if (y in user_message):await message.delete() -# Joke @dp.message_handler(content_types=types.ContentType.VOICE) async def voice_message(message:types.Message): - photo = types.InputFile(path_or_bytesio="media/photo.jpg") - await message.answer_photo(photo) + pass diff --git a/handlers/groups/user.py b/handlers/groups/user.py index 086d974..e09c488 100644 --- a/handlers/groups/user.py +++ b/handlers/groups/user.py @@ -1,9 +1,7 @@ from load import bot, dp, types import config - -from load import database -from database.models import Member +from database import Member @dp.message_handler(commands=["leave"],chat_type=[types.ChatType.SUPERGROUP]) async def leave_group(message:types.Message): @@ -14,8 +12,9 @@ async def leave_group(message:types.Message): if (len(args) < 1) or not ( ' '.join(args[1:]) == "I UNDERSTAND" ): await message.answer("Для того чтобы покинуть чат вам нужно ввести /leave I UNDERSTANT!") return - - database.delete_user(user.id) + + # TODO: rewrite it + # database.delete_user(user.id) # Ban user and save (bool) status = await bot.kick_chat_member(chat_id=message.chat.id,user_id=user.id,until_date=None) @@ -37,17 +36,15 @@ async def start_command_group(message:types.Message): @dp.message_handler(commands=["bio","me"],chat_type=[types.ChatType.SUPERGROUP]) async def get_information(message: types.Message): - user = database.search_single_member(Member.user_id,message.from_user.id) + user = Member.search(Member.user_id, message.from_user.id) - role_level = config.roles["level"] + if (not user): + await message.answer("Something wrong!") + return - if (user is None): - await message.answer("❌Sorry,you not member group.") - return - await message.answer(( - f"User:[{user.first_name}](tg://user?id={user.user_id})\n" - f"level:{role_level[user.role]}\n"), + f"[{user.first_name}](tg://user?id={user.user_id}) ({user.role})\n" + f"Warns: {user.warns}/{config.limit_of_warns}"), parse_mode="Markdown" ) diff --git a/handlers/users/user.py b/handlers/users/user.py index b022552..5e30b40 100644 --- a/handlers/users/user.py +++ b/handlers/users/user.py @@ -1,5 +1,5 @@ -from load import dp,types,database,bot -from database.models import Member +from load import dp, types, bot +from database import Member, Restriction from aiogram.types import KeyboardButton,ReplyKeyboardMarkup from aiogram.types.reply_keyboard import ReplyKeyboardRemove @@ -19,7 +19,7 @@ from keyboards.inline.callback_data import report_callback @dp.message_handler(commands=["start","help"],chat_type=[types.ChatType.PRIVATE]) async def start_command_private(message:types.Message): await message.answer(( - f"Hello,**{message.from_user.first_name}**!\n" + f"Hi, **{message.from_user.first_name}**!\n" "My commands:\n" "\t\t/help /start - read this message.") ,parse_mode="Markdown",reply_markup=menu @@ -44,28 +44,29 @@ async def about_us(message:types.Message): @dp.message_handler(Text(equals=["Check restrictions"]),state=None) async def check_for_restrict(message:types.Message): - user = message.from_user - restrictions = database.search_user_restriction(user_id=user.id) + user = Member.get(Member.user_id == message.from_user.id) + restrictions = Restriction.search(to_user=user) - if (restrictions is None): + if (not restrictions): await message.answer("✅No restrictions.") return for restriction in restrictions: - callback = report_callback.new(user_id=message.from_user.id) + callback = report_callback.new(restriction_id=restriction.id) markup = report_button("✉️ Report restriction",callback) - await message.answer(f"Restriction\n{restriction.operation}\nReason:{restriction.reason}\nDate:{restriction.date}",reply_markup=markup) + await message.answer(f"Restriction\n{restriction.operation}\nReason:{restriction.reason}\nDate:{restriction.timestamp}", + reply_markup=markup) await States.state1.set() @dp.callback_query_handler(text_contains="report_restriction",state=States.state1) -async def report_restriction(call:CallbackQuery,state:FSMContext): +async def report_restriction(call:CallbackQuery, state:FSMContext): await call.answer(cache_time=60) - # callback_data = call.data - # restriction_id = callback_data.split(":")[1] - + callback_data = call.data + restriction_id = callback_data.split(":")[1] + markup = ReplyKeyboardMarkup(resize_keyboard=True) cancel = KeyboardButton("❌ Cancel") markup.add(cancel) @@ -73,31 +74,34 @@ async def report_restriction(call:CallbackQuery,state:FSMContext): await state.update_data(restriction_id=restriction_id) await call.message.answer("Please,enter your report.",reply_markup=markup) + + await States.next() @dp.message_handler(state=States.state2) -async def get_message_report(message: types.Message,state:FSMContext): +async def get_message_report(message:types.Message, state:FSMContext): answer = message.text - - if not ("Cancel" in answer): - - restriction = database.search_user_restriction(message.from_user.id) + if not ("Cancel" in answer): + data = await state.get_data() + restriction_id = data.get("restriction_id") + restriction = Restriction.search(id=restriction_id) + if (restriction is None): return - #from_admin = restriction.from_admin - #to_user = restriction.to_user + from_user = restriction.from_user + to_user = restriction.to_user reason = restriction.reason if (not reason): reason = "No reason" - await bot.send_message(config.telegram_log_chat_id,( + await bot.send_message(config.second_group_id,( f"Report on restriction #{restriction_id}\n" - f"From admin:[{from_admin.first_name}](tg://user?id={from_admin.id})\n" - f"To user:[{from_admin.first_name}](tg://user?id={to_user.id})\n" + f"From user:[{from_user.first_name}](tg://user?id={from_user.id})\n" + f"To user:[{from_user.first_name}](tg://user?id={to_user.id})\n" f"Reason:{reason}\n" - f"Message:{answer}" + f"{answer}" ),parse_mode="Markdown") await message.answer("Report restriction sended",reply_markup=ReplyKeyboardRemove()) diff --git a/keyboards/inline/callback_data.py b/keyboards/inline/callback_data.py index d3be68d..06f8953 100644 --- a/keyboards/inline/callback_data.py +++ b/keyboards/inline/callback_data.py @@ -1,4 +1,4 @@ from aiogram.utils.callback_data import CallbackData -report_callback = CallbackData("report_restriction","user_id") +report_callback = CallbackData("report_restriction","restriction_id") diff --git a/load.py b/load.py index ffef08b..2aecea2 100644 --- a/load.py +++ b/load.py @@ -6,12 +6,6 @@ from aiogram.contrib.fsm_storage.memory import MemoryStorage import config import utils - -from database.database import Database - - -database = Database() - storage = MemoryStorage() # Create client connection diff --git a/media/photo.jpg b/media/photo.jpg deleted file mode 100644 index c907621cdcb67a8453eeeb1a0830055b96caabc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19730 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 diff --git a/utils/__init__.py b/utils/__init__.py index a539e05..984dcd7 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,9 +1,9 @@ from .notify_start import notify_started_bot from .default_commands import set_default_commands -from .update_user_data import check_user_data - from .telegram_client import TelegramClient from .parse_timedelta import parse_timedelta from .virustotal import VirusTotalAPI + +from .arguments_parser import getArgument,getCommandArgs,checkArg,parse_duration,delete_substring_from_string diff --git a/utils/arguments_parser.py b/utils/arguments_parser.py new file mode 100644 index 0000000..52b804e --- /dev/null +++ b/utils/arguments_parser.py @@ -0,0 +1,81 @@ +from dataclasses import dataclass +from database import Member +from aiogram import types + +import re + +def getArgument(arguments:list,index:int=0) -> str | None: + """ Get element from a list.If element not exist return None """ + if not (arguments): + return None + + if (len(arguments) > index): + return arguments[index] + else: + return None + +@dataclass +class CommandArguments: + to_user:Member | None + from_user:Member | None + arguments:list + +async def getCommandArgs(message: types.Message) -> CommandArguments: + """ + Describe user data and arguments from message + !command (username|id) ... + """ + + + arguments = message.text.split()[1:] + to_user = None + from_user = Member.search(Member.user_id, message.from_user.id) + + # If message replied + if (message.reply_to_message): + to_user = Member.search(Member.user_id, message.reply_to_message) + else: + user_data = getArgument(arguments) + + if (user_data): + if (user_data.isdigit()): + to_user = Member.search(Member.user_id, user_data) + + if (user_data[0] == "@"): + to_user = Member.search(Member.username, user_data) + + if (arguments) and (not to_user): + await message.answer(f"❌ User {to_user} not exist.") + + arguments = arguments[1:] + + return CommandArguments(to_user, from_user, arguments) + + +def delete_substring_from_string(string:str,substring:str) -> str: + string_list = string.split(substring) + return "".join(string_list).lstrip() + +def parse_duration(message) -> str: + duration = re.findall(r"(\d+d|\d+h|\d+m|\d+s)",''.join(message)) + duration = " ".join(duration) + return duration + +def checkArg(message:str) -> bool | None: + """ Check if first argument in ["enable","on","true"] then return true """ + if (not message): + return None + + argument = message.split() + argument = getArgument(message.split(),1) + + if (argument is None): + return None + + on = ['enable','on','true'] + off = ['disable','off','false'] + + if (argument in on): + return True + if (argument in off): + return False diff --git a/utils/update_user_data.py b/utils/update_user_data.py deleted file mode 100644 index bff9ceb..0000000 --- a/utils/update_user_data.py +++ /dev/null @@ -1,27 +0,0 @@ -from database.models import Member -from config import group_id - -async def check_user_data(): - """Check user data in database and update it""" - from load import tgc,database - - members = await tgc.members_list(group_id) - - for member in members: - exists = database.check_data_exists(Member.user_id,member["id"]) - - role = "member" - if (member["status"] == "ChatMemberStatus.OWNER"): - role = "owner" - - if (not exists): - database.register_user( - member["id"],member["first_name"], - member["username"],role - ) - else: - database.update_member_data( - member["id"], - [Member.first_name,Member.user_name], - [member["first_name"],member["username"]] - )