add files

This commit is contained in:
root committed 2025-08-16 15:08:00 +00:00
1 parent 4b06cd8a77
commit 8f2efee524
8 files changed
+291 -92

No files matched your search

+88 -52
View File
@@ -1,93 +1,124 @@
from app.core.logger import make_log, logger
from app.core.models._telegram import Wrapped_CBotChat
from app.core.models.user import User
from app.core.storage import db_session
from aiogram import BaseMiddleware, types
from app.core.models.messages import KnownTelegramMessage
from datetime import datetime
# Bot handlers historically use synchronous SQLAlchemy patterns.
# Keep a dedicated sync engine/session for bot middleware to preserve legacy behavior.
import re
from typing import Optional
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
from app.core.config import get_settings
_SYNC_ENGINE = None
_SYNC_FACTORY: Optional[sessionmaker] = None
def _to_sync_dsn(async_dsn: str) -> str:
# Convert postgresql+asyncpg:// to postgresql+psycopg2:// for synchronous engine
return re.sub(r"\+asyncpg", "+psycopg2", async_dsn)
def _ensure_sync_session() -> Session:
global _SYNC_ENGINE, _SYNC_FACTORY
if _SYNC_ENGINE is None or _SYNC_FACTORY is None:
settings = get_settings()
dsn = _to_sync_dsn(settings.DATABASE_URL)
_SYNC_ENGINE = create_engine(dsn, pool_pre_ping=True, future=True)
_SYNC_FACTORY = sessionmaker(bind=_SYNC_ENGINE, autocommit=False, autoflush=True)
return _SYNC_FACTORY()
class UserDataMiddleware(BaseMiddleware):
async def __call__(self, handler, event, data):
update_body = event.message or event.callback_query or event.inline_query or event.pre_checkout_query
if not update_body:
return
if update_body.from_user.is_bot is True:
async def __call__(self, handler, event, data):
update_body = event.message or event.callback_query or getattr(event, 'inline_query', None) or getattr(event, 'pre_checkout_query', None)
if not update_body or getattr(update_body, 'from_user', None) is None:
return
if getattr(update_body.from_user, 'is_bot', False):
return
user_id = update_body.from_user.id
assert user_id >= 1
# TODO: maybe make users cache
# Use sync session for bot handlers compatibility
from app.core.models.user.user import User as DbUser
from app.core.models.messages import KnownTelegramMessage as DbKnownMsg
from app.core.logging import logger as app_logger
with db_session(auto_commit=False) as session:
session = _ensure_sync_session()
try:
# Load or create user
try:
user = session.query(User).filter_by(telegram_id=user_id).first()
except BaseException as e:
logger.error(f"Error when middleware getting user: {e}")
user = session.query(DbUser).filter(DbUser.telegram_id == user_id).first()
except Exception as e:
await app_logger.aerror("Middleware get user failed", error=str(e))
user = None
if user is None:
logger.debug(f"User {user_id} not found. Creating new user")
user = User(
await app_logger.adebug("Creating new user", telegram_id=user_id)
user = DbUser(
telegram_id=user_id,
username=update_body.from_user.username,
lang_code='en',
last_use=datetime.now(),
meta=dict(first_name=update_body.from_user.first_name or '',
last_name=update_body.from_user.last_name or '', username=update_body.from_user.username,
language_code=update_body.from_user.language_code,
is_premium=update_body.from_user.is_premium),
created=datetime.now()
username=getattr(update_body.from_user, 'username', None),
language_code=getattr(update_body.from_user, 'language_code', 'en'),
meta={
'first_name': getattr(update_body.from_user, 'first_name', '') or '',
'last_name': getattr(update_body.from_user, 'last_name', '') or '',
'username': getattr(update_body.from_user, 'username', None),
'language_code': getattr(update_body.from_user, 'language_code', None),
'is_premium': getattr(update_body.from_user, 'is_premium', False),
}
)
session.add(user)
session.commit()
else:
if user.username != update_body.from_user.username:
user.username = update_body.from_user.username
updated_meta_fields = {}
if user.meta.get('first_name') != update_body.from_user.first_name:
updated_meta_fields['first_name'] = update_body.from_user.first_name
if user.meta.get('last_name') != update_body.from_user.last_name:
updated_meta_fields['last_name'] = update_body.from_user.last_name
user.meta = {
**user.meta,
**updated_meta_fields
}
user.last_use = datetime.now()
session.commit()
# Update username/metadata
changed = False
if user.username != getattr(update_body.from_user, 'username', None):
user.username = getattr(update_body.from_user, 'username', None)
changed = True
meta = dict(user.meta or {})
if meta.get('first_name') != getattr(update_body.from_user, 'first_name', None):
meta['first_name'] = getattr(update_body.from_user, 'first_name', None)
changed = True
if meta.get('last_name') != getattr(update_body.from_user, 'last_name', None):
meta['last_name'] = getattr(update_body.from_user, 'last_name', None)
changed = True
user.meta = meta
user.last_activity = datetime.utcnow()
if changed:
session.commit()
data['user'] = user
# Pass sync session for routers expecting .query()
data['db_session'] = session
# chat_wrap can work with sync sessions too
data['chat_wrap'] = Wrapped_CBotChat(data['bot'], chat_id=user_id, db_session=session, user=user)
data['memory'] = data['dispatcher']._s_memory
# De-duplicate known messages
if getattr(update_body, 'text', None):
message_type = 'common'
if update_body.text.startswith('/start'):
message_type = 'start_command'
if session.query(KnownTelegramMessage).filter_by(
chat_id=update_body.chat.id,
message_id=update_body.message_id,
from_user=True
).first():
make_log("UserDataMiddleware", f"Message {update_body.message_id} already processed", level='debug')
existed = session.query(DbKnownMsg).filter(
(DbKnownMsg.chat_id == update_body.chat.id) &
(DbKnownMsg.message_id == update_body.message_id) &
(DbKnownMsg.from_user == True)
).first()
if existed:
await app_logger.adebug("Message already processed", message_id=update_body.message_id)
return
new_message = KnownTelegramMessage(
type=message_type,
new_message = DbKnownMsg(
type='start_command' if str(update_body.text).startswith('/start') else 'common',
bot_id=data['chat_wrap'].bot_id,
chat_id=update_body.chat.id,
message_id=update_body.message_id,
from_user=True,
from_telegram_id=user_id,
created=datetime.now(),
created=datetime.utcnow(),
meta={}
)
session.add(new_message)
@@ -95,3 +126,8 @@ class UserDataMiddleware(BaseMiddleware):
result = await handler(event, data)
return result
finally:
try:
session.close()
except Exception:
pass
+67
View File
@@ -0,0 +1,67 @@
"""
Lightweight Telegram bot poller to ensure the bot responds.
Does not depend on legacy sync DB middleware. Provides minimal /start handler.
"""
import asyncio
import logging
from typing import Optional
from aiogram import Bot, Dispatcher, Router, types
from aiogram.filters import Command
from app.core.config import get_settings
logger = logging.getLogger(__name__)
def build_router(name: str) -> Router:
r = Router(name=name)
@r.message(Command("start"))
async def cmd_start(message: types.Message):
await message.answer(
"MY Network bot online. Use /help to get options.")
@r.message(Command("help"))
async def cmd_help(message: types.Message):
await message.answer("Available: /start, /help")
return r
async def _run_single_bot(token: str, name: str):
bot = Bot(token)
dp = Dispatcher()
dp.include_router(build_router(name))
logger.info("Starting Telegram bot polling", extra={"name": name})
try:
await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types())
except Exception as e:
logger.error("Bot polling failed", extra={"name": name, "error": str(e)})
await bot.session.close()
async def start_bots_if_configured() -> list[asyncio.Task]:
settings = get_settings()
tasks: list[asyncio.Task] = []
# Prefer TELEGRAM_WEBHOOK_ENABLED to avoid double-processing
webhook_enabled = bool(getattr(settings, 'TELEGRAM_WEBHOOK_ENABLED', False))
if webhook_enabled:
logger.info("Telegram webhook enabled; skipping polling start")
return tasks
# Main bot
token: Optional[str] = getattr(settings, 'TELEGRAM_API_KEY', None)
if token:
tasks.append(asyncio.create_task(_run_single_bot(token, 'main')))
# Client bot
ct: Optional[str] = getattr(settings, 'CLIENT_TELEGRAM_API_KEY', None)
if ct:
tasks.append(asyncio.create_task(_run_single_bot(ct, 'client')))
if tasks:
logger.info("Telegram bots polling started", extra={"count": len(tasks)})
else:
logger.info("No Telegram tokens configured; polling not started")
return tasks