Code refactoring

This commit is contained in:
hok7z 2023-01-22 12:27:20 +02:00
parent c7e3ee2ba8
commit 46c11a893a
18 changed files with 299 additions and 207 deletions

11
app.py
View File

@ -1,17 +1,18 @@
#!/usr/bin/env python3
import logging
from aiogram import executor
from database import db, Member, Restriction
from aiogram import executor
from load import dp, bot, scheduler
import filters
import config
dp.filters_factory.bind(filters.AvaibleRolesFilter)
dp.filters_factory.bind(filters.ReplayMessageFilter)
import handlers
import config
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
@ -23,6 +24,7 @@ WEBHOOK_HOST = f'http://{WEBAPP_HOST}:{WEBAPP_PORT}'
WEBHOOK_PATH = f'/bot{config.token}/'
WEBHOOK_URL = f"{WEBHOOK_HOST}{WEBHOOK_PATH}"
async def on_startup(dp):
from utils.notify_start import notify_started_bot, database_is_empty
@ -46,6 +48,7 @@ async def on_startup(dp):
await bot.set_webhook(WEBHOOK_URL)
async def on_shutdown(dp):
await bot.delete_webhook()
@ -53,6 +56,7 @@ async def on_shutdown(dp):
await dp.storage.close()
await dp.storage.wait_closed()
def main() -> None:
if config.USE_WEBHOOK:
@ -69,5 +73,6 @@ def main() -> None:
else:
executor.start_polling(dp, skip_updates=True)
if __name__ == '__main__':
main()

View File

@ -1,6 +1,7 @@
from aiogram import Dispatcher, Bot
from environs import Env
env = Env()
env.read_env()

View File

@ -3,6 +3,7 @@ from aiogram.dispatcher.filters import BoundFilter
from database import Member, MemberRoles
class AvaibleRolesFilter(BoundFilter):
"""Filter accessed roles"""

View File

@ -1,6 +1,7 @@
from aiogram import types
from aiogram.dispatcher.filters import BoundFilter
class ReplayMessageFilter(BoundFilter):
"""Check if message replied"""
key = 'replied'

View File

@ -21,8 +21,7 @@ async def errors_handler(update: types.Update, exception):
await update.message.answer("Error happaned!\nBot terminated!")
await bot.send_message(config.second_group_id,
(
await bot.send_message(config.second_group_id, (
"Bot terminated"
f"{exception}"
), parse_mode="Markdown"

View File

@ -4,7 +4,10 @@ import config
from database import Member
@dp.message_handler(commands=["start","help"],chat_type=[types.ChatType.SUPERGROUP])
@dp.message_handler(
commands=["start", "help"],
chat_type=[types.ChatType.SUPERGROUP]
)
async def start_command_group(message: types.Message):
await message.answer((
f"Hi,**{message.from_user.first_name}**!\n"
@ -14,7 +17,11 @@ async def start_command_group(message:types.Message):
parse_mode="Markdown"
)
@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):
user = message.from_user
@ -22,18 +29,28 @@ async def leave_group(message:types.Message):
await message.answer("use /leave I UNDERSTAND")
return
Member.delete().get(Member.user_id == 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)
status = await bot.kick_chat_member(
chat_id=message.chat.id,
user_id=user.id,
until_date=None
)
if status:
await message.answer(f"User [{user.first_name}](tg://user?id={user.id}) has laved chat forever",parse_mode="Markdown")
await message.answer((
f"User [{user.first_name}](tg://user?id={user.id})"
"has leaved chat for forever"
), parse_mode="Markdown"
)
Member.delete().where(Member.user_id == user.id).execute()
@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):
user = Member.get(Member.user_id == message.from_user.id)
@ -45,7 +62,7 @@ async def get_information(message: types.Message):
@dp.message_handler(
commands=["report"],
commands=["report2"],
replied=True,
chat_type=[types.ChatType.SUPERGROUP]
)
@ -53,7 +70,7 @@ async def user_report(message: types.Message):
args = message.text.split()
if (len(args) != 2):
await message.answer("!report (reason)")
await message.answer("/report (reason)")
return
reported_user = message.reply_to_message.from_user
@ -64,8 +81,8 @@ async def user_report(message: types.Message):
config.second_group_id,
(
"Complaint about: [{}](tg://user?id={})\n"
"Complaint from: [{}](tg://user?id={})\n"
"Reason: {}\n"
"Complaint from: [{}](tg://user?id={})\n\n"
"Note: {}\n"
"{}"
).format(
reported_user.first_name,
@ -73,7 +90,7 @@ async def user_report(message: types.Message):
reporter_user.first_name,
reporter_user.id,
reason,
message.reply_to_message.link("Link message", as_html=False)
message.reply_to_message.link("link message", as_html=False)
),
parse_mode="Markdown",
)

View File

@ -1,11 +1,11 @@
from load import dp, types
from database import Member
@dp.message_handler(content_types=["new_chat_members"])
async def welcome_message(message: types.Message):
user = Member.get_or_none(Member.user_id == message.from_user.id)
if (user):
await message.answer(f"Hi, {user.first_name} again")
@ -19,9 +19,8 @@ async def welcome_message(message:types.Message):
await message.answer((
f"Hi, **{user.first_name}**!\n"
"Please, read [chat rules]({})"
).format(
"https://nometa.xyz"
),parse_mode="Markdown")
).format("https://nometa.xyz"),
parse_mode="Markdown"
)
await message.delete()

View File

@ -3,7 +3,7 @@ from aiogram.types.chat_permissions import ChatPermissions
import config
from database import Member, Restriction
from database import Restriction
from database import MemberRoles
from utils import get_command_args, get_argument, parse_timedelta_from_message
@ -20,7 +20,6 @@ async def ban_user(message: types.Message):
to_user = command.to_user
from_user = command.from_user
# If can't descibe user data
if (not to_user) or (not from_user):
await message.answer((
"Usage: !ban (@username|id) reason=None\n"
@ -28,14 +27,18 @@ async def ban_user(message: types.Message):
)
return
# Ban user and save (bool)
status = await bot.kick_chat_member(chat_id=message.chat.id, user_id=to_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 and (not command.is_silent):
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")
await message.answer((
f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has banned "
f"[{to_user.first_name}](tg://user?id={to_user.user_id})"
), parse_mode="Markdown")
# Open restrict
Restriction.create(
from_user=from_user,
to_user=to_user,
@ -43,6 +46,7 @@ async def ban_user(message: types.Message):
message_id=message.message_id
)
@dp.message_handler(
commands=["unban", "sunban"],
commands_prefix="!",
@ -54,7 +58,6 @@ async def unban_user(message: types.Message):
to_user = command.to_user
from_user = command.from_user
# If can't descibe user data
if (not to_user) or (not from_user):
await message.answer((
"Usage: !unban (@username|id) reason=None\n"
@ -62,12 +65,13 @@ async def unban_user(message: types.Message):
)
return
# Unban user and set status
status = await bot.unban_chat_member(chat_id=message.chat.id, user_id=to_user.user_id)
if status and (not command.is_silent):
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")
await message.answer((
f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has unbanned "
f"[{to_user.first_name}](tg://user?id={to_user.user_id})"
), parse_mode="Markdown")
Restriction.create(
from_user=from_user,
@ -76,6 +80,7 @@ async def unban_user(message: types.Message):
message_id=message.message_id
)
@dp.message_handler(
commands=["info"],
commands_prefix="!",
@ -83,7 +88,6 @@ async def unban_user(message: types.Message):
)
async def info_user(message: types.Message):
command = await get_command_args(message)
to_user = command.to_user
if (not to_user):
@ -99,6 +103,7 @@ async def info_user(message: types.Message):
parse_mode="Markdown"
)
@dp.message_handler(
commands=["kick", "skick"],
commands_prefix="!",
@ -118,11 +123,22 @@ async def kick_user(message: types.Message):
return
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)
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 (not status1 and status2) and (not command.is_silent):
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")
await message.answer((
f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has kicked "
f"[{to_user.first_name}](tg://user?id={to_user.user_id})"
), parse_mode="Markdown")
Restriction.create(
from_user=from_user,
@ -132,7 +148,6 @@ async def kick_user(message: types.Message):
)
@dp.message_handler(
commands=["mute", "smute"],
commands_prefix="!",
@ -144,7 +159,7 @@ async def mute_user(message:types.Message):
to_user = command.to_user
from_user = command.from_user
duration = await parse_timedelta_from_message(message)
duration = parse_timedelta_from_message(message)
if (not to_user) or (not from_user):
await message.answer((
@ -163,7 +178,10 @@ async def mute_user(message:types.Message):
)
if status and (not command.is_silent):
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})",parse_mode="Markdown")
await message.answer((
f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has muted "
f"[{to_user.first_name}](tg://user?id={to_user.user_id})"
), parse_mode="Markdown")
Restriction.create(
from_user=from_user,
@ -185,7 +203,6 @@ async def umute_user(message: types.Message):
to_user = command.to_user
from_user = command.from_user
# If can't
if (not to_user) or (not from_user):
await message.answer((
"Usage:!unmute (@username|id) reason=None.\n"
@ -216,7 +233,10 @@ async def umute_user(message: types.Message):
)
if status and (not command.is_silent):
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")
await message.answer((
f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has unmuted "
f"[{to_user.first_name}](tg://user?id={to_user.user_id})"
), parse_mode="Markdown")
@dp.message_handler(
@ -256,7 +276,11 @@ async def readonly_mode(message: types.Message):
)
config.group_permissions["can_send_messages"] = not status
await bot.set_chat_permissions(chat_id = message.chat.id, permissions = chat_permissions)
await bot.set_chat_permissions(
chat_id=message.chat.id,
permissions=chat_permissions
)
@dp.message_handler(
commands=["warn", "w"],
@ -280,12 +304,22 @@ async def warn_user(message: types.Message):
to_user.warns += 1
to_user.save()
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")
await message.answer((
f"[{from_user.first_name}](tg://user?id={from_user.user_id}) has warned "
f"[{to_user.first_name}](tg://user?id={to_user.user_id})"
), parse_mode="Markdown")
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)
await message.answer((
f"[{from_user.first_name}](tg://user?id={from_user.user_id}) "
f"has banned {config.limit_of_warns}/{config.limit_of_warns} ⚠️ "
), parse_mode="Markdown")
await bot.kick_chat_member(
chat_id=message.chat.id,
user_id=to_user.user_id,
until_date=None
)
Restriction.create(
from_user=from_user,
@ -305,6 +339,7 @@ async def reload(message: types.Message):
await message.answer("Reloaded!")
@dp.message_handler(
commands=["setrole"],
commands_prefix="!",
@ -335,5 +370,7 @@ async def set_role(message:types.Message):
to_user.role = new_role
to_user.save()
await message.answer(f"{new_role.capitalize()} role set for \
[{to_user.first_name}](tg://user?id={to_user.user_id})",parse_mode="Markdown")
await message.answer((
f"{new_role.capitalize()} role set for "
f"[{to_user.first_name}](tg://user?id={to_user.user_id})"
), parse_mode="Markdown")

View File

@ -2,10 +2,11 @@ from load import dp, types, bot
from database import Member, Restriction
from aiogram.types import KeyboardButton, ReplyKeyboardMarkup
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.types.reply_keyboard import ReplyKeyboardRemove
import config
from keyboards.default import menu
from keyboards.default import menus
from aiogram.types import CallbackQuery
from aiogram.dispatcher.filters import Text
@ -13,19 +14,22 @@ from aiogram.dispatcher.filters import Text
from aiogram.dispatcher.storage import FSMContext
from states.report_message import States
from keyboards.inline.report_button import report_button
from keyboards.inline.callback_data import report_callback
from keyboards.inline.report 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):
await message.answer((
f"Hi, **{message.from_user.first_name}**!\n"
"My commands:\n"
"\t\t/help /start - read this message.")
,parse_mode="Markdown",reply_markup=menu
"\t\t/help /start - read this message."
), parse_mode="Markdown", reply_markup=menus.bot_description_menu
)
# Keyboard
@dp.message_handler(Text(equals=["About Us"]))
async def about_us(message: types.Message):
await message.answer((
@ -37,12 +41,14 @@ async def about_us(message:types.Message):
"4. Users can report admins.\n"
"5. Admins can give warnings to users.\n"
"\nRelease version:2.5.2\n"
"[Github](https://github.com/hok7z/moderator-bot)"),
parse_mode="Markdown"
"[Github](https://github.com/hok7z/moderator-bot)"),parse_mode="Markdown"
)
@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):
user = Member.get(Member.user_id == message.from_user.id)
restrictions = Restriction.select().where(Restriction.to_user == user)
@ -53,25 +59,44 @@ async def check_for_restrict(message:types.Message):
for restriction in restrictions:
callback = report_callback.new(restriction_id=restriction.id)
markup = report_button("✉️ Report restriction",callback)
markup = InlineKeyboardMarkup()
report_restriction = InlineKeyboardButton(
"✉️ Report restriction",
callback_data=callback
)
markup.insert(report_restriction)
from_user = restriction.from_user
to_user = restriction.to_user
await message.answer(
(
f"Restriction #{restriction.id}\n"
f"from user: [{from_user.first_name}](tg://user?id={from_user.user_id})\n"
f"to user: [{from_user.first_name}](tg://user?id={to_user.user_id})\n"
f"{restriction.text}\n"
f"{restriction.timestamp}\n"
),parse_mode="Markdown",
reply_markup=markup
await message.answer((
"Restriction #{}\n"
"from user [{}](tg://user?id={})\n"
"to user [{}](tg://user?id={})\n"
"Note: {}\n"
"{}\n"
).format(
restriction.id,
from_user.first_name,
from_user.user_id,
to_user.first_name,
to_user.user_id,
restriction.text,
restriction.timestamp
), parse_mode="Markdown", reply_markup=markup
)
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):
await call.answer(cache_time=60)
@ -88,6 +113,7 @@ async def report_restriction(call:CallbackQuery, state:FSMContext):
await States.next()
@dp.message_handler(state=States.state2)
async def get_message_report(message: types.Message, state: FSMContext):
answer = message.text
@ -100,12 +126,13 @@ async def get_message_report(message:types.Message, state:FSMContext):
from_user = restriction.from_user
to_user = restriction.to_user
await bot.send_message(config.second_group_id,
(
restriction_timestamp = restriction.timestamp.strftime("%d.%m.%y at %H:%M (server time)")
await bot.send_message(config.second_group_id, (
"Report on restriction #{}\n"
"from user: [{}](tg://user?id={})\n"
"to user: [{}](tg://user?id={})\n"
"{}\n"
"Complaint from: [{}](tg://user?id={})\n"
"Complaint about: [{}](tg://user?id={})\n"
"Sent {}\n"
"{}\n"
"Message: {}"
).format(
@ -115,14 +142,19 @@ async def get_message_report(message:types.Message, state:FSMContext):
to_user.first_name,
to_user.user_id,
restriction.text,
restriction.timestamp,
restriction_timestamp,
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()
)
else:
await message.answer("Operation cancaled",reply_markup=ReplyKeyboardRemove())
await message.answer(
"Operation cancaled",
reply_markup=ReplyKeyboardRemove()
)
await state.finish()

View File

@ -1,2 +1,2 @@
from .menu import menu
from .menu import cancel
from .menus import bot_description_menu
from .menus import cancel_menu

View File

@ -1,19 +0,0 @@
from aiogram.types import ReplyKeyboardMarkup,KeyboardButton
menu = ReplyKeyboardMarkup(
resize_keyboard=True,
keyboard=[
[
KeyboardButton("Check restrictions"),
KeyboardButton("About Us"),
]
])
cancel = ReplyKeyboardMarkup(
resize_keyboard=True,
keyboard=[
[
KeyboardButton("❌Cancel")
]
]
)

View File

@ -0,0 +1,18 @@
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton
bot_description_menu = ReplyKeyboardMarkup(
resize_keyboard=True,
keyboard=[[
KeyboardButton("Check restrictions"),
KeyboardButton("About Us"),
]]
)
cancel_menu = ReplyKeyboardMarkup(
resize_keyboard=True,
keyboard=[[
KeyboardButton("❌Cancel")
]]
)

View File

@ -1 +1 @@
from . import report_button
from . import report

View File

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

View File

@ -0,0 +1,12 @@
from aiogram.utils.callback_data import CallbackData
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
report_callback = CallbackData("report_restriction", "restriction_id")
def inline_button(text, callback_data):
markup = InlineKeyboardMarkup()
button = InlineKeyboardButton(text, callback_data=callback_data)
markup.insert(button)
return markup

View File

@ -1,8 +0,0 @@
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
def report_button(text,callback_data):
markup = InlineKeyboardMarkup()
button = InlineKeyboardButton(text,callback_data=callback_data)
markup.insert(button)
return markup

View File

@ -2,30 +2,31 @@ import re
import typing
import datetime
from load import types
def parse_timedelta(value: str) -> typing.Optional[datetime.timedelta]:
regex = r'(?:(\d+)(?:d|д))?(?:(\d+)(?:h|ч))?(?:(\d+)(?:m|м))?(?:(\d+)(?:s|с))?'
specification = value.strip().replace(' ', '')
match = re.fullmatch(r'(?:(\d+)(?:d|д))?(?:(\d+)(?:h|ч))?(?:(\d+)(?:m|м))?(?:(\d+)(?:s|с))?', specification)
match = re.fullmatch(regex, specification)
if match:
units = [(0 if i is None else int(i)) for i in match.groups()]
return datetime.timedelta(days=units[0], hours=units[1], minutes=units[2], seconds=units[3])
else:
return None
return datetime.timedelta(
days=units[0],
hours=units[1],
minutes=units[2],
seconds=units[3]
)
async def parse_timedelta_from_message(
message: types.Message,
) -> typing.Optional[datetime.timedelta]:
def parse_timedelta_from_message(message: types.Message) -> typing.Optional[datetime.timedelta]:
_, *args = message.text.split()
if args:
duration = re.findall(r"(\d+d|\d+h|\d+m|\d+s)", ''.join(message.text))
if duration:
duration = " ".join(duration)
duration = parse_timedelta(duration)
if not duration:
duration = datetime.timedelta(0, 0, 0)
return duration
else:
return datetime.timedelta(0,0,0) # forever