Compare commits

..

3 Commits

Author SHA1 Message Date
7d50cf95cf Feat: started working on the sub notification 2025-01-13 15:50:45 +03:00
b5a893e5aa Feat: some fixes 2025-01-13 15:18:48 +03:00
9ee8e43fb4 Feat: fixed start command 2025-01-13 11:49:59 +03:00
10 changed files with 117 additions and 9 deletions

View File

@@ -22,7 +22,7 @@ services:
POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_DB: ${POSTGRES_DB:-postgres} POSTGRES_DB: ${POSTGRES_DB:-postgres}
ports: ports:
- 5432 - "127.0.0.1:${POSTGRES_PORT}:5432"
healthcheck: healthcheck:
test: ["CMD", "pg_isready"] test: ["CMD", "pg_isready"]
interval: 10s interval: 10s

12
main.py
View File

@@ -1,3 +1,4 @@
import asyncio
import logging import logging
from sys import exit from sys import exit
@@ -7,6 +8,10 @@ from nwxraybot import NwXrayBot, Settings
from nwxraybot.handlers import * from nwxraybot.handlers import *
from nwxraybot.models import User from nwxraybot.models import User
async def main(bot: NwXrayBot, skip_updates: bool = True) -> None:
await asyncio.create_task(bot.start())
if __name__ == "__main__": if __name__ == "__main__":
config = Settings() # Load config from .env config = Settings() # Load config from .env
logging.basicConfig(level=logging.DEBUG if config.debug else logging.INFO) logging.basicConfig(level=logging.DEBUG if config.debug else logging.INFO)
@@ -18,7 +23,12 @@ if __name__ == "__main__":
User.create_table() User.create_table()
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
loop = uvloop.new_event_loop()
asyncio.set_event_loop(loop)
# Start bot # Start bot
bot = NwXrayBot(config.bot_token.get_secret_value()) bot = NwXrayBot(config.bot_token.get_secret_value())
bot.include_routers(HelloHandler(), MenuHandler(), AdminHandler()) bot.include_routers(HelloHandler(), MenuHandler(), AdminHandler())
uvloop.run(bot.start(skip_updates=True))
asyncio.run(main(bot))

View File

@@ -2,6 +2,7 @@ from aiogram import Bot, Dispatcher, Router
from aiogram.utils.callback_answer import CallbackAnswerMiddleware from aiogram.utils.callback_answer import CallbackAnswerMiddleware
from nwxraybot.middlewares import UserMiddleware from nwxraybot.middlewares import UserMiddleware
from nwxraybot.notifiers import setup_subscription_notifier
from .meta import Handler from .meta import Handler
@@ -12,6 +13,7 @@ class NwXrayBot:
self.dp = Dispatcher() self.dp = Dispatcher()
self.dp.message.middleware(UserMiddleware()) self.dp.message.middleware(UserMiddleware())
self.dp.message.middleware(CallbackAnswerMiddleware()) self.dp.message.middleware(CallbackAnswerMiddleware())
setup_subscription_notifier(self.bot)
def include_routers(self, *routers: Handler): def include_routers(self, *routers: Handler):
for router in routers: for router in routers:

View File

@@ -19,7 +19,7 @@ class AdminHandler(Handler):
@self.router.message(Command('adduser')) @self.router.message(Command('adduser'))
async def add_user(message: Message): async def add_user(message: Message):
mask = r"^(?P<name>[a-zA-Z0-9]+)\s(?P<url>[^\s]+)($|\s(?P<date>[0-9]{2}\.[0-9]{2}\.[0-9]{4})\s(?P<time>[0-9]{2}\:[0-9]{2})$)" mask = r"^(?P<name>[a-zA-Z0-9]+)\s(?P<url>vless://[^\s]+)($|\s(?P<date>[0-9]{2}\.[0-9]{2}\.[0-9]{4})\s(?P<time>[0-9]{2}\:[0-9]{2})$)"
text = message.text.replace('/adduser ', '') text = message.text.replace('/adduser ', '')
match = re.match(mask, text) match = re.match(mask, text)
if match is None: if match is None:

View File

@@ -27,22 +27,33 @@ class HelloHandler(Handler):
@self.router.message(Command("start")) @self.router.message(Command("start"))
async def hello(message: types.Message): async def hello(message: types.Message):
data = message.text.split() data = message.text.split()
user: Optional[User] = None
if len(data) == 2: if len(data) == 2:
code = data[1] code = data[1]
query = User.update(telegram_id=None).where( query = User.update(telegram_id=None).where(
User.telegram_id == message.from_user.id) User.telegram_id == message.from_user.id)
query.execute() query.execute()
user: Optional[User] = User.select().where( user = User.select().where(
User.code == code).first() User.code == code).first()
if user is None: if user is None:
await message.answer('Пользователь не найден, обратитесь к администратору за ссылкой!') await message.answer('Пользователь не найден, обратитесь к администратору за ссылкой!')
return return
user.telegram_id = message.from_user.id user.telegram_id = message.from_user.id
user.code = ''
user.save() user.save()
user = User.select().where(User.telegram_id == message.from_user.id).first()
if user is None:
await message.answer('Пользователь не найден, обратитесь к администратору за ссылкой!')
return
await message.answer(f"Приветствуем в боте NwXray! Здесь вы сможете получить информацию о своем подключении к NwXray.\n\n{get_subscription_info(message.from_user.id)}", await message.answer(f"Приветствуем в боте NwXray! Здесь вы сможете получить информацию о своем подключении к NwXray.\n\n{get_subscription_info(message.from_user.id)}",
reply_markup=self.__non_admin_main_menu(), parse_mode=ParseMode.MARKDOWN) reply_markup=self.__non_admin_main_menu(), parse_mode=ParseMode.MARKDOWN)
@self.router.callback_query(F.data == 'update') @self.router.callback_query(F.data == 'update')
async def update_data(callback: CallbackQuery): async def update_data(callback: CallbackQuery):
await callback.message.edit_text(get_subscription_info(callback.from_user.id), text = get_subscription_info(callback.from_user.id)
reply_markup=self.__non_admin_main_menu(), parse_mode=ParseMode.MARKDOWN) try:
await callback.message.edit_text(get_subscription_info(callback.from_user.id),
reply_markup=self.__non_admin_main_menu(), parse_mode=ParseMode.MARKDOWN)
except:
await callback.answer('Обновлений нет', show_alert=True)
return

View File

@@ -0,0 +1 @@
from nwxraybot.notifiers.subscription import setup_subscription_notifier

View File

@@ -0,0 +1,28 @@
import asyncio
import datetime
import logging
from aiogram import Bot
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from nwxraybot.models import User
# TODO: This shit is not working
async def send_subscription_notification(bot: Bot, telegram_id: int) -> None:
logging.debug(f"Sending subscription notification to {telegram_id}")
async def check_subscription_status(bot: Bot):
logging.debug('Running notifier task')
await asyncio.sleep(1)
for user in User.select():
if user.telegram_id != '':
await bot.send_message(user.telegram_id, 'Your subscription is active')
def setup_subscription_notifier(bot: Bot) -> None:
scheduler = AsyncIOScheduler(event_loop=asyncio.get_event_loop())
scheduler.add_job(check_subscription_status,
'interval', seconds=10, args=[bot])
scheduler.start()

View File

@@ -10,7 +10,7 @@ def get_subscription_info(telegram_id: str) -> str:
user: User = User.select().where(User.telegram_id == telegram_id).first() user: User = User.select().where(User.telegram_id == telegram_id).first()
if user is None: if user is None:
logging.error("[get_subscription_info]: User is not found") logging.error("[get_subscription_info]: User is not found")
return "Ошибка\!" return "Ошибка!"
date: Optional[datetime] = user.time date: Optional[datetime] = user.time
date_str = "" if date is None else f'До: { date_str = "" if date is None else f'До: {
date.strftime("%d.%m.%Y %H:%M")} МСК\n' date.strftime("%d.%m.%Y %H:%M")} МСК\n'

59
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
[[package]] [[package]]
name = "aiofiles" name = "aiofiles"
@@ -185,6 +185,33 @@ files = [
{file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"}, {file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"},
] ]
[[package]]
name = "apscheduler"
version = "3.11.0"
description = "In-process task scheduler with Cron-like capabilities"
optional = false
python-versions = ">=3.8"
files = [
{file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"},
{file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"},
]
[package.dependencies]
tzlocal = ">=3.0"
[package.extras]
doc = ["packaging", "sphinx", "sphinx-rtd-theme (>=1.3.0)"]
etcd = ["etcd3", "protobuf (<=3.21.0)"]
gevent = ["gevent"]
mongodb = ["pymongo (>=3.0)"]
redis = ["redis (>=3.0)"]
rethinkdb = ["rethinkdb (>=2.4.0)"]
sqlalchemy = ["sqlalchemy (>=1.4)"]
test = ["APScheduler[etcd,mongodb,redis,rethinkdb,sqlalchemy,tornado,zookeeper]", "PySide6", "anyio (>=4.5.2)", "gevent", "pytest", "pytz", "twisted"]
tornado = ["tornado (>=4.3)"]
twisted = ["twisted"]
zookeeper = ["kazoo"]
[[package]] [[package]]
name = "attrs" name = "attrs"
version = "24.3.0" version = "24.3.0"
@@ -882,6 +909,34 @@ files = [
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
] ]
[[package]]
name = "tzdata"
version = "2024.2"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
files = [
{file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
{file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
]
[[package]]
name = "tzlocal"
version = "5.2"
description = "tzinfo object for the local timezone"
optional = false
python-versions = ">=3.8"
files = [
{file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"},
{file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"},
]
[package.dependencies]
tzdata = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"]
[[package]] [[package]]
name = "uvloop" name = "uvloop"
version = "0.21.0" version = "0.21.0"
@@ -1085,4 +1140,4 @@ propcache = ">=0.2.0"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.13" python-versions = "^3.13"
content-hash = "9b455b4a27b4038063fad5d023c0d2af06d7277a9b7ee02cc2d366e458f195b4" content-hash = "57654abddded59b21f10fc936a3f75bf5897bc0fd9ebcc81da38a5699d7ca90a"

View File

@@ -14,6 +14,7 @@ peewee = "^3.17.8"
uvloop = "^0.21.0" uvloop = "^0.21.0"
jurigged = "^0.6.0" jurigged = "^0.6.0"
psycopg2-binary = "^2.9.10" psycopg2-binary = "^2.9.10"
apscheduler = "^3.11.0"
[build-system] [build-system]