Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f18cd78ad5 | |||
| 86f7c65697 | |||
| 9793648ee3 |
10
db.py
10
db.py
@@ -1,10 +1,12 @@
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import declarative_base
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite+aiosqlite:///bot.db")
|
||||
|
||||
if DATABASE_URL.startswith("sqlite+aiosqlite:///"):
|
||||
@@ -73,3 +75,9 @@ async def init_db():
|
||||
print(f"Созданы таблицы: {', '.join(tables)}")
|
||||
else:
|
||||
print("База данных уже существует и содержит таблицы, создание пропущено.")
|
||||
|
||||
async def log_action(admin_id, action, details=""):
|
||||
async with AsyncSessionLocal() as session:
|
||||
log = ActionLog(admin_id=admin_id, action=action, details=details)
|
||||
session.add(log)
|
||||
await session.commit()
|
||||
@@ -1,3 +1,6 @@
|
||||
|
||||
|
||||
|
||||
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, filters
|
||||
from db import AsyncSessionLocal
|
||||
@@ -90,8 +93,27 @@ async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
except Exception as e:
|
||||
if update.message:
|
||||
await update.message.reply_text(f'Ошибка при добавлении кнопки: {e}')
|
||||
try:
|
||||
type_, obj_id = target.split('_', 1)
|
||||
obj_id = int(obj_id)
|
||||
if type_ == 'channel':
|
||||
button = Button(name=name, url=url, channel_id=obj_id)
|
||||
elif type_ == 'group':
|
||||
button = Button(name=name, url=url, group_id=obj_id)
|
||||
else:
|
||||
await update.message.reply_text('Ошибка: неверный тип объекта.')
|
||||
session.close()
|
||||
return ConversationHandler.END
|
||||
session.add(button)
|
||||
session.commit()
|
||||
from db import log_action
|
||||
user_id = update.effective_user.id if update.effective_user else None
|
||||
log_action(user_id, "add_button", f"type={type_}, obj_id={obj_id}, name={name}, url={url}")
|
||||
await update.message.reply_text('Кнопка добавлена.')
|
||||
except Exception as e:
|
||||
await update.message.reply_text(f'Ошибка при добавлении кнопки: {e}')
|
||||
finally:
|
||||
await session.close()
|
||||
session.close()
|
||||
return ConversationHandler.END
|
||||
|
||||
add_button_conv = ConversationHandler(
|
||||
@@ -102,4 +124,4 @@ add_button_conv = ConversationHandler(
|
||||
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
|
||||
},
|
||||
fallbacks=[]
|
||||
)
|
||||
)
|
||||
@@ -57,6 +57,16 @@ async def input_channel_link(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
||||
return ConversationHandler.END
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
<<<<<<< HEAD
|
||||
channel = Channel(name=name, link=link)
|
||||
session.add(channel)
|
||||
await session.commit()
|
||||
from db import log_action
|
||||
user_id = update.effective_user.id if update.effective_user else None
|
||||
await log_action(user_id, "add_channel", f"name={name}, link={link}")
|
||||
if update.message:
|
||||
await update.message.reply_text(f'Канал "{name}" добавлен.')
|
||||
=======
|
||||
admin = await _get_or_create_admin(session, user.id)
|
||||
|
||||
# если канал уже есть — обновим имя и владельца
|
||||
@@ -72,6 +82,7 @@ async def input_channel_link(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
||||
session.add(channel)
|
||||
await session.commit()
|
||||
await update.message.reply_text(f'Канал "{name}" добавлен и привязан к вашему админ-аккаунту.')
|
||||
>>>>>>> main
|
||||
return ConversationHandler.END
|
||||
|
||||
add_channel_conv = ConversationHandler(
|
||||
|
||||
@@ -133,6 +133,16 @@ async def input_group_link(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
return ConversationHandler.END
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
<<<<<<< HEAD
|
||||
group = Group(name=name, link=link)
|
||||
session.add(group)
|
||||
await session.commit()
|
||||
from db import log_action
|
||||
user_id = update.effective_user.id if update.effective_user else None
|
||||
log_action(user_id, "add_group", f"name={name}, link={link}")
|
||||
if update.message:
|
||||
await update.message.reply_text(f'Группа "{name}" добавлена.')
|
||||
=======
|
||||
# гарантируем наличие админа
|
||||
admin = await _get_or_create_admin(session, user.id)
|
||||
|
||||
@@ -153,6 +163,7 @@ async def input_group_link(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
await session.commit()
|
||||
await update.message.reply_text(f'Группа "{name}" добавлена и привязана к вашему админ-аккаунту.')
|
||||
|
||||
>>>>>>> main
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from telegram import Update
|
||||
from telegram.ext import CommandHandler, ContextTypes
|
||||
from db import AsyncSessionLocal
|
||||
from db import AsyncSessionLocal, log_action
|
||||
from models import Button
|
||||
|
||||
async def del_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
@@ -10,9 +10,8 @@ async def del_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
await update.message.reply_text('Используйте: /del_button <название>')
|
||||
return
|
||||
name = args[0]
|
||||
session = AsyncSessionLocal()
|
||||
try:
|
||||
from sqlalchemy import select
|
||||
async with AsyncSessionLocal() as session:
|
||||
result = await session.execute(select(Button).where(Button.name == name))
|
||||
button = result.scalar_one_or_none()
|
||||
if not button:
|
||||
@@ -21,7 +20,7 @@ async def del_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
return
|
||||
await session.delete(button)
|
||||
await session.commit()
|
||||
user_id = update.effective_user.id if update.effective_user else None
|
||||
await log_action(user_id, "del_button", f"name={name}")
|
||||
if update.message:
|
||||
await update.message.reply_text(f'Кнопка "{name}" удалена.')
|
||||
finally:
|
||||
await session.close()
|
||||
await update.message.reply_text(f'Кнопка \"{name}\" удалена.')
|
||||
|
||||
@@ -20,6 +20,9 @@ async def edit_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
button.name = new_name
|
||||
button.url = new_url
|
||||
await session.commit()
|
||||
from db import log_action
|
||||
user_id = update.effective_user.id if update.effective_user else None
|
||||
log_action(user_id, "edit_button", f"old_name={name}, new_name={new_name}, new_url={new_url}")
|
||||
if update.message:
|
||||
await update.message.reply_text(f'Кнопка "{name}" изменена.')
|
||||
finally:
|
||||
|
||||
26
handlers/invite_admin.py
Normal file
26
handlers/invite_admin.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from telegram import Update
|
||||
from telegram.ext import CommandHandler, ContextTypes
|
||||
from db import AsyncSessionLocal, log_action
|
||||
from models import Admin
|
||||
|
||||
async def invite_admin(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
args = context.args
|
||||
if len(args) < 3:
|
||||
await update.message.reply_text("Неверная ссылка.")
|
||||
return
|
||||
channel_id, inviter_id, token = args
|
||||
user_id = update.effective_user.id
|
||||
session = AsyncSessionLocal()
|
||||
admin = session.query(Admin).filter_by(invite_token=token, channel_id=channel_id).first()
|
||||
if not admin:
|
||||
await update.message.reply_text("Ссылка недействительна.")
|
||||
session.close()
|
||||
return
|
||||
new_admin = Admin(tg_id=user_id, channel_id=channel_id, inviter_id=inviter_id)
|
||||
session.add(new_admin)
|
||||
session.commit()
|
||||
session.close()
|
||||
await update.message.reply_text("Вы добавлены как администратор канала!")
|
||||
log_action(user_id, "invite_admin", f"channel_id={channel_id}, inviter_id={inviter_id}")
|
||||
|
||||
invite_admin_handler = CommandHandler("invite_admin", invite_admin)
|
||||
@@ -13,9 +13,13 @@ from telegram.error import BadRequest
|
||||
from sqlalchemy import select as sa_select
|
||||
|
||||
from db import AsyncSessionLocal
|
||||
<<<<<<< HEAD
|
||||
from models import Channel, Group, Button, Admin
|
||||
=======
|
||||
from models import Channel, Group
|
||||
from .permissions import get_or_create_admin, list_channels_for_admin, has_scope_on_channel, SCOPE_POST
|
||||
from models import Channel, Group, Button
|
||||
>>>>>>> main
|
||||
|
||||
SELECT_MEDIA, SELECT_TEXT, SELECT_TARGET = range(3)
|
||||
|
||||
|
||||
23
handlers/share_bot.py
Normal file
23
handlers/share_bot.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import secrets
|
||||
from telegram import Update
|
||||
from telegram.ext import CommandHandler, ContextTypes
|
||||
from db import AsyncSessionLocal, log_action
|
||||
from models import Admin
|
||||
|
||||
async def share_bot(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
user_id = update.effective_user.id
|
||||
channel_id = context.user_data.get("channel_id")
|
||||
if not channel_id:
|
||||
await update.message.reply_text("Сначала выберите канал через /channel_buttons.")
|
||||
return
|
||||
token = secrets.token_urlsafe(16)
|
||||
session = AsyncSessionLocal()
|
||||
admin = Admin(tg_id=user_id, channel_id=channel_id, inviter_id=user_id, invite_token=token)
|
||||
session.add(admin)
|
||||
session.commit()
|
||||
session.close()
|
||||
link = f"/invite_admin {channel_id} {user_id} {token}"
|
||||
await update.message.reply_text(f"Инвайт-ссылка для нового администратора:\n{link}")
|
||||
log_action(user_id, "share_bot", f"channel_id={channel_id}, token={token}")
|
||||
|
||||
share_bot_handler = CommandHandler("share_bot", share_bot)
|
||||
7
main.py
7
main.py
@@ -1,3 +1,5 @@
|
||||
from handlers.share_bot import share_bot_handler
|
||||
from handlers.invite_admin import invite_admin_handler
|
||||
import sys
|
||||
import asyncio
|
||||
if sys.platform.startswith('win'):
|
||||
@@ -129,7 +131,12 @@ def main():
|
||||
application.add_handler(CommandHandler('edit_button', edit_button))
|
||||
application.add_handler(CommandHandler('del_button', del_button))
|
||||
application.add_handler(admin_panel_conv)
|
||||
<<<<<<< HEAD
|
||||
application.add_handler(share_bot_handler)
|
||||
application.add_handler(invite_admin_handler)
|
||||
=======
|
||||
application.add_handler(share_channel_conv)
|
||||
>>>>>>> main
|
||||
import sys
|
||||
import asyncio
|
||||
if sys.platform.startswith('win'):
|
||||
|
||||
23
models.py
23
models.py
@@ -1,8 +1,25 @@
|
||||
<<<<<<< HEAD
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, Text
|
||||
=======
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Boolean
|
||||
>>>>>>> main
|
||||
from sqlalchemy.orm import relationship
|
||||
from db import Base
|
||||
from datetime import datetime
|
||||
|
||||
<<<<<<< HEAD
|
||||
class ActionLog(Base):
|
||||
__tablename__ = 'action_logs'
|
||||
id = Column(Integer, primary_key=True)
|
||||
admin_id = Column(Integer)
|
||||
action = Column(String)
|
||||
details = Column(String)
|
||||
timestamp = Column(String, default=lambda: datetime.utcnow().isoformat())
|
||||
|
||||
=======
|
||||
# Битовые флаги прав
|
||||
SCOPE_POST = 1 # право постить
|
||||
SCOPE_MANAGE_BTNS = 2 # право управлять кнопками (опционально)
|
||||
@@ -31,10 +48,14 @@ class ChannelAccess(Base):
|
||||
expires_at = Column(DateTime, nullable=True)
|
||||
|
||||
channel = relationship("Channel", foreign_keys=[channel_id])
|
||||
>>>>>>> main
|
||||
class Admin(Base):
|
||||
__tablename__ = 'admins'
|
||||
id = Column(Integer, primary_key=True)
|
||||
tg_id = Column(Integer, unique=True, nullable=False)
|
||||
tg_id = Column(Integer, nullable=False)
|
||||
channel_id = Column(Integer, ForeignKey('channels.id'), nullable=True)
|
||||
inviter_id = Column(Integer, nullable=True)
|
||||
invite_token = Column(String, nullable=True, unique=True)
|
||||
|
||||
class Channel(Base):
|
||||
__tablename__ = 'channels'
|
||||
|
||||
Reference in New Issue
Block a user