mirror of
https://github.com/civsocit/olgram.git
synced 2023-07-22 01:29:12 +03:00
Пример Instance бота, разные команды
This commit is contained in:
parent
4e70ebf32c
commit
d738da2fa9
@ -5,7 +5,7 @@ Open-source self-hosted Livegram alternative
|
||||
|
||||
#####
|
||||
|
||||
таблицы
|
||||
|
||||
Боты: токен, имя, владелец
|
||||
Пользователи: идентификатор
|
||||
instance поведение
|
||||
Кто-то написал сообщение в любом чате - переслать в супер-чат
|
||||
Кто-то ответил на сообщение в супер-чате - переслать автору сообщения
|
||||
Кто-то написал /start - отправить стартовое сообщение
|
3
bot.py
3
bot.py
@ -6,7 +6,7 @@ from settings import BotSettings
|
||||
|
||||
from olgram.bot.bots import router as bots_router
|
||||
from olgram.bot.start import router as start_router
|
||||
|
||||
from olgram.bot.bot import router as bot_router
|
||||
from olgram.utils.database import init_database
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ def main():
|
||||
|
||||
start_router.setup(dp)
|
||||
bots_router.setup(dp)
|
||||
bot_router.setup(dp)
|
||||
|
||||
executor.start_polling(dp, skip_updates=True)
|
||||
|
||||
|
@ -7,9 +7,18 @@ services:
|
||||
- POSTGRES_PASSWORD=test_passwd
|
||||
- POSTGRES_DB=olgram
|
||||
ports:
|
||||
- '5431:5432'
|
||||
- '5430:5432'
|
||||
volumes:
|
||||
- database:/var/lib/postgresql/data
|
||||
redis:
|
||||
image: 'bitnami/redis:latest'
|
||||
environment:
|
||||
- ALLOW_EMPTY_PASSWORD=yes
|
||||
volumes:
|
||||
- redis-db:/bitnami/redis/data
|
||||
ports:
|
||||
- '6370:6379'
|
||||
|
||||
volumes:
|
||||
database:
|
||||
redis-db:
|
||||
|
10
instance/Dockerfile
Normal file
10
instance/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM python:3.8-buster
|
||||
|
||||
COPY . /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip install --upgrade pip && \
|
||||
pip install -r requirements.txt && \
|
||||
|
||||
CMD ["python", "instance"]
|
0
instance/__init__.py
Normal file
0
instance/__init__.py
Normal file
77
instance/bot.py
Normal file
77
instance/bot.py
Normal file
@ -0,0 +1,77 @@
|
||||
import asyncio
|
||||
import aioredis
|
||||
import typing as ty
|
||||
from aiogram import Bot, Dispatcher, executor, types, exceptions
|
||||
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||
|
||||
from olgram.utils.router import Router
|
||||
|
||||
token = "(token)"
|
||||
bot_id = token.split(":")[0]
|
||||
start_text = 'Здравствуйте! Напишите тут что-то'
|
||||
super_chat_id = -1 # ID чата здесь
|
||||
|
||||
router = Router()
|
||||
redis: ty.Optional[aioredis.Redis] = None
|
||||
|
||||
|
||||
def message_unique_id(message_id) -> str:
|
||||
return bot_id + "-" + str(message_id)
|
||||
|
||||
|
||||
@router.message_handler(content_types=[types.ContentType.ANY])
|
||||
async def receive_text(message: types.Message):
|
||||
"""
|
||||
Some text received
|
||||
:param message:
|
||||
:return:
|
||||
"""
|
||||
if message.text and message.text.startswith("/start"):
|
||||
await message.answer(start_text)
|
||||
return
|
||||
|
||||
if message.chat.id != super_chat_id:
|
||||
# Это обычный чат
|
||||
new_message = await message.forward(super_chat_id)
|
||||
await redis.set(message_unique_id(new_message.message_id), message.chat.id)
|
||||
else:
|
||||
# Это чат, в который бот должен пересылать сообщения
|
||||
if message.reply_to_message:
|
||||
chat_id = await redis.get(message_unique_id(message.reply_to_message.message_id))
|
||||
if not chat_id:
|
||||
chat_id = message.reply_to_message.forward_from_chat
|
||||
if not chat_id:
|
||||
await message.reply("Невозможно ответить, автор сообщения не найден")
|
||||
return
|
||||
chat_id = int(chat_id)
|
||||
try:
|
||||
await message.copy_to(chat_id)
|
||||
except exceptions.MessageError:
|
||||
await message.reply("Невозможно отправить сообщение пользователю: возможно, он заблокировал бота")
|
||||
return
|
||||
|
||||
else:
|
||||
await message.forward(super_chat_id)
|
||||
|
||||
|
||||
async def init_redis():
|
||||
global redis
|
||||
redis = await aioredis.create_redis_pool('redis://localhost:6370')
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Classic polling
|
||||
"""
|
||||
|
||||
asyncio.get_event_loop().run_until_complete(init_redis())
|
||||
|
||||
bot = Bot(token)
|
||||
dp = Dispatcher(bot, storage=MemoryStorage())
|
||||
router.setup(dp)
|
||||
|
||||
executor.start_polling(dp, skip_updates=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
46
olgram/bot/bot.py
Normal file
46
olgram/bot/bot.py
Normal file
@ -0,0 +1,46 @@
|
||||
from aiogram import types, Bot as AioBot
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from aiogram.utils.callback_data import CallbackData
|
||||
from textwrap import dedent
|
||||
|
||||
from olgram.utils.router import Router
|
||||
from olgram.utils.mix import try_delete_message
|
||||
from olgram.models.models import Bot, User
|
||||
|
||||
router = Router()
|
||||
|
||||
# Пользователь выбрал бота
|
||||
select_bot = CallbackData('bot_select', 'bot_id')
|
||||
# Пользователь выбрал, что хочет сделать со своим ботом
|
||||
bot_operation = CallbackData('bot_operation', 'operation')
|
||||
|
||||
|
||||
@router.callback_query_handler(select_bot.filter())
|
||||
async def select_bot_callback(call: types.CallbackQuery, callback_data: dict, state: FSMContext):
|
||||
"""
|
||||
Пользователь выбрал бота для редактирования
|
||||
"""
|
||||
bot_id = callback_data["bot_id"]
|
||||
bot = await Bot.get_or_none(id=bot_id)
|
||||
if not bot or (await bot.owner).telegram_id != call.from_user.id:
|
||||
await call.answer("Такого бота нет", show_alert=True)
|
||||
return
|
||||
|
||||
async with state.proxy() as proxy:
|
||||
proxy["bot"] = bot
|
||||
|
||||
await try_delete_message(call.message)
|
||||
|
||||
keyboard = types.InlineKeyboardMarkup(row_width=2)
|
||||
keyboard.insert(types.InlineKeyboardButton(text="Текст", callback_data=bot_operation.new(operation="text")))
|
||||
keyboard.insert(types.InlineKeyboardButton(text="Чат", callback_data=bot_operation.new(operation="chat")))
|
||||
keyboard.insert(types.InlineKeyboardButton(text="Удалить бот", callback_data=bot_operation.new(operation="delete")))
|
||||
keyboard.insert(types.InlineKeyboardButton(text="<<Вернуться к списку ботов",
|
||||
callback_data=bot_operation.new(operation="back")))
|
||||
|
||||
await AioBot.get_current().send_message(call.message.chat.id, dedent(f"""
|
||||
Управление ботом @{bot.name}.
|
||||
|
||||
Если у вас возникли вопросы по настройке бота, то посмотрите нашу справку /help.
|
||||
"""), reply_markup=keyboard)
|
||||
|
@ -5,40 +5,65 @@ import re
|
||||
from textwrap import dedent
|
||||
|
||||
from ..utils.router import Router
|
||||
from olgram.models.bot import Bot
|
||||
from olgram.models.user import User
|
||||
from .bot import select_bot
|
||||
from olgram.models.models import Bot, User
|
||||
from olgram.settings import OlgramSettings
|
||||
|
||||
router = Router()
|
||||
token_pattern = r'[0-9]{8,10}:[a-zA-Z0-9_-]{35}'
|
||||
|
||||
|
||||
@router.message_handler(commands=["my_bots"])
|
||||
@router.message_handler(commands=["mybots"], state="*")
|
||||
async def my_bots(message: types.Message, state: FSMContext):
|
||||
"""
|
||||
Команда /mybots (список ботов)
|
||||
"""
|
||||
user = await User.get_or_none(telegram_id=message.from_user.id)
|
||||
bots = await Bot.filter(owner=user)
|
||||
if not bots:
|
||||
await message.answer(dedent("""
|
||||
У вас нет добавленных ботов.
|
||||
|
||||
Отправьте команду /add_bot, чтобы добавить бот.
|
||||
Отправьте команду /addbot, чтобы добавить бот.
|
||||
"""))
|
||||
return
|
||||
|
||||
bots_str = "\n".join(["@" + bot.name for bot in bots])
|
||||
await message.answer(dedent(f"""
|
||||
Ваши боты:
|
||||
{bots_str}
|
||||
"""))
|
||||
keyboard = types.InlineKeyboardMarkup(row_width=2)
|
||||
for bot in bots:
|
||||
keyboard.insert(types.InlineKeyboardButton(text="@" + bot.name, callback_data=select_bot.new(bot_id=bot.id)))
|
||||
|
||||
await message.answer("Ваши боты", reply_markup=keyboard)
|
||||
|
||||
|
||||
@router.message_handler(commands=["add_bot"])
|
||||
@router.message_handler(commands=["addbot"], state="*")
|
||||
async def add_bot(message: types.Message, state: FSMContext):
|
||||
await message.answer("Окей, пришли тогда токен plz")
|
||||
"""
|
||||
Команда /addbot (добавить бота)
|
||||
"""
|
||||
bot_count = await Bot.filter(user__telegram_id=message.from_user.id).count()
|
||||
if bot_count > OlgramSettings.max_bots_per_user():
|
||||
await message.answer("У вас уже слишком много ботов")
|
||||
return
|
||||
|
||||
await message.answer(dedent("""
|
||||
Чтобы подключить бот, вам нужно выполнить три действия:
|
||||
|
||||
1. Перейдите в бот @BotFather, нажмите START и отправьте команду /newbot
|
||||
2. Введите название бота, а потом username бота.
|
||||
3. После создания бота перешлите ответное сообщение в этот бот или скопируйте и пришлите token бота.
|
||||
|
||||
Важно: не подключайте боты, которые используются в других сервисах (Manybot, Chatfuel, Livegram и других).
|
||||
|
||||
Подробную инструкцию по созданию бота читайте здесь /help)
|
||||
"""))
|
||||
await state.set_state("add_bot")
|
||||
|
||||
|
||||
@router.message_handler(state="add_bot", content_types="text", regexp="^[^/].+") # Not command
|
||||
async def bot_added(message: types.Message, state: FSMContext):
|
||||
"""
|
||||
Пользователь добавляет бота и мы ждём от него токен
|
||||
"""
|
||||
token = re.findall(token_pattern, message.text)
|
||||
|
||||
async def on_invalid_token():
|
||||
@ -66,6 +91,7 @@ async def bot_added(message: types.Message, state: FSMContext):
|
||||
try:
|
||||
test_bot = AioBot(token)
|
||||
test_bot_info = await test_bot.get_me()
|
||||
await test_bot.session.close()
|
||||
except ValueError:
|
||||
return await on_invalid_token()
|
||||
except Unauthorized:
|
||||
|
@ -1,4 +1,4 @@
|
||||
from aiogram import Bot, Dispatcher, executor, types
|
||||
from aiogram import types
|
||||
from aiogram.dispatcher import FSMContext
|
||||
from textwrap import dedent
|
||||
from ..utils.router import Router
|
||||
@ -9,7 +9,7 @@ router = Router()
|
||||
@router.message_handler(commands=["start"], state="*")
|
||||
async def start(message: types.Message, state: FSMContext):
|
||||
"""
|
||||
Start command handler
|
||||
Команда /start
|
||||
"""
|
||||
await state.reset_state()
|
||||
|
||||
@ -20,8 +20,8 @@ async def start(message: types.Message, state: FSMContext):
|
||||
|
||||
Используйте эти команды, чтобы управлять этим ботом:
|
||||
|
||||
/add_bot - добавить бот
|
||||
/my_bots - управление ботами
|
||||
/addbot - добавить бот
|
||||
/mybots - управление ботами
|
||||
|
||||
/help - помощь
|
||||
|
||||
@ -32,7 +32,7 @@ async def start(message: types.Message, state: FSMContext):
|
||||
@router.message_handler(commands=["help"], state="*")
|
||||
async def help(message: types.Message, state: FSMContext):
|
||||
"""
|
||||
Help command handler
|
||||
Команда /help
|
||||
"""
|
||||
await message.answer(dedent("""
|
||||
<todo: help here>
|
||||
|
@ -10,3 +10,11 @@ class Bot(Model):
|
||||
|
||||
class Meta:
|
||||
table = 'bot'
|
||||
|
||||
|
||||
class User(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
telegram_id = fields.IntField(index=True, unique=True)
|
||||
|
||||
class Meta:
|
||||
table = 'user'
|
@ -1,9 +0,0 @@
|
||||
from tortoise import Model, fields
|
||||
|
||||
|
||||
class User(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
telegram_id = fields.IntField(index=True, unique=True)
|
||||
|
||||
class Meta:
|
||||
table = 'user'
|
@ -15,6 +15,16 @@ class _Settings(ABC):
|
||||
return parameter
|
||||
|
||||
|
||||
class OlgramSettings(_Settings):
|
||||
@classmethod
|
||||
def max_bots_per_user(cls) -> int:
|
||||
"""
|
||||
Максимальное количество ботов у одного пользователя
|
||||
:return: int
|
||||
"""
|
||||
return 5
|
||||
|
||||
|
||||
class BotSettings(_Settings):
|
||||
@classmethod
|
||||
def token(cls) -> str:
|
@ -8,8 +8,8 @@ async def init_database():
|
||||
# which contain models from "app.models"
|
||||
await Tortoise.init(
|
||||
db_url=f'postgres://{DatabaseSettings.user()}:{DatabaseSettings.password()}'
|
||||
f'@localhost:5431/{DatabaseSettings.database_name()}',
|
||||
modules={'models': ['olgram.models.bot', 'olgram.models.user']}
|
||||
f'@localhost:5430/{DatabaseSettings.database_name()}',
|
||||
modules={'models': ['olgram.models.models']}
|
||||
)
|
||||
# Generate the schema
|
||||
await Tortoise.generate_schemas()
|
||||
|
9
olgram/utils/mix.py
Normal file
9
olgram/utils/mix.py
Normal file
@ -0,0 +1,9 @@
|
||||
from aiogram.types import Message
|
||||
from aiogram.utils.exceptions import TelegramAPIError
|
||||
|
||||
|
||||
async def try_delete_message(message: Message):
|
||||
try:
|
||||
await message.delete()
|
||||
except TelegramAPIError:
|
||||
pass
|
@ -2,3 +2,4 @@ aiogram
|
||||
tortoise-orm[asyncpg]
|
||||
aerich
|
||||
python-dotenv
|
||||
aioredis
|
Loading…
Reference in New Issue
Block a user