init commit
Some checks failed
continuous-integration/drone Build is failing

This commit is contained in:
2025-09-04 01:51:59 +09:00
commit aca280b64d
1841 changed files with 753304 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
__pycache__/
*.pyc
*.pyo
*.pyd
.env
.env.local
.env.*.local
bot.db
.idea/
.vscode/
tests/
.git/
.gitignore

View File

@@ -0,0 +1,13 @@
__pycache__/
*.pyc
*.pyo
*.pyd
.env
.env.local
.env.*.local
bot.db
.idea/
.vscode/
tests/
.git/
.gitignore

View File

@@ -0,0 +1,14 @@
kind: pipeline
name: default
steps:
- name: build
image: docker:dind
privileged: true
commands:
- docker build -t post_bot .
- name: test
image: python:3.11-slim
commands:
- pip install --no-cache-dir python-telegram-bot sqlalchemy python-dotenv pytest
- pytest tests/

View File

@@ -0,0 +1,14 @@
kind: pipeline
name: default
steps:
- name: build
image: docker:dind
privileged: true
commands:
- docker build -t post_bot .
- name: test
image: python:3.11-slim
commands:
- pip install --no-cache-dir python-telegram-bot sqlalchemy python-dotenv pytest
- pytest tests/

View File

@@ -0,0 +1,2 @@
TELEGRAM_TOKEN=your_token_here
DATABASE_URL=sqlite:///bot.db

View File

@@ -0,0 +1,2 @@
TELEGRAM_TOKEN=your_token_here
DATABASE_URL=sqlite:///bot.db

View File

@@ -0,0 +1,2 @@
TELEGRAM_TOKEN=your_token_here
DATABASE_URL=sqlite:///bot.db

View File

@@ -0,0 +1,2 @@
TELEGRAM_TOKEN=6414100562:AAFxeXt331_sKf8ui1EJve9vinUHyKHBjiU
DATABASE_URL=sqlite:///bot.db

View File

@@ -0,0 +1,2 @@
TELEGRAM_TOKEN=6414100562:AAEbDJZnFFJfddS0SV1xA4L0MQqxOArU4a0
DATABASE_URL=sqlite:///bot.db

View File

View File

@@ -0,0 +1,5 @@
.env
.venv/
.history
__pycache__/
.db

View File

@@ -0,0 +1,27 @@
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
# Env
.env
.env.local
.env.*.local
# DB
bot.db
# IDE
.idea/
.vscode/
# Docker
*.log
# Tests
tests/
# Git
.git/

View File

@@ -0,0 +1,27 @@
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
# Env
.env
.env.local
.env.*.local
# DB
bot.db
# IDE
.idea/
.vscode/
# Docker
*.log
# Tests
tests/
# Git
.git/

View File

@@ -0,0 +1,5 @@
FROM python:3.11-slim
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir python-telegram-bot sqlalchemy python-dotenv
CMD ["python", "main.py"]

View File

@@ -0,0 +1,5 @@
FROM python:3.11-slim
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir python-telegram-bot sqlalchemy python-dotenv
CMD ["python", "main.py"]

View File

@@ -0,0 +1,13 @@
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base
from dotenv import load_dotenv
load_dotenv()
DATABASE_URL = os.getenv('DATABASE_URL', 'sqlite:///bot.db')
engine = create_engine(DATABASE_URL, echo=True)
SessionLocal = sessionmaker(bind=engine)
def init_db():
Base.metadata.create_all(engine)

View File

@@ -0,0 +1,13 @@
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base
from dotenv import load_dotenv
load_dotenv()
DATABASE_URL = os.getenv('DATABASE_URL', 'sqlite:///bot.db')
engine = create_engine(DATABASE_URL, echo=True)
SessionLocal = sessionmaker(bind=engine)
def init_db():
Base.metadata.create_all(engine)

View File

@@ -0,0 +1,9 @@
version: '3.8'
services:
bot:
build: .
env_file:
- .env.example
volumes:
- ./bot.db:/app/bot.db
restart: unless-stopped

View File

@@ -0,0 +1,9 @@
version: '3.8'
services:
bot:
build: .
env_file:
- .env.example
volumes:
- ./bot.db:/app/bot.db
restart: unless-stopped

View File

@@ -0,0 +1,63 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data['button_name']
target = context.user_data['target']
session = SessionLocal()
if target.startswith('channel_'):
channel_id = int(target.split('_')[1])
button = Button(name=name, url=url, channel_id=channel_id)
else:
group_id = int(target.split('_')[1])
button = Button(name=name, url=url, group_id=group_id)
session.add(button)
session.commit()
session.close()
await update.message.reply_text('Кнопка добавлена.')
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,63 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data['button_name']
target = context.user_data['target']
session = SessionLocal()
if target.startswith('channel_'):
channel_id = int(target.split('_')[1])
button = Button(name=name, url=url, channel_id=channel_id)
else:
group_id = int(target.split('_')[1])
button = Button(name=name, url=url, group_id=group_id)
session.add(button)
session.commit()
session.close()
await update.message.reply_text('Кнопка добавлена.')
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,63 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data['button_name']
target = context.user_data['target']
session = SessionLocal()
if target.startswith('channel_'):
channel_id = int(target.split('_')[1])
button = Button(name=name, url=url, channel_id=channel_id)
else:
group_id = int(target.split('_')[1])
button = Button(name=name, url=url, group_id=group_id)
session.add(button)
session.commit()
session.close()
await update.message.reply_text('Кнопка добавлена.')
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,63 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data['button_name']
target = context.user_data['target']
session = SessionLocal()
if target.startswith('channel_'):
channel_id = int(target.split('_')[1])
button = Button(name=name, url=url, channel_id=channel_id)
else:
group_id = int(target.split('_')[1])
button = Button(name=name, url=url, group_id=group_id)
session.add(button)
session.commit()
session.close()
await update.message.reply_text('Кнопка добавлена.')
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,73 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Если выбран канал или группа уже сохранены — сразу переход к названию
if context.user_data.get('channel_id'):
context.user_data['target'] = f"channel_{context.user_data['channel_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
elif context.user_data.get('group_id'):
context.user_data['target'] = f"group_{context.user_data['group_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
# Если нет — стандартный выбор
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data['button_name']
target = context.user_data['target']
session = SessionLocal()
if target.startswith('channel_'):
channel_id = int(target.split('_')[1])
button = Button(name=name, url=url, channel_id=channel_id)
else:
group_id = int(target.split('_')[1])
button = Button(name=name, url=url, group_id=group_id)
session.add(button)
session.commit()
session.close()
await update.message.reply_text('Кнопка добавлена.')
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,73 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Если выбран канал или группа уже сохранены — сразу переход к названию
if context.user_data.get('channel_id'):
context.user_data['target'] = f"channel_{context.user_data['channel_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
elif context.user_data.get('group_id'):
context.user_data['target'] = f"group_{context.user_data['group_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
# Если нет — стандартный выбор
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data['button_name']
target = context.user_data['target']
session = SessionLocal()
if target.startswith('channel_'):
channel_id = int(target.split('_')[1])
button = Button(name=name, url=url, channel_id=channel_id)
else:
group_id = int(target.split('_')[1])
button = Button(name=name, url=url, group_id=group_id)
session.add(button)
session.commit()
session.close()
await update.message.reply_text('Кнопка добавлена.')
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,84 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Если выбран канал или группа уже сохранены — сразу переход к названию
if context.user_data.get('channel_id'):
context.user_data['target'] = f"channel_{context.user_data['channel_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
elif context.user_data.get('group_id'):
context.user_data['target'] = f"group_{context.user_data['group_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
# Если нет — стандартный выбор
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data.get('button_name')
target = context.user_data.get('target')
if not target or ('_' not in target):
await update.message.reply_text('Ошибка: не выбран канал или группа. Попробуйте снова.')
return ConversationHandler.END
session = SessionLocal()
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()
await update.message.reply_text('Кнопка добавлена.')
except Exception as e:
await update.message.reply_text(f'Ошибка при добавлении кнопки: {e}')
finally:
session.close()
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,84 @@
from telegram import Update
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_TARGET, INPUT_NAME, INPUT_URL = range(3)
async def add_button_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Если выбран канал или группа уже сохранены — сразу переход к названию
if context.user_data.get('channel_id'):
context.user_data['target'] = f"channel_{context.user_data['channel_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
elif context.user_data.get('group_id'):
context.user_data['target'] = f"group_{context.user_data['group_id']}"
await update.message.reply_text('Введите название кнопки:')
return INPUT_NAME
# Если нет — стандартный выбор
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([{'text': f'Канал: {c.name}', 'callback_data': f'channel_{c.id}'}])
for g in groups:
keyboard.append([{'text': f'Группа: {g.name}', 'callback_data': f'group_{g.id}'}])
if not keyboard:
await update.message.reply_text('Нет каналов или групп для добавления кнопки.')
return ConversationHandler.END
await update.message.reply_text('Выберите канал или группу:', reply_markup=None)
context.user_data['keyboard'] = keyboard
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
context.user_data['target'] = data
await query.edit_message_text('Введите название кнопки:')
return INPUT_NAME
async def input_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['button_name'] = update.message.text
await update.message.reply_text('Введите ссылку для кнопки:')
return INPUT_URL
async def input_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
url = update.message.text
name = context.user_data.get('button_name')
target = context.user_data.get('target')
if not target or ('_' not in target):
await update.message.reply_text('Ошибка: не выбран канал или группа. Попробуйте снова.')
return ConversationHandler.END
session = SessionLocal()
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()
await update.message.reply_text('Кнопка добавлена.')
except Exception as e:
await update.message.reply_text(f'Ошибка при добавлении кнопки: {e}')
finally:
session.close()
return ConversationHandler.END
add_button_conv = ConversationHandler(
entry_points=[CommandHandler('add_button', add_button_start)],
states={
SELECT_TARGET: [CallbackQueryHandler(select_target)],
INPUT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_name)],
INPUT_URL: [MessageHandler(filters.TEXT & ~filters.COMMAND, input_url)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,17 @@
from telegram import Update
from telegram.ext import ContextTypes
from db import SessionLocal
from models import Channel
async def add_channel(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if len(args) < 2:
await update.message.reply_text('Используйте: /add_channel <название> <ссылка>')
return
name, link = args[0], args[1]
session = SessionLocal()
channel = Channel(name=name, link=link)
session.add(channel)
session.commit()
session.close()
await update.message.reply_text(f'Канал "{name}" добавлен.')

View File

@@ -0,0 +1,17 @@
from telegram import Update
from telegram.ext import ContextTypes
from db import SessionLocal
from models import Channel
async def add_channel(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if len(args) < 2:
await update.message.reply_text('Используйте: /add_channel <название> <ссылка>')
return
name, link = args[0], args[1]
session = SessionLocal()
channel = Channel(name=name, link=link)
session.add(channel)
session.commit()
session.close()
await update.message.reply_text(f'Канал "{name}" добавлен.')

View File

@@ -0,0 +1,17 @@
from telegram import Update
from telegram.ext import ContextTypes
from db import SessionLocal
from models import Group
async def add_group(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if len(args) < 2:
await update.message.reply_text('Используйте: /add_group <название> <ссылка>')
return
name, link = args[0], args[1]
session = SessionLocal()
group = Group(name=name, link=link)
session.add(group)
session.commit()
session.close()
await update.message.reply_text(f'Группа "{name}" добавлена.')

View File

@@ -0,0 +1,17 @@
from telegram import Update
from telegram.ext import ContextTypes
from db import SessionLocal
from models import Group
async def add_group(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if len(args) < 2:
await update.message.reply_text('Используйте: /add_group <название> <ссылка>')
return
name, link = args[0], args[1]
session = SessionLocal()
group = Group(name=name, link=link)
session.add(group)
session.commit()
session.close()
await update.message.reply_text(f'Группа "{name}" добавлена.')

View File

@@ -0,0 +1,40 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, CallbackQueryHandler, ConversationHandler, ContextTypes
from db import SessionLocal
from models import Channel, Button
SELECT_CHANNEL, MANAGE_BUTTONS = range(2)
async def channel_buttons_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
channels = session.query(Channel).all()
session.close()
keyboard = [[InlineKeyboardButton(c.name, callback_data=str(c.id))] for c in channels]
await update.message.reply_text(
"Выберите канал для настройки клавиатуры:",
reply_markup=InlineKeyboardMarkup(keyboard)
)
return SELECT_CHANNEL
async def select_channel(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
channel_id = int(query.data)
context.user_data['channel_id'] = channel_id
session = SessionLocal()
buttons = session.query(Button).filter_by(channel_id=channel_id).all()
session.close()
text = "Кнопки этого канала:\n"
for b in buttons:
text += f"- {b.name}: {b.url}\n"
text += "\nДобавить новую кнопку: /add_button\nУдалить: /del_button <название>"
await query.edit_message_text(text)
return ConversationHandler.END
channel_buttons_conv = ConversationHandler(
entry_points=[CommandHandler('channel_buttons', channel_buttons_start)],
states={
SELECT_CHANNEL: [CallbackQueryHandler(select_channel)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,40 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, CallbackQueryHandler, ConversationHandler, ContextTypes
from db import SessionLocal
from models import Channel, Button
SELECT_CHANNEL, MANAGE_BUTTONS = range(2)
async def channel_buttons_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
channels = session.query(Channel).all()
session.close()
keyboard = [[InlineKeyboardButton(c.name, callback_data=str(c.id))] for c in channels]
await update.message.reply_text(
"Выберите канал для настройки клавиатуры:",
reply_markup=InlineKeyboardMarkup(keyboard)
)
return SELECT_CHANNEL
async def select_channel(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
channel_id = int(query.data)
context.user_data['channel_id'] = channel_id
session = SessionLocal()
buttons = session.query(Button).filter_by(channel_id=channel_id).all()
session.close()
text = "Кнопки этого канала:\n"
for b in buttons:
text += f"- {b.name}: {b.url}\n"
text += "\nДобавить новую кнопку: /add_button\nУдалить: /del_button <название>"
await query.edit_message_text(text)
return ConversationHandler.END
channel_buttons_conv = ConversationHandler(
entry_points=[CommandHandler('channel_buttons', channel_buttons_start)],
states={
SELECT_CHANNEL: [CallbackQueryHandler(select_channel)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,21 @@
from telegram import Update
from telegram.ext import CommandHandler, ContextTypes
from db import SessionLocal
from models import Button
async def del_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if not args:
await update.message.reply_text('Используйте: /del_button <название>')
return
name = args[0]
session = SessionLocal()
button = session.query(Button).filter_by(name=name).first()
if not button:
await update.message.reply_text('Кнопка не найдена.')
session.close()
return
session.delete(button)
session.commit()
session.close()
await update.message.reply_text(f'Кнопка "{name}" удалена.')

View File

@@ -0,0 +1,21 @@
from telegram import Update
from telegram.ext import CommandHandler, ContextTypes
from db import SessionLocal
from models import Button
async def del_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if not args:
await update.message.reply_text('Используйте: /del_button <название>')
return
name = args[0]
session = SessionLocal()
button = session.query(Button).filter_by(name=name).first()
if not button:
await update.message.reply_text('Кнопка не найдена.')
session.close()
return
session.delete(button)
session.commit()
session.close()
await update.message.reply_text(f'Кнопка "{name}" удалена.')

View File

@@ -0,0 +1,22 @@
from telegram import Update
from telegram.ext import CommandHandler, ContextTypes
from db import SessionLocal
from models import Button
async def edit_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if len(args) < 3:
await update.message.reply_text('Используйте: /edit_button <название> <новоеазвание> <новая_ссылка>')
return
name, new_name, new_url = args[0], args[1], args[2]
session = SessionLocal()
button = session.query(Button).filter_by(name=name).first()
if not button:
await update.message.reply_text('Кнопка не найдена.')
session.close()
return
button.name = new_name
button.url = new_url
session.commit()
session.close()
await update.message.reply_text(f'Кнопка "{name}" изменена.')

View File

@@ -0,0 +1,22 @@
from telegram import Update
from telegram.ext import CommandHandler, ContextTypes
from db import SessionLocal
from models import Button
async def edit_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
args = context.args
if len(args) < 3:
await update.message.reply_text('Используйте: /edit_button <название> <новоеазвание> <новая_ссылка>')
return
name, new_name, new_url = args[0], args[1], args[2]
session = SessionLocal()
button = session.query(Button).filter_by(name=name).first()
if not button:
await update.message.reply_text('Кнопка не найдена.')
session.close()
return
button.name = new_name
button.url = new_url
session.commit()
session.close()
await update.message.reply_text(f'Кнопка "{name}" изменена.')

View File

@@ -0,0 +1,2 @@
# Заглушка для будущей реализации редактирования поста
# Можно реализовать хранение черновиков и их редактирование

View File

@@ -0,0 +1,2 @@
# Заглушка для будущей реализации редактирования поста
# Можно реализовать хранение черновиков и их редактирование

View File

@@ -0,0 +1,40 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, CallbackQueryHandler, ConversationHandler, ContextTypes
from db import SessionLocal
from models import Group, Button
SELECT_GROUP, MANAGE_BUTTONS = range(2)
async def group_buttons_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
groups = session.query(Group).all()
session.close()
keyboard = [[InlineKeyboardButton(g.name, callback_data=str(g.id))] for g in groups]
await update.message.reply_text(
"Выберите группу для настройки клавиатуры:",
reply_markup=InlineKeyboardMarkup(keyboard)
)
return SELECT_GROUP
async def select_group(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
group_id = int(query.data)
context.user_data['group_id'] = group_id
session = SessionLocal()
buttons = session.query(Button).filter_by(group_id=group_id).all()
session.close()
text = "Кнопки этой группы:\n"
for b in buttons:
text += f"- {b.name}: {b.url}\n"
text += "\nДобавить новую кнопку: /add_button\nУдалить: /del_button <название>"
await query.edit_message_text(text)
return ConversationHandler.END
group_buttons_conv = ConversationHandler(
entry_points=[CommandHandler('group_buttons', group_buttons_start)],
states={
SELECT_GROUP: [CallbackQueryHandler(select_group)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,40 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, CallbackQueryHandler, ConversationHandler, ContextTypes
from db import SessionLocal
from models import Group, Button
SELECT_GROUP, MANAGE_BUTTONS = range(2)
async def group_buttons_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
groups = session.query(Group).all()
session.close()
keyboard = [[InlineKeyboardButton(g.name, callback_data=str(g.id))] for g in groups]
await update.message.reply_text(
"Выберите группу для настройки клавиатуры:",
reply_markup=InlineKeyboardMarkup(keyboard)
)
return SELECT_GROUP
async def select_group(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
group_id = int(query.data)
context.user_data['group_id'] = group_id
session = SessionLocal()
buttons = session.query(Button).filter_by(group_id=group_id).all()
session.close()
text = "Кнопки этой группы:\n"
for b in buttons:
text += f"- {b.name}: {b.url}\n"
text += "\nДобавить новую кнопку: /add_button\nУдалить: /del_button <название>"
await query.edit_message_text(text)
return ConversationHandler.END
group_buttons_conv = ConversationHandler(
entry_points=[CommandHandler('group_buttons', group_buttons_start)],
states={
SELECT_GROUP: [CallbackQueryHandler(select_group)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,62 @@
from telegram import Update, InputMediaPhoto, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ContextTypes, ConversationHandler, MessageHandler, CommandHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_MEDIA, SELECT_TEXT, SELECT_TARGET = range(3)
async def new_post_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text('Отправьте картинку для поста или /skip:')
return SELECT_MEDIA
async def select_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
if update.message.photo:
context.user_data['photo'] = update.message.photo[-1].file_id
await update.message.reply_text('Введите текст поста или пересланное сообщение:')
return SELECT_TEXT
async def select_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['text'] = update.message.text or update.message.caption
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([InlineKeyboardButton(f'Канал: {c.name}', callback_data=f'channel_{c.id}')])
for g in groups:
keyboard.append([InlineKeyboardButton(f'Группа: {g.name}', callback_data=f'group_{g.id}')])
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=reply_markup)
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
session = SessionLocal()
if data.startswith('channel_'):
channel_id = int(data.split('_')[1])
channel = session.query(Channel).get(channel_id)
buttons = session.query(Button).filter_by(channel_id=channel_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
await context.bot.send_photo(chat_id=channel.link, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
else:
group_id = int(data.split('_')[1])
group = session.query(Group).get(group_id)
buttons = session.query(Button).filter_by(group_id=group_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
await context.bot.send_photo(chat_id=group.link, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
session.close()
await query.edit_message_text('Пост отправлен!')
return ConversationHandler.END
new_post_conv = ConversationHandler(
entry_points=[CommandHandler('new_post', new_post_start)],
states={
SELECT_MEDIA: [MessageHandler(filters.PHOTO | filters.Document.IMAGE | filters.COMMAND, select_media)],
SELECT_TEXT: [MessageHandler(filters.TEXT | filters.FORWARDED, select_text)],
SELECT_TARGET: [CallbackQueryHandler(select_target)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,62 @@
from telegram import Update, InputMediaPhoto, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ContextTypes, ConversationHandler, MessageHandler, CommandHandler, filters
from db import SessionLocal
from models import Channel, Group, Button
SELECT_MEDIA, SELECT_TEXT, SELECT_TARGET = range(3)
async def new_post_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text('Отправьте картинку для поста или /skip:')
return SELECT_MEDIA
async def select_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
if update.message.photo:
context.user_data['photo'] = update.message.photo[-1].file_id
await update.message.reply_text('Введите текст поста или пересланное сообщение:')
return SELECT_TEXT
async def select_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['text'] = update.message.text or update.message.caption
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([InlineKeyboardButton(f'Канал: {c.name}', callback_data=f'channel_{c.id}')])
for g in groups:
keyboard.append([InlineKeyboardButton(f'Группа: {g.name}', callback_data=f'group_{g.id}')])
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=reply_markup)
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
session = SessionLocal()
if data.startswith('channel_'):
channel_id = int(data.split('_')[1])
channel = session.query(Channel).get(channel_id)
buttons = session.query(Button).filter_by(channel_id=channel_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
await context.bot.send_photo(chat_id=channel.link, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
else:
group_id = int(data.split('_')[1])
group = session.query(Group).get(group_id)
buttons = session.query(Button).filter_by(group_id=group_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
await context.bot.send_photo(chat_id=group.link, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
session.close()
await query.edit_message_text('Пост отправлен!')
return ConversationHandler.END
new_post_conv = ConversationHandler(
entry_points=[CommandHandler('new_post', new_post_start)],
states={
SELECT_MEDIA: [MessageHandler(filters.PHOTO | filters.Document.IMAGE | filters.COMMAND, select_media)],
SELECT_TEXT: [MessageHandler(filters.TEXT | filters.FORWARDED, select_text)],
SELECT_TARGET: [CallbackQueryHandler(select_target)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,63 @@
from telegram import Update, InputMediaPhoto, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ContextTypes, ConversationHandler, MessageHandler, CommandHandler, filters, CallbackQueryHandler, ContextTypes
from db import SessionLocal
from models import Channel, Group, Button
SELECT_MEDIA, SELECT_TEXT, SELECT_TARGET = range(3)
async def new_post_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text('Отправьте картинку для поста или /skip:')
return SELECT_MEDIA
async def select_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
if update.message.photo:
context.user_data['photo'] = update.message.photo[-1].file_id
await update.message.reply_text('Введите текст поста или пересланное сообщение:')
return SELECT_TEXT
async def select_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['text'] = update.message.text or update.message.caption
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([InlineKeyboardButton(f'Канал: {c.name}', callback_data=f'channel_{c.id}')])
for g in groups:
keyboard.append([InlineKeyboardButton(f'Группа: {g.name}', callback_data=f'group_{g.id}')])
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=reply_markup)
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
session = SessionLocal()
if data.startswith('channel_'):
channel_id = int(data.split('_')[1])
channel = session.query(Channel).get(channel_id)
buttons = session.query(Button).filter_by(channel_id=channel_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
await context.bot.send_photo(chat_id=channel.link, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
else:
group_id = int(data.split('_')[1])
group = session.query(Group).get(group_id)
buttons = session.query(Button).filter_by(group_id=group_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
await context.bot.send_photo(chat_id=group.link, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
session.close()
await query.edit_message_text('Пост отправлен!')
return ConversationHandler.END
new_post_conv = ConversationHandler(
entry_points=[CommandHandler('new_post', new_post_start)],
states={
SELECT_MEDIA: [MessageHandler(filters.PHOTO | filters.Document.IMAGE | filters.COMMAND, select_media)],
SELECT_TEXT: [MessageHandler(filters.TEXT | filters.FORWARDED, select_text)],
SELECT_TARGET: [CallbackQueryHandler(select_target)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,73 @@
from telegram import Update, InputMediaPhoto, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ContextTypes, ConversationHandler, MessageHandler, CommandHandler, filters, CallbackQueryHandler, ContextTypes
from db import SessionLocal
from models import Channel, Group, Button
SELECT_MEDIA, SELECT_TEXT, SELECT_TARGET = range(3)
async def new_post_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text('Отправьте картинку для поста или /skip:')
return SELECT_MEDIA
async def select_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
if update.message.photo:
context.user_data['photo'] = update.message.photo[-1].file_id
await update.message.reply_text('Введите текст поста или пересланное сообщение:')
return SELECT_TEXT
async def select_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['text'] = update.message.text or update.message.caption
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([InlineKeyboardButton(f'Канал: {c.name}', callback_data=f'channel_{c.id}')])
for g in groups:
keyboard.append([InlineKeyboardButton(f'Группа: {g.name}', callback_data=f'group_{g.id}')])
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=reply_markup)
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
session = SessionLocal()
try:
if data.startswith('channel_'):
channel_id = int(data.split('_')[1])
channel = session.query(Channel).get(channel_id)
buttons = session.query(Button).filter_by(channel_id=channel_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
chat_id = channel.link.strip()
else:
group_id = int(data.split('_')[1])
group = session.query(Group).get(group_id)
buttons = session.query(Button).filter_by(group_id=group_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
chat_id = group.link.strip()
# Проверка chat_id
if not (chat_id.startswith('@') or chat_id.startswith('-')):
await query.edit_message_text('Ошибка: ссылка должна быть username (@channel) или числовой ID (-100...)')
return ConversationHandler.END
try:
await context.bot.send_photo(chat_id=chat_id, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
await query.edit_message_text('Пост отправлен!')
except Exception as e:
await query.edit_message_text(f'Ошибка отправки поста: {e}')
finally:
session.close()
return ConversationHandler.END
new_post_conv = ConversationHandler(
entry_points=[CommandHandler('new_post', new_post_start)],
states={
SELECT_MEDIA: [MessageHandler(filters.PHOTO | filters.Document.IMAGE | filters.COMMAND, select_media)],
SELECT_TEXT: [MessageHandler(filters.TEXT | filters.FORWARDED, select_text)],
SELECT_TARGET: [CallbackQueryHandler(select_target)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,73 @@
from telegram import Update, InputMediaPhoto, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ContextTypes, ConversationHandler, MessageHandler, CommandHandler, filters, CallbackQueryHandler, ContextTypes
from db import SessionLocal
from models import Channel, Group, Button
SELECT_MEDIA, SELECT_TEXT, SELECT_TARGET = range(3)
async def new_post_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text('Отправьте картинку для поста или /skip:')
return SELECT_MEDIA
async def select_media(update: Update, context: ContextTypes.DEFAULT_TYPE):
if update.message.photo:
context.user_data['photo'] = update.message.photo[-1].file_id
await update.message.reply_text('Введите текст поста или пересланное сообщение:')
return SELECT_TEXT
async def select_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
context.user_data['text'] = update.message.text or update.message.caption
session = SessionLocal()
channels = session.query(Channel).all()
groups = session.query(Group).all()
session.close()
keyboard = []
for c in channels:
keyboard.append([InlineKeyboardButton(f'Канал: {c.name}', callback_data=f'channel_{c.id}')])
for g in groups:
keyboard.append([InlineKeyboardButton(f'Группа: {g.name}', callback_data=f'group_{g.id}')])
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('Выберите, куда отправить пост:', reply_markup=reply_markup)
return SELECT_TARGET
async def select_target(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
data = query.data
session = SessionLocal()
try:
if data.startswith('channel_'):
channel_id = int(data.split('_')[1])
channel = session.query(Channel).get(channel_id)
buttons = session.query(Button).filter_by(channel_id=channel_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
chat_id = channel.link.strip()
else:
group_id = int(data.split('_')[1])
group = session.query(Group).get(group_id)
buttons = session.query(Button).filter_by(group_id=group_id).all()
markup = InlineKeyboardMarkup([[InlineKeyboardButton(b.name, url=b.url)] for b in buttons]) if buttons else None
chat_id = group.link.strip()
# Проверка chat_id
if not (chat_id.startswith('@') or chat_id.startswith('-')):
await query.edit_message_text('Ошибка: ссылка должна быть username (@channel) или числовой ID (-100...)')
return ConversationHandler.END
try:
await context.bot.send_photo(chat_id=chat_id, photo=context.user_data.get('photo'), caption=context.user_data.get('text'), reply_markup=markup)
await query.edit_message_text('Пост отправлен!')
except Exception as e:
await query.edit_message_text(f'Ошибка отправки поста: {e}')
finally:
session.close()
return ConversationHandler.END
new_post_conv = ConversationHandler(
entry_points=[CommandHandler('new_post', new_post_start)],
states={
SELECT_MEDIA: [MessageHandler(filters.PHOTO | filters.Document.IMAGE | filters.COMMAND, select_media)],
SELECT_TEXT: [MessageHandler(filters.TEXT | filters.FORWARDED, select_text)],
SELECT_TARGET: [CallbackQueryHandler(select_target)],
},
fallbacks=[]
)

View File

@@ -0,0 +1,2 @@
# Заглушка для будущей реализации отправки поста вручную
# Можно реализовать выбор поста и канал/группу для отправки

View File

@@ -0,0 +1,2 @@
# Заглушка для будущей реализации отправки поста вручную
# Можно реализовать выбор поста и канал/группу для отправки

View File

@@ -0,0 +1,39 @@
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 SessionLocal, init_db
from models import Admin, Channel, Group, Button
load_dotenv()
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
init_db()
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
user_id = update.effective_user.id
admin = session.query(Admin).filter_by(tg_id=user_id).first()
if not admin:
admin = Admin(tg_id=user_id)
session.add(admin)
session.commit()
await update.message.reply_text('Вы зарегистрированы как админ.')
else:
await update.message.reply_text('Вы уже зарегистрированы.')
session.close()
# ...handlers for add_channel, add_group, add_button, new_post, etc. будут добавлены...
def main():
application = Application.builder().token(TELEGRAM_TOKEN).build()
application.add_handler(CommandHandler('start', start))
# ...add other handlers...
application.run_polling()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,48 @@
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 SessionLocal, init_db
from models import Admin, Channel, Group, Button
load_dotenv()
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
init_db()
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
user_id = update.effective_user.id
admin = session.query(Admin).filter_by(tg_id=user_id).first()
if not admin:
admin = Admin(tg_id=user_id)
session.add(admin)
session.commit()
await update.message.reply_text('Вы зарегистрированы как админ.')
else:
await update.message.reply_text('Вы уже зарегистрированы.')
session.close()
# Импорт обработчиков
from handlers.add_channel import add_channel
from handlers.add_group import add_group
from handlers.add_button import add_button_conv
from handlers.new_post import new_post_conv
def main():
application = Application.builder().token(TELEGRAM_TOKEN).build()
application.add_handler(CommandHandler('start', start))
application.add_handler(CommandHandler('add_channel', add_channel))
application.add_handler(CommandHandler('add_group', add_group))
application.add_handler(add_button_conv)
application.add_handler(new_post_conv)
application.run_polling()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,48 @@
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 SessionLocal, init_db
from models import Admin, Channel, Group, Button
load_dotenv()
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
init_db()
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
user_id = update.effective_user.id
admin = session.query(Admin).filter_by(tg_id=user_id).first()
if not admin:
admin = Admin(tg_id=user_id)
session.add(admin)
session.commit()
await update.message.reply_text('Вы зарегистрированы как админ.')
else:
await update.message.reply_text('Вы уже зарегистрированы.')
session.close()
# Импорт обработчиков
from handlers.add_channel import add_channel
from handlers.add_group import add_group
from handlers.add_button import add_button_conv
from handlers.new_post import new_post_conv
def main():
application = Application.builder().token(TELEGRAM_TOKEN).build()
application.add_handler(CommandHandler('start', start))
application.add_handler(CommandHandler('add_channel', add_channel))
application.add_handler(CommandHandler('add_group', add_group))
application.add_handler(add_button_conv)
application.add_handler(new_post_conv)
application.run_polling()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,56 @@
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 SessionLocal, init_db
from models import Admin, Channel, Group, Button
load_dotenv()
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
init_db()
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
user_id = update.effective_user.id
admin = session.query(Admin).filter_by(tg_id=user_id).first()
if not admin:
admin = Admin(tg_id=user_id)
session.add(admin)
session.commit()
await update.message.reply_text('Вы зарегистрированы как админ.')
else:
await update.message.reply_text('Вы уже зарегистрированы.')
session.close()
# Импорт обработчиков
from handlers.add_channel import add_channel
from handlers.add_group import add_group
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
def main():
application = Application.builder().token(TELEGRAM_TOKEN).build()
application.add_handler(CommandHandler('start', start))
application.add_handler(CommandHandler('add_channel', add_channel))
application.add_handler(CommandHandler('add_group', add_group))
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.run_polling()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,56 @@
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 SessionLocal, init_db
from models import Admin, Channel, Group, Button
load_dotenv()
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
init_db()
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
session = SessionLocal()
user_id = update.effective_user.id
admin = session.query(Admin).filter_by(tg_id=user_id).first()
if not admin:
admin = Admin(tg_id=user_id)
session.add(admin)
session.commit()
await update.message.reply_text('Вы зарегистрированы как админ.')
else:
await update.message.reply_text('Вы уже зарегистрированы.')
session.close()
# Импорт обработчиков
from handlers.add_channel import add_channel
from handlers.add_group import add_group
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
def main():
application = Application.builder().token(TELEGRAM_TOKEN).build()
application.add_handler(CommandHandler('start', start))
application.add_handler(CommandHandler('add_channel', add_channel))
application.add_handler(CommandHandler('add_group', add_group))
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.run_polling()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,33 @@
from sqlalchemy import Column, Integer, String, ForeignKey, Text
from sqlalchemy.orm import declarative_base, relationship
Base = declarative_base()
class Admin(Base):
__tablename__ = 'admins'
id = Column(Integer, primary_key=True)
tg_id = Column(Integer, unique=True, nullable=False)
class Channel(Base):
__tablename__ = 'channels'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
link = Column(String, nullable=False)
buttons = relationship('Button', back_populates='channel')
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
link = Column(String, nullable=False)
buttons = relationship('Button', back_populates='group')
class Button(Base):
__tablename__ = 'buttons'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
url = Column(String, nullable=False)
channel_id = Column(Integer, ForeignKey('channels.id'), nullable=True)
group_id = Column(Integer, ForeignKey('groups.id'), nullable=True)
channel = relationship('Channel', back_populates='buttons')
group = relationship('Group', back_populates='buttons')

View File

@@ -0,0 +1,33 @@
from sqlalchemy import Column, Integer, String, ForeignKey, Text
from sqlalchemy.orm import declarative_base, relationship
Base = declarative_base()
class Admin(Base):
__tablename__ = 'admins'
id = Column(Integer, primary_key=True)
tg_id = Column(Integer, unique=True, nullable=False)
class Channel(Base):
__tablename__ = 'channels'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
link = Column(String, nullable=False)
buttons = relationship('Button', back_populates='channel')
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
link = Column(String, nullable=False)
buttons = relationship('Button', back_populates='group')
class Button(Base):
__tablename__ = 'buttons'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
url = Column(String, nullable=False)
channel_id = Column(Integer, ForeignKey('channels.id'), nullable=True)
group_id = Column(Integer, ForeignKey('groups.id'), nullable=True)
channel = relationship('Channel', back_populates='buttons')
group = relationship('Group', back_populates='buttons')

View File

@@ -0,0 +1,4 @@
python-telegram-bot>=20.0
sqlalchemy>=2.0
python-dotenv>=1.0
pytest>=7.0

View File

@@ -0,0 +1,4 @@
python-telegram-bot>=20.0
sqlalchemy>=2.0
python-dotenv>=1.0
pytest>=7.0