Some fix database and etc

This commit is contained in:
hok7z 2022-11-06 10:48:10 +02:00
parent 2f7cae39f4
commit d390d2ebe9
21 changed files with 437 additions and 478 deletions

View File

@ -1,5 +1,7 @@
bot_token = "" bot_token = ""
limit_of_warns = 5
api_id = "" api_id = ""
api_hash = "" api_hash = ""

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ modules/__pycache__
pyrightconfig.json pyrightconfig.json
session.session session.session
session.session-journal session.session-journal
venv

View File

@ -40,7 +40,8 @@ Logging admin command actions in database.
- [ ] Analys file for malware 🔎 - [ ] Analys file for malware 🔎
- [ ] Paste text to PasteBin or PrivNote 📋 - [ ] Paste text to PasteBin or PrivNote 📋
- [ ] Site for group moderator 🌍 - [ ] Site for group moderator 🌍
- [ ] Fix database errors ForeignKeys - [ ] Change PeeWee to SQLAlchemy
- [x] Some fix in database
## Support ## Support
Every investition helps in maintaining this project and making it better. Every investition helps in maintaining this project and making it better.

9
app.py
View File

@ -1,7 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import logging import logging
from aiogram import executor from aiogram import executor
from database import models from database import build
from load import dp, bot from load import dp, bot
import filters import filters
@ -12,7 +13,6 @@ dp.filters_factory.bind(filters.ReplayMessageFilter)
import handlers import handlers
import config import config
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
WEBAPP_HOST = '127.0.0.1' WEBAPP_HOST = '127.0.0.1'
@ -43,9 +43,9 @@ async def on_shutdown(dp):
await dp.storage.wait_closed() await dp.storage.wait_closed()
def main() -> None: def main() -> None:
models.build() build()
if config.use_webhook: if config.USE_WEBHOOK:
executor.start_webhook( executor.start_webhook(
dispatcher=dp, dispatcher=dp,
webhook_path=WEBHOOK_PATH, webhook_path=WEBHOOK_PATH,
@ -59,6 +59,5 @@ def main() -> None:
else: else:
executor.start_polling(dp,skip_updates=True) executor.start_polling(dp,skip_updates=True)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -4,13 +4,15 @@ from environs import Env
env = Env() env = Env()
env.read_env() env.read_env()
use_webhook = True USE_WEBHOOK = True
# bot token # bot token
token = env.str("bot_token") token = env.str("bot_token")
group_id = env.str("group_id") group_id = env.int("group_id")
second_group_id = env.str("second_group_id") second_group_id = env.int("second_group_id")
limit_of_warns = 5
# Telegram Application # Telegram Application
api_id = env.int("api_id") api_id = env.int("api_id")
@ -19,6 +21,7 @@ api_hash = env.str("api_hash")
# Virus Total API # Virus Total API
vt_api = env.str("vt_api") vt_api = env.str("vt_api")
group_permissions = { group_permissions = {
"can_send_messages":True, "can_send_messages":True,
"can_send_media_messages":False, "can_send_media_messages":False,
@ -32,11 +35,5 @@ group_permissions = {
db_url = env.str("db_url") db_url = env.str("db_url")
# telegram-bot-api-service
telegram_api_server = env.str("telegram_api_server").split(":") telegram_api_server = env.str("telegram_api_server").split(":")
telegram_api_server = { telegram_api_server = f"http://{telegram_api_server[0]}:{telegram_api_server[1]}"
"ip":telegram_api_server[0],
"port":telegram_api_server[1]
}
telegram_api_server = f"http://{telegram_api_server['ip']}:{telegram_api_server['port']}"

87
database.py Normal file
View File

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

View File

@ -1 +0,0 @@
from .database import Database

View File

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

View File

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

View File

@ -1,10 +1,7 @@
from aiogram import types from aiogram import types
from aiogram.dispatcher.filters import BoundFilter from aiogram.dispatcher.filters import BoundFilter
from database.database import Member from database import Member, MemberRoles
from database.models import MemberRoles
from load import database
class AvaibleRolesFilter(BoundFilter): class AvaibleRolesFilter(BoundFilter):
"""Filter accessed roles""" """Filter accessed roles"""
@ -15,7 +12,12 @@ class AvaibleRolesFilter(BoundFilter):
self.avaible_roles = available_roles self.avaible_roles = available_roles
async def check(self,message:types.Message): 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"): if (member.role == "owner"):
return True return True

View File

@ -3,4 +3,4 @@ from load import dp,types
# TODO: channel post forward in chat # TODO: channel post forward in chat
@dp.channel_post_handler() @dp.channel_post_handler()
async def channel_handler(message:types.Message): async def channel_handler(message:types.Message):
print(message.text) pass

View File

@ -1,248 +1,197 @@
from load import bot, database, dp, types from load import bot, dp, types
from aiogram.types.chat_permissions import ChatPermissions from aiogram.types.chat_permissions import ChatPermissions
import config import config
import utils 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
@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: # Filters
string_list = string.split(substring) # is_admin=True - Check admin permission, if user is admin, continue.
return "".join(string_list).lstrip() # replied=True - If message is answer, continue.
# accessed_roles - list roles.
# Filters: @dp.message_handler(commands=["ban"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN])
# 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])
async def ban_user(message: types.Message): async def ban_user(message: types.Message):
"""
!ban (@username/id) reason=None
"""
command = await getCommandArgs(message) command = await getCommandArgs(message)
reason = getArgument(command.arguments) reason = getArgument(command.arguments)
user = command.user to_user = command.to_user
admin = message.from_user from_user = command.from_user
# If can't descibe user data # If can't descibe user data
if (user is None): if (not to_user) or (not from_user):
await message.answer(( await message.answer((
"Usage:!ban (@username|id) reason=None.\n" "Usage: !ban (@username|id) reason=None\n"
"Reply to a message or use with a username.") "Reply to a message or use with a username")
) )
return return
# Ban user and save (bool) # 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: if status:
await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been banned.", 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")
parse_mode="Markdown")
# Delete user from database
database.delete_user(user.user_id)
# Open restrict # 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.HELPER,MemberRoles.ADMIN])
@dp.message_handler(commands=["unban"],commands_prefix="!",
available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER])
async def unban_user(message: types.Message): async def unban_user(message: types.Message):
"""
!unban (@username/id) reason=None
"""
command = await getCommandArgs(message) command = await getCommandArgs(message)
user = command.user
to_user = command.to_user
from_user = command.from_user
# If can't descibe user data # If can't descibe user data
if (user is None): if (not to_user) or (not from_user):
await message.answer(( await message.answer((
"Usage:!unban (@username|id) reason=None.\n" "Usage: !unban (@username|id) reason=None\n"
"Reply to a message or use with username/id.") "Reply to a message or use with username/id")
) )
return return
# Unban user and set status (bool) # Unban user and set status (bool)
status = await bot.unban_chat_member(chat_id=message.chat.id, user_id=user.user_id) status = await bot.unban_chat_member(chat_id=message.chat.id, user_id=to_user.user_id)
# add user to database
database.register_user(user.user_id, user.first_name)
if status: if status:
await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been unbaned.", 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")
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=["kick"],commands_prefix="!", @dp.message_handler(commands=["info"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN])
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): async def kick_user(message:types.Message):
"""
!kick (@username/id) reason=None
"""
command = await getCommandArgs(message) command = await getCommandArgs(message)
arguments = command.arguments arguments = command.arguments
user = command.user to_user = command.to_user
admin = message.from_user from_user = command.from_user
reason = getArgument(arguments) reason = getArgument(arguments)
if (user is None): if (not to_user) or (not from_user):
await message.answer(( await message.answer((
"Usage:!kick (@username|id) reason=None.\n" "Usage: !kick (@username|id) reason=None\n"
"Reply to a message or use with a username/id.") "Reply to a message or use with a username/id")
) )
return return
status1 = await bot.kick_chat_member(chat_id=message.chat.id, user_id=user.user_id, until_date=None) 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=user.user_id) status2 = await bot.unban_chat_member(chat_id=message.chat.id, user_id=to_user.user_id)
if (status1 and status2): if (not status1 and status2):
await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been kicked.", 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")
parse_mode="Markdown")
database.create_restriction(admin.id,user.user_id,"kick",reason)
@dp.message_handler(commands=["mute"],commands_prefix="!", Restriction.create(
available_roles=[MemberRoles.ADMIN]) from_user=from_user,
to_user=to_user,
action="Kick user",
reason=reason,
)
@dp.message_handler(commands=["mute"],commands_prefix="!",available_roles=[MemberRoles.ADMIN])
async def mute_user(message:types.Message): async def mute_user(message:types.Message):
"""
!mute (@username/id) reason=None
"""
command = await getCommandArgs(message) command = await getCommandArgs(message)
arguments = command.arguments arguments = command.arguments
user = command.user to_user = command.to_user
admin = message.from_user from_user = command.from_user
if (user is None): if (not to_user) or (not from_user):
await message.answer(( await message.answer((
"Usage:!mute (@username|id) [duration].\n" "Usage:!mute (@username|id) (duration)\n"
"Reply to a message or use with a username/id.") "Reply to a message or use with a username/id")
) )
return return
duration = re.findall(r"(\d+d|\d+h|\d+m|\d+s)",''.join(arguments)) duration_string = parse_duration(arguments)
duration = " ".join(duration) duration = None
reason = delete_substring_from_string(" ".join(arguments),duration) reason = None
duration_timedelta = utils.parse_timedelta(duration)
if not duration: if (duration_string):
await message.answer(f"Error: \"{duration}\" — неверный формат времени. Examles: 3ч, 5м, 4h30s.") duration = utils.parse_timedelta(duration_string)
return
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) permissions = ChatPermissions(can_send_messages=False)
status = await bot.restrict_chat_member( status = await bot.restrict_chat_member(
chat_id=message.chat.id, chat_id=message.chat.id,
user_id=user.user_id, user_id=to_user.user_id,
until_date=duration_timedelta, until_date=duration,
permissions=permissions permissions=permissions
) )
if status: if status:
await message.answer(f"User **{user.first_name}** has been muted.", 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")
parse_mode="Markdown")
database.create_restriction(user.user_id, admin.id, "mute", reason)
@dp.message_handler(commands=["umute"],commands_prefix="!", Restriction.create(
available_roles=[MemberRoles.ADMIN]) from_user=from_user,
to_user=to_user,
action="Mute user",
reason=reason,
)
@dp.message_handler(commands=["unmute"],commands_prefix="!",available_roles=[MemberRoles.ADMIN])
async def umute_user(message: types.Message): async def umute_user(message: types.Message):
"""
!umute (@username/id) reason=None
"""
# Get information # Get information
command = await getCommandArgs(message) command = await getCommandArgs(message)
user = command.user
to_user = command.to_user
from_user = command.from_user
# If can't # If can't
if (user is None): if (not to_user) or (not from_user):
await message.answer(( await message.answer((
"Usage:!unmute (@username|id) reason=None.\n" "Usage:!unmute (@username|id) reason=None.\n"
"Reply to a message or use with a username/id.") "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 # Restrict user and save
status = await bot.restrict_chat_member( status = await bot.restrict_chat_member(
chat_id=message.chat.id, chat_id=message.chat.id,
user_id=user.user_id, user_id=to_user.user_id,
permissions=permissions permissions=permissions
) )
if status: if status:
await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has been unmuted.", 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")
parse_mode="Markdown")
@dp.message_handler(commands=["pin"],commands_prefix="!", @dp.message_handler(commands=["pin"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN])
available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER])
async def pin_message(message:types.Message): async def pin_message(message:types.Message):
await bot.pin_chat_message(message.chat.id, message.reply_to_message.message_id) await bot.pin_chat_message(message.chat.id, message.reply_to_message.message_id)
@dp.message_handler(commands=["readonly","ro"],commands_prefix="!", @dp.message_handler(commands=["readonly","ro"],commands_prefix="!",available_roles=[MemberRoles.ADMIN])
available_roles=[MemberRoles.ADMIN])
async def readonly_mode(message:types.Message): async def readonly_mode(message:types.Message):
"""
!ro/!readonly (@username/id)
"""
check = checkArg(message.text) check = checkArg(message.text)
if (check is None): if (not check):
await message.answer("!ro on/off alias:disable,enable,start,stop.") await message.answer("Usage:!ro on,enable,start/off,disable,off\n")
return return
# Get chat permissions # Get chat permissions
@ -318,13 +261,12 @@ async def readonly_mode(message:types.Message):
await message.answer(f"readonly - {check}") await message.answer(f"readonly - {check}")
@dp.message_handler(commands=["media"],commands_prefix="!", @dp.message_handler(commands=["media"],commands_prefix="!",available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER])
available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER])
async def media_content(message: types.Message): async def media_content(message: types.Message):
check = checkArg(message.text) check = checkArg(message.text)
if (check is None): if (not check):
await message.answer("!media on/off alias:disable,enable,start,stop.") await message.answer("Usage: !media on,enable,start/off,disable,off")
return return
# Get chat permissions # 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) status = await bot.set_chat_permissions(chat_id=message.chat.id, permissions=chat_permissions)
if status: if status:
await message.answer(f"media - {check}.") await message.answer(f"media - {check}")
@dp.message_handler(commands=["stickers"],commands_prefix="!", @dp.message_handler(commands=["stickers"],commands_prefix="!",available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER])
available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER])
async def send_stickers(message: types.Message): async def send_stickers(message: types.Message):
# Get arguments # Get arguments
check = checkArg(message.text) check = checkArg(message.text)
if (check is None): if (not check):
await message.answer("!stickers on/off alias:disable,enable,start,stop") await message.answer("Usage: !stickers on,enable,start/off,disable,off")
return return
# Get chat permissions # 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) status = await bot.set_chat_permissions(chat_id=message.chat.id, permissions=chat_permissions)
if status: if status:
await message.answer(f"stickes - {check}.") await message.answer(f"stickes - {check}")
@dp.message_handler(commands=["w","warn"],commands_prefix="!", @dp.message_handler(commands=["warn","w"],commands_prefix="!",available_roles=[MemberRoles.HELPER,MemberRoles.ADMIN])
available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER])
async def warn_user(message: types.Message): async def warn_user(message: types.Message):
# Get information # Get information
command = await getCommandArgs(message) command = await getCommandArgs(message)
reason = getArgument(command.arguments) reason = getArgument(command.arguments)
user = command.user to_user = command.to_user
admin = message.from_user from_user = command.from_user
if (user is None): if (not to_user) or (not from_user):
await message.answer(( await message.answer((
"Usage:!warn (@username/id) reason=None.\n" "Usage: !warn (@username|id) reason=None\n"
"Reply to a message or use with username/id.") "Reply to a message or use with username/id")
) )
return return
# Add warning to_user.warns += 1
database.change_reports(user.user_id, delete=True) to_user.save()
await message.answer(f"User [{user.first_name}](tg://user?id={user.user_id}) has gotten a warning.", 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")
parse_mode="Markdown")
database.create_restriction(user.user_id, admin.id, "warn", reason)
@dp.message_handler(commands=["reload"],commands_prefix="!",available_roles=[MemberRoles.ADMIN,MemberRoles.HELPER]) 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="!")
async def reload(message:types.Message): 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 = await bot.get_chat(message.chat.id)
group_permissions = dict(group["permissions"]) group_permissions = dict(group["permissions"])
@ -417,33 +391,34 @@ async def reload(message:types.Message):
for permission in group_permissions.keys(): for permission in group_permissions.keys():
config.group_permissions[permission] = group_permissions[permission] 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="!", @dp.message_handler(commands=["setrole"],commands_prefix="!",available_roles=[MemberRoles.ADMIN])
available_roles=[MemberRoles.ADMIN])
async def set_role(message:types.Message): async def set_role(message:types.Message):
command = await getCommandArgs(message) command = await getCommandArgs(message)
new_role = getArgument(command.arguments) new_role = getArgument(command.arguments)
user = command.user to_user = command.to_user
admin = database.search_single_member(Member.user_id,message.from_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(( 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." "Reply to a message or use with username."
)) ))
return return
if not (new_role in [member.value for member in MemberRoles]): 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 return
if (admin.user_id == user.user_id): if (from_user.user_id == to_user.user_id):
await message.answer("❌ You can't set role yourself.") await message.answer("❌ You can't set role yourself")
return return
database.update_member_data(user.user_id,[Member.role],[new_role]) to_user.role = new_role
to_user.save()
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")

View File

@ -1,6 +1,5 @@
from load import dp, database, types from load import dp, types
from database.models import Member from database import Member
# TODO: fix it # TODO: fix it
# import utils # import utils
@ -21,13 +20,18 @@ async def welcome_message(message:types.Message):
# User # User
user = message.from_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): if (exists):
await message.answer("Спасибо что вы с нами.") await message.answer("Спасибо что вы с нами.")
if not (exists): 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 # TODO: translate it
await message.answer(( await message.answer((
f"Привет,{user.first_name}\n" f"Привет,{user.first_name}\n"
@ -48,8 +52,6 @@ async def welcome_message(message:types.Message):
# for user_message in message.text.lower().split(): # for user_message in message.text.lower().split():
# if (y in user_message):await message.delete() # if (y in user_message):await message.delete()
# Joke
@dp.message_handler(content_types=types.ContentType.VOICE) @dp.message_handler(content_types=types.ContentType.VOICE)
async def voice_message(message:types.Message): async def voice_message(message:types.Message):
photo = types.InputFile(path_or_bytesio="media/photo.jpg") pass
await message.answer_photo(photo)

View File

@ -1,9 +1,7 @@
from load import bot, dp, types from load import bot, dp, types
import config import config
from database import Member
from load import database
from database.models import Member
@dp.message_handler(commands=["leave"],chat_type=[types.ChatType.SUPERGROUP]) @dp.message_handler(commands=["leave"],chat_type=[types.ChatType.SUPERGROUP])
async def leave_group(message:types.Message): async def leave_group(message:types.Message):
@ -15,7 +13,8 @@ async def leave_group(message:types.Message):
await message.answer("Для того чтобы покинуть чат вам нужно ввести /leave I UNDERSTANT!") await message.answer("Для того чтобы покинуть чат вам нужно ввести /leave I UNDERSTANT!")
return return
database.delete_user(user.id) # TODO: rewrite it
# database.delete_user(user.id)
# Ban user and save (bool) # Ban user and save (bool)
status = await bot.kick_chat_member(chat_id=message.chat.id,user_id=user.id,until_date=None) 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]) @dp.message_handler(commands=["bio","me"],chat_type=[types.ChatType.SUPERGROUP])
async def get_information(message: types.Message): 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!")
if (user is None):
await message.answer("❌Sorry,you not member group.")
return return
await message.answer(( await message.answer((
f"User:[{user.first_name}](tg://user?id={user.user_id})\n" f"[{user.first_name}](tg://user?id={user.user_id}) ({user.role})\n"
f"level:{role_level[user.role]}\n"), f"Warns: {user.warns}/{config.limit_of_warns}"),
parse_mode="Markdown" parse_mode="Markdown"
) )

View File

@ -1,5 +1,5 @@
from load import dp,types,database,bot from load import dp, types, bot
from database.models import Member from database import Member, Restriction
from aiogram.types import KeyboardButton,ReplyKeyboardMarkup from aiogram.types import KeyboardButton,ReplyKeyboardMarkup
from aiogram.types.reply_keyboard import ReplyKeyboardRemove 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]) @dp.message_handler(commands=["start","help"],chat_type=[types.ChatType.PRIVATE])
async def start_command_private(message:types.Message): async def start_command_private(message:types.Message):
await message.answer(( await message.answer((
f"Hello,**{message.from_user.first_name}**!\n" f"Hi, **{message.from_user.first_name}**!\n"
"My commands:\n" "My commands:\n"
"\t\t/help /start - read this message.") "\t\t/help /start - read this message.")
,parse_mode="Markdown",reply_markup=menu ,parse_mode="Markdown",reply_markup=menu
@ -44,27 +44,28 @@ async def about_us(message:types.Message):
@dp.message_handler(Text(equals=["Check restrictions"]),state=None) @dp.message_handler(Text(equals=["Check restrictions"]),state=None)
async def check_for_restrict(message:types.Message): async def check_for_restrict(message:types.Message):
user = message.from_user user = Member.get(Member.user_id == message.from_user.id)
restrictions = database.search_user_restriction(user_id=user.id) restrictions = Restriction.search(to_user=user)
if (restrictions is None): if (not restrictions):
await message.answer("✅No restrictions.") await message.answer("✅No restrictions.")
return return
for restriction in restrictions: 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) 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() await States.state1.set()
@dp.callback_query_handler(text_contains="report_restriction",state=States.state1) @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) await call.answer(cache_time=60)
# callback_data = call.data callback_data = call.data
# restriction_id = callback_data.split(":")[1] restriction_id = callback_data.split(":")[1]
markup = ReplyKeyboardMarkup(resize_keyboard=True) markup = ReplyKeyboardMarkup(resize_keyboard=True)
cancel = KeyboardButton("❌ Cancel") cancel = KeyboardButton("❌ Cancel")
@ -74,30 +75,33 @@ async def report_restriction(call:CallbackQuery,state:FSMContext):
await call.message.answer("Please,enter your report.",reply_markup=markup) await call.message.answer("Please,enter your report.",reply_markup=markup)
await States.next()
@dp.message_handler(state=States.state2) @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 answer = message.text
if not ("Cancel" in answer): if not ("Cancel" in answer):
data = await state.get_data()
restriction = database.search_user_restriction(message.from_user.id) restriction_id = data.get("restriction_id")
restriction = Restriction.search(id=restriction_id)
if (restriction is None): if (restriction is None):
return return
#from_admin = restriction.from_admin from_user = restriction.from_user
#to_user = restriction.to_user to_user = restriction.to_user
reason = restriction.reason reason = restriction.reason
if (not reason): if (not reason):
reason = "No 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"Report on restriction #{restriction_id}\n"
f"From admin:[{from_admin.first_name}](tg://user?id={from_admin.id})\n" f"From user:[{from_user.first_name}](tg://user?id={from_user.id})\n"
f"To user:[{from_admin.first_name}](tg://user?id={to_user.id})\n" f"To user:[{from_user.first_name}](tg://user?id={to_user.id})\n"
f"Reason:{reason}\n" f"Reason:{reason}\n"
f"Message:{answer}" f"{answer}"
),parse_mode="Markdown") ),parse_mode="Markdown")
await message.answer("Report restriction sended",reply_markup=ReplyKeyboardRemove()) await message.answer("Report restriction sended",reply_markup=ReplyKeyboardRemove())

View File

@ -1,4 +1,4 @@
from aiogram.utils.callback_data import CallbackData from aiogram.utils.callback_data import CallbackData
report_callback = CallbackData("report_restriction","user_id") report_callback = CallbackData("report_restriction","restriction_id")

View File

@ -6,12 +6,6 @@ from aiogram.contrib.fsm_storage.memory import MemoryStorage
import config import config
import utils import utils
from database.database import Database
database = Database()
storage = MemoryStorage() storage = MemoryStorage()
# Create client connection # Create client connection

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,9 +1,9 @@
from .notify_start import notify_started_bot from .notify_start import notify_started_bot
from .default_commands import set_default_commands from .default_commands import set_default_commands
from .update_user_data import check_user_data
from .telegram_client import TelegramClient from .telegram_client import TelegramClient
from .parse_timedelta import parse_timedelta from .parse_timedelta import parse_timedelta
from .virustotal import VirusTotalAPI from .virustotal import VirusTotalAPI
from .arguments_parser import getArgument,getCommandArgs,checkArg,parse_duration,delete_substring_from_string

81
utils/arguments_parser.py Normal file
View File

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

View File

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