mirror of
https://github.com/MrSedan/neuro-reply-bot-reworked.git
synced 2026-01-14 13:39:42 +03:00
Some refactoring and started second bot
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
TOKEN=
|
TOKEN=
|
||||||
|
|
||||||
|
PROXY_TOKEN=
|
||||||
|
|
||||||
API_URL="http://localhost:3000"
|
API_URL="http://localhost:3000"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Any, List
|
from typing import List
|
||||||
|
|
||||||
from aiogram import Bot, F, Router, types
|
from aiogram import Bot, F, types
|
||||||
from aiogram.filters import Command
|
from aiogram.filters import Command
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.utils.media_group import MediaGroupBuilder
|
from aiogram.utils.media_group import MediaGroupBuilder
|
||||||
@@ -9,6 +9,7 @@ import neuroapi.types as neuroTypes
|
|||||||
from handlers.filters.new_post import (ChangePosts, NewPostFilter,
|
from handlers.filters.new_post import (ChangePosts, NewPostFilter,
|
||||||
NewSoloPostFilter)
|
NewSoloPostFilter)
|
||||||
from handlers.filters.reply_to_user import ReplyToUser
|
from handlers.filters.reply_to_user import ReplyToUser
|
||||||
|
from handlers.handler import Handler
|
||||||
from handlers.middlewares.user import AdminMiddleware
|
from handlers.middlewares.user import AdminMiddleware
|
||||||
from handlers.states.change_post import ChangePost
|
from handlers.states.change_post import ChangePost
|
||||||
from neuroapi import neuroapi
|
from neuroapi import neuroapi
|
||||||
@@ -23,13 +24,9 @@ def get_post_info(post: neuroTypes.Post, post_id: int) -> str:
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
class Admin_commands:
|
class AdminCommands(Handler):
|
||||||
bot: Bot
|
|
||||||
router: Router
|
|
||||||
|
|
||||||
def __init__(self, bot: Bot) -> None:
|
def __init__(self, bot: Bot) -> None:
|
||||||
self.bot = bot
|
super().__init__(bot)
|
||||||
self.router = Router()
|
|
||||||
self.router.message.middleware(AdminMiddleware())
|
self.router.message.middleware(AdminMiddleware())
|
||||||
|
|
||||||
@self.router.message(NewPostFilter())
|
@self.router.message(NewPostFilter())
|
||||||
@@ -210,9 +207,4 @@ class Admin_commands:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def __call__(self, *args: Any, **kwds: Any) -> Router:
|
|
||||||
return self.router
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot: Bot) -> Router:
|
|
||||||
return Admin_commands(bot)()
|
|
||||||
|
|||||||
16
handlers/handler.py
Normal file
16
handlers/handler.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from aiogram import Bot, Router
|
||||||
|
|
||||||
|
|
||||||
|
class Handler:
|
||||||
|
bot: Bot
|
||||||
|
router: Router
|
||||||
|
|
||||||
|
def __init__(self, bot: Bot) -> None:
|
||||||
|
assert isinstance(bot, Bot)
|
||||||
|
self.bot = bot
|
||||||
|
self.router = Router()
|
||||||
|
|
||||||
|
def __call__(self) -> Router:
|
||||||
|
return self.router
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
from typing import Any, List
|
from typing import List
|
||||||
|
|
||||||
from aiogram import Bot, F, Router, types
|
from aiogram import Bot, F, types
|
||||||
|
|
||||||
|
from handlers.handler import Handler
|
||||||
from neuroapi import neuroapi
|
from neuroapi import neuroapi
|
||||||
|
|
||||||
from neuroapi.types import Admin as AdminType
|
from neuroapi.types import Admin as AdminType
|
||||||
|
|
||||||
|
|
||||||
class User_commands:
|
class UserCommands(Handler):
|
||||||
bot: Bot
|
|
||||||
router: Router
|
|
||||||
|
|
||||||
def __init__(self, bot: Bot) -> None:
|
def __init__(self, bot: Bot) -> None:
|
||||||
self.bot = bot
|
super().__init__(bot)
|
||||||
self.router = Router()
|
|
||||||
|
|
||||||
@self.router.message(F.chat.type == 'private')
|
@self.router.message(F.chat.type == 'private')
|
||||||
async def forward_post(message: types.Message):
|
async def forward_post(message: types.Message):
|
||||||
admins: List[AdminType] = await neuroapi.admin.get()
|
admins: List[AdminType] = await neuroapi.admin.get()
|
||||||
@@ -27,9 +24,3 @@ class User_commands:
|
|||||||
canReply = False
|
canReply = False
|
||||||
await message.reply('Ваше сообщение было отправлено администраторам'+('' if canReply else '\nНо они не смогут вам ответить из-за ваших настроек конфиденциальности.'))
|
await message.reply('Ваше сообщение было отправлено администраторам'+('' if canReply else '\nНо они не смогут вам ответить из-за ваших настроек конфиденциальности.'))
|
||||||
|
|
||||||
def __call__(self, *args: Any, **kwds: Any) -> Router:
|
|
||||||
return self.router
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot: Bot) -> Router:
|
|
||||||
return User_commands(bot)()
|
|
||||||
|
|||||||
75
main.py
75
main.py
@@ -1,37 +1,68 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import os
|
import signal
|
||||||
import sys
|
import sys
|
||||||
from os.path import dirname, join
|
|
||||||
|
|
||||||
# import aioschedule as schedule
|
# import aioschedule as schedule
|
||||||
import dotenv
|
from aiogram import Bot, Dispatcher
|
||||||
from aiogram import Bot, Dispatcher, types
|
|
||||||
from aiogram.filters import CommandStart
|
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
from handlers.admin_commands import AdminCommands
|
||||||
|
from handlers.handler import Handler
|
||||||
|
from handlers.user_commands import UserCommands
|
||||||
|
from neuroapi.config import Config
|
||||||
|
|
||||||
token = os.getenv('TOKEN')
|
|
||||||
|
|
||||||
bot = Bot(token)
|
class NeuroApiBot:
|
||||||
dp = Dispatcher()
|
bot: Bot
|
||||||
|
dp: Dispatcher
|
||||||
|
|
||||||
|
_instances = {}
|
||||||
|
|
||||||
|
def __init__(self, token: str) -> None:
|
||||||
|
self.bot = Bot(token)
|
||||||
|
self.dp = Dispatcher()
|
||||||
|
self._instances
|
||||||
|
|
||||||
|
def __new__(cls, token: str) -> 'NeuroApiBot':
|
||||||
|
assert isinstance(token, str)
|
||||||
|
if token not in cls._instances:
|
||||||
|
cls._instances[token] = super(NeuroApiBot, cls).__new__(cls)
|
||||||
|
return cls._instances[token]
|
||||||
|
|
||||||
|
def include_router(self, *routerClasses: Handler) -> None:
|
||||||
|
for routerClass in routerClasses:
|
||||||
|
assert issubclass(routerClass, Handler)
|
||||||
|
self.dp.include_routers(routerClass(self.bot)())
|
||||||
|
|
||||||
|
async def start(self, skip_updates=True):
|
||||||
|
await self.dp.start_polling(self.bot, skip_updates=skip_updates)
|
||||||
|
|
||||||
@dp.message(CommandStart())
|
async def delay_bot()->None:
|
||||||
async def start_message(message: types.Message):
|
if Config().token is None:
|
||||||
await message.answer('Добро пожаловать в бота ')
|
print('Delay bot needs token in environment')
|
||||||
|
return
|
||||||
handlers_dir = join(dirname(__file__), 'handlers')
|
bot = NeuroApiBot(Config().token)
|
||||||
|
bot.include_router(AdminCommands, UserCommands)
|
||||||
for filename in os.listdir(handlers_dir):
|
await bot.start()
|
||||||
if filename.endswith('.py'):
|
|
||||||
module_name = filename[:-3]
|
|
||||||
setup = __import__(f"handlers.{module_name}", locals(), globals(), ['setup']).setup
|
|
||||||
dp.include_router(setup(bot))
|
|
||||||
|
|
||||||
|
async def proxy_bot()->None:
|
||||||
|
if Config().proxy_token is None:
|
||||||
|
print('Proxy bot needs token in environment')
|
||||||
|
return
|
||||||
|
bot = NeuroApiBot(Config().proxy_token)
|
||||||
|
bot.include_router()
|
||||||
|
await bot.start()
|
||||||
|
|
||||||
async def main() -> None:
|
async def main() -> None:
|
||||||
await dp.start_polling(bot, skip_updates=True)
|
tasks = [asyncio.create_task(delay_bot()), asyncio.create_task(proxy_bot())]
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
|
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
|
||||||
asyncio.run(main())
|
loop = asyncio.get_event_loop()
|
||||||
|
for signame in ('SIGINT', 'SIGTERM'):
|
||||||
|
loop.add_signal_handler(getattr(signal, signame), loop.stop)
|
||||||
|
try:
|
||||||
|
asyncio.run(main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
@@ -1,21 +1,15 @@
|
|||||||
import os
|
import os
|
||||||
import tomllib
|
import tomllib
|
||||||
from typing import List, Self, TypeVar
|
from typing import List, Optional
|
||||||
|
|
||||||
from attr import dataclass
|
from attr import dataclass
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
from neuroapi.types import Singleton
|
||||||
|
|
||||||
from .types._helpers import *
|
from .types._helpers import *
|
||||||
|
|
||||||
|
|
||||||
class _Singleton:
|
|
||||||
_instances = {}
|
|
||||||
|
|
||||||
def __new__(cls) -> Self:
|
|
||||||
if cls not in cls._instances:
|
|
||||||
cls._instances[cls] = super(_Singleton, cls).__new__(cls)
|
|
||||||
return cls._instances[cls]
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Settings:
|
class Settings:
|
||||||
time: List[str]
|
time: List[str]
|
||||||
@@ -31,13 +25,21 @@ class Settings:
|
|||||||
result['time'] = from_list(from_str, self.time)
|
result['time'] = from_list(from_str, self.time)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class Config(_Singleton):
|
class Config(Singleton):
|
||||||
api_url: str
|
api_url: str
|
||||||
settings: Settings
|
settings: Settings
|
||||||
|
token: Optional[str]
|
||||||
|
proxy_token: Optional[str]
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))
|
load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))
|
||||||
if not os.path.exists(os.path.join(os.path.dirname(__file__), '..', 'settings.toml')): raise Exception('Settings.toml must be in root folder')
|
if not os.path.exists(os.path.join(os.path.dirname(__file__), '..', 'settings.toml')): raise Exception('Settings.toml must be in root folder')
|
||||||
with open(os.path.join(os.path.dirname(__file__), '..', 'settings.toml'), 'rb') as f:
|
with open(os.path.join(os.path.dirname(__file__), '..', 'settings.toml'), 'rb') as f:
|
||||||
settings = tomllib.load(f)
|
settings = tomllib.load(f)
|
||||||
self.settings = Settings.from_dict(settings)
|
self.settings = Settings.from_dict(settings)
|
||||||
self.api_url = os.environ.get('API_URL')
|
self.api_url = os.environ.get('API_URL')
|
||||||
|
self.token = os.environ.get('TOKEN')
|
||||||
|
if self.token == '':
|
||||||
|
self.token = None
|
||||||
|
self.proxy_token = os.environ.get('PROXY_TOKEN')
|
||||||
|
if self.proxy_token == '':
|
||||||
|
self.proxy_token = None
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
from ._post import Post
|
|
||||||
from ._image import Image
|
|
||||||
from ._admin import Admin
|
from ._admin import Admin
|
||||||
|
from ._image import Image
|
||||||
|
from ._post import Post
|
||||||
|
from ._singleton import Singleton
|
||||||
from ._user import User
|
from ._user import User
|
||||||
|
|||||||
10
neuroapi/types/_singleton.py
Normal file
10
neuroapi/types/_singleton.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from typing import Self
|
||||||
|
|
||||||
|
|
||||||
|
class Singleton:
|
||||||
|
_instances = {}
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs) -> Self:
|
||||||
|
if cls not in cls._instances:
|
||||||
|
cls._instances[cls] = super(Singleton, cls).__new__(cls)
|
||||||
|
return cls._instances[cls]
|
||||||
Reference in New Issue
Block a user