Skip to content

Commit

Permalink
Merge pull request #1 from TimNekk/develop
Browse files Browse the repository at this point in the history
feat: add advanced logging, refactor and linter
  • Loading branch information
TimNekk authored Aug 10, 2022
2 parents 5fd722e + 7230363 commit e7ce4e7
Show file tree
Hide file tree
Showing 27 changed files with 167 additions and 73 deletions.
8 changes: 6 additions & 2 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@ REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASS=exampleRedisPassword

# Misc settings
LOG_FILE_NAME=log_file_name
# Log settings
LOG_FILE_NAME=log_file_name
LOG_ROTATION=00:00 # HH:MM
LOG_RETENTION=3 # in days

# Misc settings
28 changes: 28 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Lint

on:
- push
- pull_request

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy flake8
- name: Analysing the code with mypy
run: |
mypy . --install-types --non-interactive
- name: Analysing the code with flake8
run: |
flake8
23 changes: 11 additions & 12 deletions alembic/versions/73ffa12c03c8_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '73ffa12c03c8'
down_revision = None
Expand All @@ -19,17 +18,17 @@
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('users',
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('username', sa.String(length=32), nullable=True),
sa.Column('first_name', sa.String(length=255), nullable=True),
sa.Column('last_name', sa.String(length=255), nullable=True),
sa.Column('is_banned', sa.Boolean(), nullable=False),
sa.Column('deep_link', sa.String(length=225), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('username', sa.String(length=32), nullable=True),
sa.Column('first_name', sa.String(length=255), nullable=True),
sa.Column('last_name', sa.String(length=255), nullable=True),
sa.Column('is_banned', sa.Boolean(), nullable=False),
sa.Column('deep_link', sa.String(length=225), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('id')
)
# ### end Alembic commands ###


Expand Down
4 changes: 2 additions & 2 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
from tgbot.services.setting_commands import set_bot_command


async def main():
async def main() -> None:
config = load_config(".env")

logging.setup(config.misc.log_file_name)
logging.setup(config.log.file_name, config.log.rotation, config.log.retention)
logger.info("Starting bot")

if config.tg_bot.use_redis:
Expand Down
18 changes: 18 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[flake8]
max-line-length = 120
exclude =.git,__pycache__,docs/source/conf.py,build,dist,tests,alembic
ignore = I101,D100,D101,D102,D103,D104,D105,D107,D401,E203,I900,N802,N806,N812,W503,S311,S605,S607,ISC003,ISC001,T101,T000,F541,PL123,E712
per-file-ignores = __init__.py:F401,F403

[mypy]

ignore_missing_imports = True
disallow_untyped_defs = True
check_untyped_defs = True
warn_redundant_casts = True
no_implicit_optional = True
strict_optional = True
show_error_codes = True
strict_equality = true
warn_unreachable = true
warn_unused_configs = true
31 changes: 22 additions & 9 deletions tgbot/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass, fields
from typing import List, Optional
from datetime import time, timedelta, datetime
from typing import List, Optional, Generator

from aiogram.types import BotCommand
from environs import Env
Expand All @@ -13,15 +14,15 @@ class CommandInfo:
is_admin: bool = False
bot_command: Optional[BotCommand] = None

def __post_init__(self):
def __post_init__(self) -> None:
self.bot_command = BotCommand(self.command, self.description)


@dataclass
class Commands:
send_all: CommandInfo

def __iter__(self):
def __iter__(self) -> Generator[CommandInfo, None, None]:
return (getattr(self, field.name) for field in fields(self))


Expand All @@ -34,7 +35,7 @@ class DbConfig:
port: int
uri: str = ""

def __post_init__(self):
def __post_init__(self) -> None:
self.uri = f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}"


Expand All @@ -53,22 +54,31 @@ class TgBot:
commands: Commands


@dataclass
class LogConfig:
file_name: str
rotation: time
retention: timedelta


@dataclass
class Miscellaneous:
log_file_name: str
pass


@dataclass
class Config:
tg_bot: TgBot
db: DbConfig
redis: RedisConfig
log: LogConfig
misc: Miscellaneous


def load_config(path: str = None):
def load_config(path: str | None = None) -> Config:
env = Env()
env.read_env(path)

return Config(
tg_bot=TgBot(
token=env.str("BOT_TOKEN"),
Expand All @@ -90,7 +100,10 @@ def load_config(path: str = None):
password=env.str('REDIS_PASS'),
port=env.int('REDIS_PORT'),
),
misc=Miscellaneous(
log_file_name=env.str("LOG_FILE_NAME")
)
log=LogConfig(
file_name=env.str('LOG_FILE_NAME'),
rotation=datetime.strptime(env.str('LOG_ROTATION'), '%H:%M').time(),
retention=timedelta(days=env.int('LOG_RETENTION')),
),
misc=Miscellaneous()
)
2 changes: 1 addition & 1 deletion tgbot/filters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
from .command import CommandFilter


def register_filters(dp: Dispatcher):
def register_filters(dp: Dispatcher) -> None:
dp.filters_factory.bind(AdminFilter)
dp.filters_factory.bind(CommandFilter)
3 changes: 2 additions & 1 deletion tgbot/filters/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass

from aiogram import types
from aiogram.dispatcher.filters import BoundFilter

from tgbot.config import Config
Expand All @@ -10,6 +11,6 @@ class AdminFilter(BoundFilter):
key = 'is_admin'
is_admin: bool

async def check(self, obj):
async def check(self, obj: types.base.TelegramObject) -> bool:
config: Config = obj.bot.get('config')
return (obj.from_user.id in config.tg_bot.admin_ids) == self.is_admin
2 changes: 1 addition & 1 deletion tgbot/filters/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class CommandFilter(BoundFilter):
key = 'command'
command: CommandInfo

async def check(self, obj):
async def check(self, obj: types.base.TelegramObject) -> bool:
if not isinstance(obj, types.Message):
raise NotImplementedError("CommandFilter can only be used with Message")
message: types.Message = obj
Expand Down
2 changes: 1 addition & 1 deletion tgbot/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
from .start import register_start_handlers


def register_handlers(dp: Dispatcher):
def register_handlers(dp: Dispatcher) -> None:
register_admin_handlers(dp)
register_start_handlers(dp)
2 changes: 1 addition & 1 deletion tgbot/handlers/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
from .send_all import register_send_all_handlers


def register_admin_handlers(dp: Dispatcher):
def register_admin_handlers(dp: Dispatcher) -> None:
register_send_all_handlers(dp)
29 changes: 18 additions & 11 deletions tgbot/handlers/admin/send_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@
import validators

from tgbot.config import Config
from tgbot.keyboards.inline.admin import send_all_callback_data, send_all_keyboard, SendAllAction, broadcast_message_keyboard
from tgbot.keyboards.inline.admin import send_all_callback_data, send_all_keyboard, SendAllAction, \
broadcast_message_keyboard
from tgbot.models.user import User
from tgbot.models.user_tg import UserTG
from tgbot.states import SendAllState


async def send_all(message: types.Message, user: UserTG, state: FSMContext):
async def send_all(message: types.Message, user: UserTG, state: FSMContext) -> None:
text = "<b>Отправьте сообщение для рассылки</b>"
initial_message = await user.send_message(text)

await SendAllState.waiting_for_message.set()
await state.update_data(initial_message_id=initial_message.message_id)


async def confirm_send(message: types.Message, user: UserTG, state: FSMContext):
async def confirm_send(message: types.Message, user: UserTG, state: FSMContext) -> None:
data = await state.get_data()
initial_message_id: int = data.get("initial_message_id")

Expand All @@ -34,7 +35,7 @@ async def confirm_send(message: types.Message, user: UserTG, state: FSMContext):
await state.update_data(broadcast_message_id=broadcast_message.message_id)


async def ask_to_change_buttons(call: types.CallbackQuery, user: UserTG, state: FSMContext):
async def ask_to_change_buttons(call: types.CallbackQuery, user: UserTG, state: FSMContext) -> None:
text = """
Введите <b>ВСЕ</b> кнопки в формате:
Expand All @@ -51,7 +52,7 @@ async def ask_to_change_buttons(call: types.CallbackQuery, user: UserTG, state:
await state.update_data(buttons_message_id=buttons_message.message_id)


async def change_buttons(message: types.Message, user: UserTG, state: FSMContext):
async def change_buttons(message: types.Message, user: UserTG, state: FSMContext) -> None:
data = await state.get_data()
buttons_message_id: int = data.get("buttons_message_id")
broadcast_message_id: int = data.get("broadcast_message_id")
Expand All @@ -60,7 +61,8 @@ async def change_buttons(message: types.Message, user: UserTG, state: FSMContext
await message.delete()

buttons = list(filter(lambda button: len(button) == 2 and validators.url(button[1]),
[[button_args.strip() for button_args in button.split("|")] for button in message.text.split("\n")]))
[[button_args.strip() for button_args in button.split("|")]
for button in message.text.split("\n")]))

if buttons:
keyboard = broadcast_message_keyboard(buttons)
Expand All @@ -70,7 +72,7 @@ async def change_buttons(message: types.Message, user: UserTG, state: FSMContext
await SendAllState.waiting_for_confirm.set()


async def cancel(call: types.CallbackQuery, state: FSMContext):
async def cancel(call: types.CallbackQuery, state: FSMContext) -> None:
data = await state.get_data()
broadcast_message_id: int = data.get("broadcast_message_id")
await state.finish()
Expand All @@ -79,7 +81,7 @@ async def cancel(call: types.CallbackQuery, state: FSMContext):
await call.bot.delete_message(call.message.chat.id, broadcast_message_id)


async def start_broadcast(call: types.CallbackQuery, user: UserTG, state: FSMContext):
async def start_broadcast(call: types.CallbackQuery, user: UserTG, state: FSMContext) -> None:
data = await state.get_data()
broadcast_message_id: int = data.get("broadcast_message_id")
keyboard: types.InlineKeyboardMarkup = data.get("keyboard")
Expand All @@ -95,10 +97,15 @@ async def start_broadcast(call: types.CallbackQuery, user: UserTG, state: FSMCon
await user.send_message("<b>Рассылка закончилась!</b>")


def register_send_all_handlers(dp: Dispatcher):
def register_send_all_handlers(dp: Dispatcher) -> None:
config: Config = dp.bot.get("config")
dp.register_message_handler(send_all, command=config.tg_bot.commands.send_all, is_admin=True)
dp.register_message_handler(confirm_send, content_types=ContentType.ANY, is_admin=True, state=SendAllState.waiting_for_message)
dp.register_message_handler(send_all,
command=config.tg_bot.commands.send_all,
is_admin=True)
dp.register_message_handler(confirm_send,
content_types=ContentType.ANY,
is_admin=True,
state=SendAllState.waiting_for_message)
dp.register_callback_query_handler(ask_to_change_buttons,
send_all_callback_data.filter(action=SendAllAction.BUTTONS()),
is_admin=True,
Expand Down
4 changes: 2 additions & 2 deletions tgbot/handlers/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
from tgbot.models.user_tg import UserTG


async def start(message: types.Message, user: UserTG):
async def start(message: types.Message, user: UserTG) -> None:
text = f"""
<b>Привет{f', {user.info}' if user.info else ''}!</b>
"""

await user.send_message(text)


def register_start_handlers(dp: Dispatcher):
def register_start_handlers(dp: Dispatcher) -> None:
dp.register_message_handler(start, CommandStart())
4 changes: 2 additions & 2 deletions tgbot/keyboards/inline/admin/send_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ class SendAllAction(StringEnum):
CANCEL = auto()


def make_send_all_callback_data(action: SendAllAction):
def make_send_all_callback_data(action: SendAllAction) -> str:
return send_all_callback_data.new(action=action)


def send_all_keyboard():
def send_all_keyboard() -> InlineKeyboardMarkup:
keyboard = InlineKeyboardMarkup()

keyboard.add(InlineKeyboardButton(text="Изменить кнопки ⚙️",
Expand Down
2 changes: 1 addition & 1 deletion tgbot/middlewares/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .throttling import ThrottlingMiddleware


def register_middlewares(dp: Dispatcher, config: Config):
def register_middlewares(dp: Dispatcher, config: Config) -> None:
dp.setup_middleware(EnvironmentMiddleware(config=config))
dp.setup_middleware(ACLMiddleware())
dp.setup_middleware(LoggingMiddleware())
Expand Down
6 changes: 3 additions & 3 deletions tgbot/middlewares/acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class ACLMiddleware(BaseMiddleware):
@staticmethod
async def set_data(telegram_user: types.User, data: dict, deep_link: str | None = None):
async def set_data(telegram_user: types.User, data: dict, deep_link: str | None = None) -> None:
user = await UserTG.get(telegram_user.id)

if user is None:
Expand All @@ -24,8 +24,8 @@ async def set_data(telegram_user: types.User, data: dict, deep_link: str | None
data["user"] = user
data["deep_link"] = deep_link

async def on_pre_process_message(self, message: types.Message, data: dict):
async def on_pre_process_message(self, message: types.Message, data: dict) -> None:
await self.set_data(message.from_user, data, deep_link=message.get_args())

async def on_pre_process_callback_query(self, callback_query: types.CallbackQuery, data: dict):
async def on_pre_process_callback_query(self, callback_query: types.CallbackQuery, data: dict) -> None:
await self.set_data(callback_query.from_user, data)
Loading

0 comments on commit e7ce4e7

Please sign in to comment.