Skip to content

Commit

Permalink
Merge pull request #25 from TimNekk/develop
Browse files Browse the repository at this point in the history
Add Webhook
  • Loading branch information
TimNekk authored Nov 28, 2023
2 parents 534308e + e6a957f commit bfeff65
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 28 deletions.
26 changes: 24 additions & 2 deletions .env.dist
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# Bot settings

BOT_TOKEN=123456:Your-TokEn_ExaMple
ADMINS=123456,654321
USE_REDIS=False
SUBSCRIPTION_CHANNELS_IDS=-12345678901234,-12345678901235
USE_WEBHOOK=False
SKIP_UPDATES=False

# Docker settings

BOT_CONTAINER_NAME=bot_container_name
BOT_IMAGE_NAME=botimage_name

# DB settings

DB_USER=exampleDBUserName
DB_PASS=exampleDBPassword
DB_NAME=exampleDBName
Expand All @@ -18,17 +23,34 @@ DB_CONTAINER_NAME=db_container_name
DB_EXPOSE_PORT=5433

# Redis settings

REDIS_CONTAINER_NAME=redis_container_name
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASS=exampleRedisPassword
REDIS_POOL_SIZE=100

# Webhook settings

WEBHOOK_HOST=example.com
WEBHOOK_PORT=8443
WEBHOOK_PATH=/path/to/webhook

# Web Server settings

WEB_SERVER_HOST=0.0.0.0
WEB_SERVER_PORT=8443

# SSL settings

SSL_CERTS_PATH=/path/to/ssl/certs
SSL_CERT_FILE_NAME=ssl_cert_file_name.pem
SSL_KEY_FILE_NAME=ssl_key_file_name.pem

# Log settings

LOG_FILE_NAME=log_file_name.log
# HH:MM
LOG_ROTATION=00:00
# in days
LOG_RETENTION=3

# Misc settings
119 changes: 93 additions & 26 deletions bot.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import ssl

from aiogram import Bot, Dispatcher
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.contrib.fsm_storage.redis import RedisStorage2
from aiogram.dispatcher.storage import BaseStorage
from aiogram.types import AllowedUpdates
from asyncio import run

from aiogram.utils import executor
from loguru import logger

from tgbot.config import load_config
from tgbot.config import load_config, Config
from tgbot import filters
from tgbot import handlers
from tgbot import middlewares
Expand All @@ -16,50 +20,113 @@
from tgbot.services.setting_commands import set_bot_command


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

def setup_logging(config: Config) -> None:
logging.setup(config.log.file_name, config.log.rotation, config.log.retention)
logger.info("Starting bot")


def get_aiogram_storage(config: Config) -> BaseStorage:
if config.tg_bot.use_redis:
storage = RedisStorage2(host=config.redis.host,
port=config.redis.port,
password=config.redis.password,
pool_size=config.redis.pool_size)
else:
storage = MemoryStorage()
return RedisStorage2(host=config.redis.host,
port=config.redis.port,
password=config.redis.password,
pool_size=config.redis.pool_size)

return MemoryStorage()


def setup_dispatcher(config: Config, storage: BaseStorage | None) -> Dispatcher:
bot = Bot(token=config.tg_bot.token, parse_mode='HTML')
bot['config'] = config
dp = Dispatcher(bot, storage=storage)
return dp


async def setup_storages(config: Config, bot: Bot) -> None:
logger.info("Setting up storages...")
await db.on_startup(config.db.uri)
UserTG.bot = bot


async def register_middlewares_filters_handlers(dp: Dispatcher, config: Config) -> None:
logger.info("Registering middlewares, filters, handlers, dialogs...")
await middlewares.register(dp, config)
filters.register(dp)
handlers.register(dp)

await set_bot_command(bot, config)
await send_to_admins(bot, "Бот запущен")

try:
await dp.start_polling(allowed_updates=[
AllowedUpdates.MESSAGE,
AllowedUpdates.CALLBACK_QUERY,
AllowedUpdates.MY_CHAT_MEMBER
])
finally:
await dp.storage.close()
await dp.storage.wait_closed()
await (await bot.get_session()).close()
await db.on_shutdown()
async def setup_webhook(bot: Bot, config: Config) -> None:
ssl_cert = open(config.tg_bot.ssl.cert_file_path, 'rb').read()
logger.info(f"Set webhook on {config.tg_bot.webhook.url}")
await bot.set_webhook(
url=config.tg_bot.webhook.url,
certificate=ssl_cert
)


async def on_startup(dp: Dispatcher) -> None:
config: Config = dp.bot["config"]

await setup_storages(config, dp.bot)

await send_to_admins(dp.bot, "🟡 Бот запускается")

if config.tg_bot.use_webhook:
await setup_webhook(dp.bot, config)

await register_middlewares_filters_handlers(dp, config)
await set_bot_command(dp.bot, config)

await send_to_admins(dp.bot, "🟢 Бот запущен")


async def on_shutdown(dp: Dispatcher) -> None:
await send_to_admins(dp.bot, "🔴 Бот остановлен")
await dp.storage.close()
await dp.storage.wait_closed()
await (await dp.bot.get_session()).close()
await db.on_shutdown()
await dp.bot.delete_webhook()


def main() -> None:
config = load_config(".env")
setup_logging(config)
logger.info("Starting bot")

aiogram_storage = get_aiogram_storage(config)
dp = setup_dispatcher(config, aiogram_storage)

if config.tg_bot.use_webhook:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(config.tg_bot.ssl.cert_file_path,
config.tg_bot.ssl.key_file_path)

logger.info(f"Start webhook on webhook_path={config.tg_bot.webhook.path}, "
f"host={config.tg_bot.web_server.host}, port={config.tg_bot.web_server.port}")
executor.start_webhook(
dispatcher=dp,
webhook_path=config.tg_bot.webhook.path,
host=config.tg_bot.web_server.host,
port=config.tg_bot.web_server.port,
ssl_context=ssl_context,
on_startup=on_startup,
on_shutdown=on_shutdown,
skip_updates=config.tg_bot.skip_updates,
)
else:
executor.start_polling(dp,
on_startup=on_startup,
on_shutdown=on_shutdown,
allowed_updates=[
AllowedUpdates.MESSAGE,
AllowedUpdates.CALLBACK_QUERY,
AllowedUpdates.MY_CHAT_MEMBER
])


if __name__ == '__main__':
try:
run(main())
main()
except (KeyboardInterrupt, SystemExit):
logger.info("Bot stopped!")
raise SystemExit(0)
48 changes: 48 additions & 0 deletions tgbot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@ def __iter__(self) -> Generator[CommandInfo, None, None]:
return (getattr(self, field.name) for field in fields(self))


@dataclass
class WebhookConfig:
host: str
port: int
path: str

def __post_init__(self) -> None:
self.path = f"/{self.path}"
self.url = f"{self.host}:{self.port}{self.path}"


@dataclass
class WebServerConfig:
host: str
port: int


@dataclass
class SSLConfig:
cert_file_name: str
key_file_name: str

def __post_init__(self) -> None:
directory = "certs/"
self.cert_file_path = f"{directory}{self.cert_file_name}"
self.key_file_path = f"{directory}{self.key_file_name}"


@dataclass
class DbConfig:
host: str
Expand All @@ -53,6 +81,11 @@ class TgBot:
token: str
admin_ids: List[int]
use_redis: bool
skip_updates: bool
use_webhook: bool
webhook: WebhookConfig
web_server: WebServerConfig
ssl: SSLConfig
commands: Commands
subscription_channels_ids: List[int]

Expand Down Expand Up @@ -91,6 +124,21 @@ def load_config(path: str | None = None) -> Config:
commands=Commands(
send_all=CommandInfo("send_all", "Запустить рассылку", is_admin=True),
ping=CommandInfo("ping", "Узнать пинг", is_admin=True),
),
use_webhook=env.bool("USE_WEBHOOK"),
skip_updates=env.bool("SKIP_UPDATES"),
webhook=WebhookConfig(
host=env.str('WEBHOOK_HOST'),
port=env.str("WEBHOOK_PORT"),
path=env.str('WEBHOOK_PATH')
),
web_server=WebServerConfig(
host=env.str('WEB_SERVER_HOST'),
port=env.int('WEB_SERVER_PORT')
),
ssl=SSLConfig(
cert_file_name=env.str('SSL_CERT_FILE_NAME'),
key_file_name=env.str('SSL_KEY_FILE_NAME')
)
),
db=DbConfig(
Expand Down

0 comments on commit bfeff65

Please sign in to comment.