137 lines
6.8 KiB
Python
137 lines
6.8 KiB
Python
import sys
|
||
import asyncio
|
||
if sys.platform.startswith('win'):
|
||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||
import logging
|
||
import os
|
||
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
||
from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackQueryHandler, ConversationHandler, ContextTypes
|
||
from dotenv import load_dotenv
|
||
from db import AsyncSessionLocal, init_db
|
||
from models import Admin, Channel, Group, Button
|
||
from asyncio import run as sync_to_async
|
||
from handlers.admin_panel import admin_panel_conv
|
||
load_dotenv()
|
||
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
|
||
|
||
logging.basicConfig(level=logging.INFO)
|
||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
import asyncio
|
||
from telegram.ext import CommandHandler
|
||
from sqlalchemy import select
|
||
from datetime import datetime
|
||
from db import AsyncSessionLocal
|
||
from models import ChannelAccess
|
||
from handlers.permissions import get_or_create_admin, token_hash
|
||
|
||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
args = (context.args or [])
|
||
if args and args[0].startswith("sch_"):
|
||
# формат: sch_<invite_id>_<token>
|
||
try:
|
||
_, sid, token = args[0].split("_", 2)
|
||
invite_id = int(sid)
|
||
except Exception:
|
||
await update.message.reply_text("Неверная ссылка приглашения.")
|
||
return
|
||
|
||
async with AsyncSessionLocal() as session:
|
||
me = await get_or_create_admin(session, update.effective_user.id)
|
||
res = await session.execute(select(ChannelAccess).where(ChannelAccess.id == invite_id))
|
||
acc = res.scalar_one_or_none()
|
||
if not acc or acc.status != "pending":
|
||
await update.message.reply_text("Приглашение не найдено или уже активировано/отозвано.")
|
||
return
|
||
if acc.expires_at and acc.expires_at < datetime.utcnow():
|
||
await update.message.reply_text("Срок действия приглашения истёк.")
|
||
return
|
||
if token_hash(token) != acc.token_hash:
|
||
await update.message.reply_text("Неверный токен приглашения.")
|
||
return
|
||
|
||
acc.invited_admin_id = me.id
|
||
acc.accepted_at = datetime.utcnow()
|
||
acc.status = "active"
|
||
await session.commit()
|
||
|
||
await update.message.reply_text("Доступ к каналу успешно активирован. Можно постить через /new_post.")
|
||
return
|
||
|
||
|
||
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||
help_text = (
|
||
"<b>Справка по командам бота:</b>\n\n"
|
||
"<b>/start</b> — регистрация пользователя как администратора.\n"
|
||
"<b>/help</b> — показать это подробное описание команд.\n\n"
|
||
"<b>/add_channel <название> <ссылка></b> — добавить канал для публикаций. Пример: /add_channel MyChannel @my_channel\n"
|
||
"<b>/add_group <название> <ссылка></b> — добавить группу для публикаций. Пример: /add_group MyGroup @my_group\n"
|
||
"<b>/add_button</b> — добавить кнопку к выбранному каналу или группе. Запустит диалог выбора и добавления.\n"
|
||
"<b>/edit_button <название> <новое_название> <новая_ссылка></b> — изменить кнопку. Пример: /edit_button old new https://site.ru\n"
|
||
"<b>/del_button <название></b> — удалить кнопку по названию.\n"
|
||
"<b>/new_post</b> — создать новый пост (картинка + текст + выбор канала/группы + кнопки).\n"
|
||
"<b>/group_buttons</b> — показать и настроить кнопки для группы.\n"
|
||
"<b>/channel_buttons</b> — показать и настроить кнопки для канала.\n\n"
|
||
"<b>Примеры использования:</b>\n"
|
||
"- /add_channel Новости @news_channel\n"
|
||
"- /add_group Чат @chat_group\n"
|
||
"- /edit_button Подписка Subscribe https://subscribe.ru\n"
|
||
"- /del_button Subscribe\n\n"
|
||
"<b>Порядок работы:</b>\n"
|
||
"1. Добавьте канал или группу.\n"
|
||
"2. Добавьте кнопки для них.\n"
|
||
"3. Создайте пост через /new_post и выберите, куда отправить.\n"
|
||
"4. Управляйте кнопками через /group_buttons и /channel_buttons.\n\n"
|
||
"Если возникли вопросы — используйте /help или обратитесь к администратору."
|
||
)
|
||
if update.message:
|
||
await update.message.reply_text(help_text, parse_mode='HTML')
|
||
|
||
# Импорт обработчиков
|
||
from handlers.add_channel import add_channel_conv
|
||
from handlers.add_group import add_group_conv
|
||
from handlers.add_button import add_button_conv
|
||
from handlers.new_post import new_post_conv
|
||
from handlers.group_buttons import group_buttons_conv
|
||
from handlers.channel_buttons import channel_buttons_conv
|
||
from handlers.edit_button import edit_button
|
||
from handlers.del_button import del_button
|
||
from handlers.share_channel import share_channel_conv
|
||
|
||
|
||
|
||
|
||
def main():
|
||
if not TELEGRAM_TOKEN:
|
||
print("Ошибка: TELEGRAM_TOKEN не найден в переменных окружения.")
|
||
return
|
||
# sync_to_async(init_db())
|
||
application = Application.builder().token(TELEGRAM_TOKEN).build()
|
||
application.add_handler(CommandHandler('start', start))
|
||
application.add_handler(CommandHandler('help', help_command))
|
||
application.add_handler(add_channel_conv)
|
||
application.add_handler(add_group_conv)
|
||
application.add_handler(add_button_conv)
|
||
application.add_handler(new_post_conv)
|
||
application.add_handler(group_buttons_conv)
|
||
application.add_handler(channel_buttons_conv)
|
||
application.add_handler(CommandHandler('edit_button', edit_button))
|
||
application.add_handler(CommandHandler('del_button', del_button))
|
||
application.add_handler(admin_panel_conv)
|
||
application.add_handler(share_channel_conv)
|
||
import sys
|
||
import asyncio
|
||
if sys.platform.startswith('win'):
|
||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||
try:
|
||
loop = asyncio.get_event_loop()
|
||
except RuntimeError:
|
||
loop = asyncio.new_event_loop()
|
||
asyncio.set_event_loop(loop)
|
||
application.run_polling()
|
||
|
||
if __name__ == "__main__":
|
||
main()
|
||
|