Compare commits
3 Commits
af837b54be
...
7d50cf95cf
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d50cf95cf | |||
| b5a893e5aa | |||
| 9ee8e43fb4 |
@@ -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
12
main.py
@@ -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))
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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):
|
||||||
|
text = get_subscription_info(callback.from_user.id)
|
||||||
|
try:
|
||||||
await callback.message.edit_text(get_subscription_info(callback.from_user.id),
|
await callback.message.edit_text(get_subscription_info(callback.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)
|
||||||
|
except:
|
||||||
|
await callback.answer('Обновлений нет', show_alert=True)
|
||||||
|
return
|
||||||
|
|||||||
1
nwxraybot/notifiers/__init__.py
Normal file
1
nwxraybot/notifiers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from nwxraybot.notifiers.subscription import setup_subscription_notifier
|
||||||
28
nwxraybot/notifiers/subscription.py
Normal file
28
nwxraybot/notifiers/subscription.py
Normal 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()
|
||||||
@@ -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
59
poetry.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user