mirror of
				https://github.com/civsocit/olgram.git
				synced 2023-07-22 01:29:12 +03:00 
			
		
		
		
	Merge branch 'main' into stable
This commit is contained in:
		
						commit
						4c22563974
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -10,3 +10,5 @@ docs/build
 | 
				
			|||||||
ad.md
 | 
					ad.md
 | 
				
			||||||
release.env
 | 
					release.env
 | 
				
			||||||
test.py
 | 
					test.py
 | 
				
			||||||
 | 
					backup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								docs/images/inline.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/images/inline.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.1 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/images/settemplates.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/images/settemplates.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 175 KiB  | 
@ -14,6 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   about
 | 
					   about
 | 
				
			||||||
   quick_start
 | 
					   quick_start
 | 
				
			||||||
 | 
					   templates
 | 
				
			||||||
   developer
 | 
					   developer
 | 
				
			||||||
   additional
 | 
					   additional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@
 | 
				
			|||||||
Как создать бота
 | 
					Как создать бота
 | 
				
			||||||
----------------
 | 
					----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Перейдите по ссылке `@Olgram <https://t.me/olgrambot>`_ и нажмите Запустить:
 | 
					Перейдите по ссылке `@OlgramBot <https://t.me/olgrambot>`_ и нажмите Запустить:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. image:: ../images/start.jpg
 | 
					.. image:: ../images/start.jpg
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										32
									
								
								docs/source/templates.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								docs/source/templates.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					Шаблоны ответов
 | 
				
			||||||
 | 
					=============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Иногда в поддержке приходится отвечать на однотипные вопросы однотипными ответами. Например:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Q. ``Здравствуйте! Когда будет доставлен мой заказ?``
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   A. ``Добрый день. Ваш заказ принят в обработку. Среднее время доставки 2-4 дня. Мы уведомим вас об изменении статуса заказа``
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Чтобы не печатать каждый раз одинаковые тексты, в Olgram можно задать список шаблонных ответов. Тогда диалог с
 | 
				
			||||||
 | 
					пользователем может выглядеть так:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. image:: ../images/inline.gif
 | 
				
			||||||
 | 
					   :width: 300
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Заметьте, чтобы увидеть список вариантов ответов, нужно упомянуть вашего feedback бота и нажать пробел
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Как настроить шаблоны
 | 
				
			||||||
 | 
					---------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Шаблоны можно задать в меню Olgram бота Текст -> Автоответчик -> Шаблоны ответов.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. image:: ../images/settemplates.jpg
 | 
				
			||||||
 | 
					   :width: 300
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Обязательно включите inline mode в вашем feedback боте. Для этого отправьте @BotFather команду ``/setinline``
 | 
				
			||||||
 | 
					и следуйте инструкциям
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. note::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Может пройти несколько минут, прежде чем добавленные в OlgramBot шаблоны появятся в списке вашего feedback бота
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,3 +24,6 @@ WEBHOOK_PORT=8443
 | 
				
			|||||||
CUSTOM_CERT=true
 | 
					CUSTOM_CERT=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
REDIS_PATH=redis://redis
 | 
					REDIS_PATH=redis://redis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set log level, can be CRITICAL, ERROR, WARNING, INFO, DEBUG. By default it set to INFO.
 | 
				
			||||||
 | 
					LOGLEVEL=
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
from olgram.router import dp
 | 
					from olgram.router import dp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from aiogram import types, Bot as AioBot
 | 
					from aiogram import types, Bot as AioBot
 | 
				
			||||||
from olgram.models.models import Bot, User
 | 
					from olgram.models.models import Bot, User, DefaultAnswer
 | 
				
			||||||
from aiogram.dispatcher import FSMContext
 | 
					from aiogram.dispatcher import FSMContext
 | 
				
			||||||
from aiogram.utils.callback_data import CallbackData
 | 
					from aiogram.utils.callback_data import CallbackData
 | 
				
			||||||
from textwrap import dedent
 | 
					from textwrap import dedent
 | 
				
			||||||
from olgram.utils.mix import edit_or_create, button_text_limit
 | 
					from olgram.utils.mix import edit_or_create, button_text_limit, wrap
 | 
				
			||||||
from olgram.commands import bot_actions
 | 
					from olgram.commands import bot_actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import typing as ty
 | 
					import typing as ty
 | 
				
			||||||
@ -158,7 +158,7 @@ async def send_bot_text_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] =
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    text = dedent("""
 | 
					    text = dedent("""
 | 
				
			||||||
    Сейчас вы редактируете текст, который отправляется после того, как пользователь отправит вашему боту {0}
 | 
					    Сейчас вы редактируете текст, который отправляется после того, как пользователь отправит вашему боту @{0}
 | 
				
			||||||
    команду /start
 | 
					    команду /start
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Текущий текст:
 | 
					    Текущий текст:
 | 
				
			||||||
@ -188,6 +188,11 @@ async def send_bot_second_text_menu(bot: Bot, call: ty.Optional[types.CallbackQu
 | 
				
			|||||||
                                   callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="text",
 | 
					                                   callback_data=menu_callback.new(level=2, bot_id=bot.id, operation="text",
 | 
				
			||||||
                                                                   chat=empty))
 | 
					                                                                   chat=empty))
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    keyboard.insert(
 | 
				
			||||||
 | 
					        types.InlineKeyboardButton(text="Шаблоны ответов...",
 | 
				
			||||||
 | 
					                                   callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="templates",
 | 
				
			||||||
 | 
					                                                                   chat=empty))
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    keyboard.insert(
 | 
					    keyboard.insert(
 | 
				
			||||||
        types.InlineKeyboardButton(text="Сбросить текст",
 | 
					        types.InlineKeyboardButton(text="Сбросить текст",
 | 
				
			||||||
                                   callback_data=menu_callback.new(level=3, bot_id=bot.id,
 | 
					                                   callback_data=menu_callback.new(level=3, bot_id=bot.id,
 | 
				
			||||||
@ -211,6 +216,43 @@ async def send_bot_second_text_menu(bot: Bot, call: ty.Optional[types.CallbackQu
 | 
				
			|||||||
        await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
 | 
					        await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def send_bot_templates_menu(bot: Bot, call: ty.Optional[types.CallbackQuery] = None,
 | 
				
			||||||
 | 
					                                  chat_id: ty.Optional[int] = None):
 | 
				
			||||||
 | 
					    if call:
 | 
				
			||||||
 | 
					        await call.answer()
 | 
				
			||||||
 | 
					    keyboard = types.InlineKeyboardMarkup(row_width=2)
 | 
				
			||||||
 | 
					    keyboard.insert(
 | 
				
			||||||
 | 
					        types.InlineKeyboardButton(text="<< Завершить редактирование",
 | 
				
			||||||
 | 
					                                   callback_data=menu_callback.new(level=1, bot_id=bot.id, operation=empty, chat=empty))
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    text = dedent("""
 | 
				
			||||||
 | 
					    Сейчас вы редактируете шаблоны ответов для @{0}. Текущие шаблоны:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <pre>
 | 
				
			||||||
 | 
					    {1}
 | 
				
			||||||
 | 
					    </pre>
 | 
				
			||||||
 | 
					    Отправьте какую-нибудь фразу (например: "Ваш заказ готов, ожидайте!"), чтобы добавить её в шаблон.
 | 
				
			||||||
 | 
					    Чтобы удалить шаблон из списка, отправьте его номер в списке (например, 4)
 | 
				
			||||||
 | 
					    """)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    templates = await bot.answers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    total_text_len = sum(len(t.text) for t in templates) + len(text)  # примерная длина текста
 | 
				
			||||||
 | 
					    max_len = 1000
 | 
				
			||||||
 | 
					    if total_text_len > 4000:
 | 
				
			||||||
 | 
					        max_len = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    templates_text = "\n".join(f"{n}. {wrap(template.text, max_len)}" for n, template in enumerate(templates))
 | 
				
			||||||
 | 
					    if not templates_text:
 | 
				
			||||||
 | 
					        templates_text = "(нет шаблонов)"
 | 
				
			||||||
 | 
					    text = text.format(bot.name, templates_text)
 | 
				
			||||||
 | 
					    if call:
 | 
				
			||||||
 | 
					        await edit_or_create(call, text, keyboard, parse_mode="HTML")
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        await AioBot.get_current().send_message(chat_id, text, reply_markup=keyboard, parse_mode="HTML")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@dp.message_handler(state="wait_start_text", content_types="text", regexp="^[^/].+")  # Not command
 | 
					@dp.message_handler(state="wait_start_text", content_types="text", regexp="^[^/].+")  # Not command
 | 
				
			||||||
async def start_text_received(message: types.Message, state: FSMContext):
 | 
					async def start_text_received(message: types.Message, state: FSMContext):
 | 
				
			||||||
    async with state.proxy() as proxy:
 | 
					    async with state.proxy() as proxy:
 | 
				
			||||||
@ -231,6 +273,38 @@ async def second_text_received(message: types.Message, state: FSMContext):
 | 
				
			|||||||
    await send_bot_second_text_menu(bot, chat_id=message.chat.id)
 | 
					    await send_bot_second_text_menu(bot, chat_id=message.chat.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@dp.message_handler(state="wait_template", content_types="text", regexp="^[^/](.+)?")  # Not command
 | 
				
			||||||
 | 
					async def template_received(message: types.Message, state: FSMContext):
 | 
				
			||||||
 | 
					    async with state.proxy() as proxy:
 | 
				
			||||||
 | 
					        bot_id = proxy.get("bot_id")
 | 
				
			||||||
 | 
					    bot = await Bot.get_or_none(pk=bot_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if message.text.isdigit():
 | 
				
			||||||
 | 
					        # Delete template
 | 
				
			||||||
 | 
					        number = int(message.text)
 | 
				
			||||||
 | 
					        templates = await bot.answers
 | 
				
			||||||
 | 
					        if not templates:
 | 
				
			||||||
 | 
					            await message.answer("У вас нет шаблонов, чтобы их удалять")
 | 
				
			||||||
 | 
					        if number < 0 or number >= len(templates):
 | 
				
			||||||
 | 
					            await message.answer(f"Неправильное число. Чтобы удалить шаблон, введите число от 0 до {len(templates)}")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        await templates[number].delete()
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        # Add template
 | 
				
			||||||
 | 
					        total_templates = len(await bot.answers)
 | 
				
			||||||
 | 
					        if total_templates > 30:
 | 
				
			||||||
 | 
					            await message.answer("У вашего бота уже слишком много шаблонов")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            answers = await bot.answers.filter(text=message.text)
 | 
				
			||||||
 | 
					            if answers:
 | 
				
			||||||
 | 
					                await message.answer("Такой текст уже есть в списке шаблонов")
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                template = DefaultAnswer(text=message.text, bot=bot)
 | 
				
			||||||
 | 
					                await template.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await send_bot_templates_menu(bot, chat_id=message.chat.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@dp.callback_query_handler(menu_callback.filter(), state="*")
 | 
					@dp.callback_query_handler(menu_callback.filter(), state="*")
 | 
				
			||||||
async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMContext):
 | 
					async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMContext):
 | 
				
			||||||
    level = callback_data.get("level")
 | 
					    level = callback_data.get("level")
 | 
				
			||||||
@ -245,6 +319,7 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
 | 
				
			|||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if level == "1":
 | 
					    if level == "1":
 | 
				
			||||||
 | 
					        await state.reset_state()
 | 
				
			||||||
        return await send_bot_menu(bot, call)
 | 
					        return await send_bot_menu(bot, call)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operation = callback_data.get("operation")
 | 
					    operation = callback_data.get("operation")
 | 
				
			||||||
@ -276,3 +351,8 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
 | 
				
			|||||||
        if operation == "reset_second_text":
 | 
					        if operation == "reset_second_text":
 | 
				
			||||||
            await bot_actions.reset_bot_second_text(bot, call)
 | 
					            await bot_actions.reset_bot_second_text(bot, call)
 | 
				
			||||||
            return await send_bot_second_text_menu(bot, call)
 | 
					            return await send_bot_second_text_menu(bot, call)
 | 
				
			||||||
 | 
					        if operation == "templates":
 | 
				
			||||||
 | 
					            await state.set_state("wait_template")
 | 
				
			||||||
 | 
					            async with state.proxy() as proxy:
 | 
				
			||||||
 | 
					                proxy["bot_id"] = bot.id
 | 
				
			||||||
 | 
					            return await send_bot_templates_menu(bot, call)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								olgram/migrations/models/7_20220210194635_update.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								olgram/migrations/models/7_20220210194635_update.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					-- upgrade --
 | 
				
			||||||
 | 
					CREATE TABLE IF NOT EXISTS "defaultanswer" (
 | 
				
			||||||
 | 
					    "id" BIGSERIAL NOT NULL PRIMARY KEY,
 | 
				
			||||||
 | 
					    "bot_id" INT NOT NULL REFERENCES "bot" ("id") ON DELETE CASCADE
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					-- downgrade --
 | 
				
			||||||
 | 
					DROP TABLE IF EXISTS "defaultanswer";
 | 
				
			||||||
							
								
								
									
										4
									
								
								olgram/migrations/models/8_20220210201740_update.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								olgram/migrations/models/8_20220210201740_update.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					-- upgrade --
 | 
				
			||||||
 | 
					ALTER TABLE "defaultanswer" ADD "text" TEXT NOT NULL;
 | 
				
			||||||
 | 
					-- downgrade --
 | 
				
			||||||
 | 
					ALTER TABLE "defaultanswer" DROP COLUMN "text";
 | 
				
			||||||
@ -83,3 +83,9 @@ class BannedUser(Model):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        table = "bot_banned_user"
 | 
					        table = "bot_banned_user"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DefaultAnswer(Model):
 | 
				
			||||||
 | 
					    id = fields.BigIntField(pk=True)
 | 
				
			||||||
 | 
					    bot = fields.ForeignKeyField("models.Bot", related_name="answers", on_delete=fields.relational.CASCADE)
 | 
				
			||||||
 | 
					    text = fields.TextField()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
from dotenv import load_dotenv
 | 
					from dotenv import load_dotenv
 | 
				
			||||||
from abc import ABC
 | 
					from abc import ABC
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
from olgram.utils.crypto import Cryptor
 | 
					from olgram.utils.crypto import Cryptor
 | 
				
			||||||
from functools import lru_cache
 | 
					from functools import lru_cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -30,7 +31,7 @@ class OlgramSettings(AbstractSettings):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def version(cls):
 | 
					    def version(cls):
 | 
				
			||||||
        return "0.2.0"
 | 
					        return "0.3.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    @lru_cache
 | 
					    @lru_cache
 | 
				
			||||||
@ -54,10 +55,6 @@ class ServerSettings(AbstractSettings):
 | 
				
			|||||||
    def hook_port(cls) -> int:
 | 
					    def hook_port(cls) -> int:
 | 
				
			||||||
        return int(cls._get_env("WEBHOOK_PORT"))
 | 
					        return int(cls._get_env("WEBHOOK_PORT"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def app_host(cls) -> str:
 | 
					 | 
				
			||||||
        return "olgram"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def app_port(cls) -> int:
 | 
					    def app_port(cls) -> int:
 | 
				
			||||||
        return 80
 | 
					        return 80
 | 
				
			||||||
@ -87,6 +84,8 @@ class ServerSettings(AbstractSettings):
 | 
				
			|||||||
    def append_text(cls) -> str:
 | 
					    def append_text(cls) -> str:
 | 
				
			||||||
        return "\n\nЭтот бот создан с помощью @OlgramBot"
 | 
					        return "\n\nЭтот бот создан с помощью @OlgramBot"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BotSettings(AbstractSettings):
 | 
					class BotSettings(AbstractSettings):
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
 | 
				
			|||||||
@ -22,8 +22,11 @@ async def edit_or_create(call: CallbackQuery, message: str,
 | 
				
			|||||||
                                    parse_mode=parse_mode)
 | 
					                                    parse_mode=parse_mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def button_text_limit(data: str) -> str:
 | 
					def wrap(data: str, max_len: int) -> str:
 | 
				
			||||||
    max_len = 30
 | 
					 | 
				
			||||||
    if len(data) > max_len:
 | 
					    if len(data) > max_len:
 | 
				
			||||||
        data = data[:max_len-4] + "..."
 | 
					        data = data[:max_len-4] + "..."
 | 
				
			||||||
    return data
 | 
					    return data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def button_text_limit(data: str) -> str:
 | 
				
			||||||
 | 
					    return wrap(data, 30)
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ import logging
 | 
				
			|||||||
import typing as ty
 | 
					import typing as ty
 | 
				
			||||||
from olgram.settings import ServerSettings
 | 
					from olgram.settings import ServerSettings
 | 
				
			||||||
from olgram.models.models import Bot, GroupChat, BannedUser
 | 
					from olgram.models.models import Bot, GroupChat, BannedUser
 | 
				
			||||||
 | 
					from server.inlines import inline_handler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_logger = logging.getLogger(__name__)
 | 
					_logger = logging.getLogger(__name__)
 | 
				
			||||||
_logger.setLevel(logging.INFO)
 | 
					_logger.setLevel(logging.INFO)
 | 
				
			||||||
@ -90,8 +90,8 @@ async def message_handler(message: types.Message, *args, **kwargs):
 | 
				
			|||||||
                await message.reply("<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>",
 | 
					                await message.reply("<i>Невозможно переслать сообщение (автор заблокировал бота?)</i>",
 | 
				
			||||||
                                    parse_mode="HTML")
 | 
					                                    parse_mode="HTML")
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
        else:
 | 
					        elif super_chat_id > 0:
 | 
				
			||||||
            # в супер-чате кто-то пишет сообщение сам себе
 | 
					            # в супер-чате кто-то пишет сообщение сам себе, только для личных сообщений
 | 
				
			||||||
            await message.forward(super_chat_id)
 | 
					            await message.forward(super_chat_id)
 | 
				
			||||||
            # И отправить пользователю специальный текст, если он указан
 | 
					            # И отправить пользователю специальный текст, если он указан
 | 
				
			||||||
            if bot.second_text:
 | 
					            if bot.second_text:
 | 
				
			||||||
@ -136,6 +136,12 @@ async def receive_left(message: types.Message):
 | 
				
			|||||||
            await bot.save()
 | 
					            await bot.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def receive_inline(inline_query):
 | 
				
			||||||
 | 
					    _logger.info("inline handler")
 | 
				
			||||||
 | 
					    bot = db_bot_instance.get()
 | 
				
			||||||
 | 
					    return await inline_handler(inline_query, bot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def receive_migrate(message: types.Message):
 | 
					async def receive_migrate(message: types.Message):
 | 
				
			||||||
    bot = db_bot_instance.get()
 | 
					    bot = db_bot_instance.get()
 | 
				
			||||||
    from_id = message.chat.id
 | 
					    from_id = message.chat.id
 | 
				
			||||||
@ -175,6 +181,7 @@ class CustomRequestHandler(WebhookRequestHandler):
 | 
				
			|||||||
        dp.register_message_handler(receive_left, content_types=[types.ContentType.LEFT_CHAT_MEMBER])
 | 
					        dp.register_message_handler(receive_left, content_types=[types.ContentType.LEFT_CHAT_MEMBER])
 | 
				
			||||||
        dp.register_message_handler(receive_migrate, content_types=[types.ContentType.MIGRATE_TO_CHAT_ID])
 | 
					        dp.register_message_handler(receive_migrate, content_types=[types.ContentType.MIGRATE_TO_CHAT_ID])
 | 
				
			||||||
        dp.register_message_handler(receive_group_create, content_types=[types.ContentType.GROUP_CHAT_CREATED])
 | 
					        dp.register_message_handler(receive_group_create, content_types=[types.ContentType.GROUP_CHAT_CREATED])
 | 
				
			||||||
 | 
					        dp.register_inline_handler(receive_inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return dp
 | 
					        return dp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										56
									
								
								server/inlines.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								server/inlines.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					from aiocache import cached
 | 
				
			||||||
 | 
					import hashlib
 | 
				
			||||||
 | 
					from aiogram.types import InlineQuery, InputTextMessageContent, InlineQueryResultArticle
 | 
				
			||||||
 | 
					from aiogram.bot import Bot as AioBot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from olgram.models.models import Bot
 | 
				
			||||||
 | 
					import typing as ty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@cached(ttl=60)
 | 
				
			||||||
 | 
					async def get_phrases(bot: Bot) -> ty.List:
 | 
				
			||||||
 | 
					    objects = await bot.answers
 | 
				
			||||||
 | 
					    return [obj.text for obj in objects]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def check_chat_member(chat_id: int, user_id: int, bot: AioBot) -> bool:
 | 
				
			||||||
 | 
					    member = await bot.get_chat_member(chat_id, user_id)
 | 
				
			||||||
 | 
					    return member.is_chat_member()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@cached(ttl=60)
 | 
				
			||||||
 | 
					async def check_permissions(inline_query: InlineQuery, bot: Bot):
 | 
				
			||||||
 | 
					    user_id = inline_query.from_user.id
 | 
				
			||||||
 | 
					    super_chat_id = await bot.super_chat_id()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if super_chat_id == user_id:
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if super_chat_id < 0:  # Group chat
 | 
				
			||||||
 | 
					        is_member = await check_chat_member(super_chat_id, user_id, inline_query.bot)
 | 
				
			||||||
 | 
					        return is_member
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def inline_handler(inline_query: InlineQuery, bot: Bot):
 | 
				
			||||||
 | 
					    # Check permissions at first
 | 
				
			||||||
 | 
					    allow = await check_permissions(inline_query, bot)
 | 
				
			||||||
 | 
					    if not allow:
 | 
				
			||||||
 | 
					        return await inline_query.answer([])  # forbidden
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    all_phrases = await get_phrases(bot)
 | 
				
			||||||
 | 
					    phrases = [phrase for phrase in all_phrases if inline_query.query.lower() in phrase.lower()]
 | 
				
			||||||
 | 
					    items = []
 | 
				
			||||||
 | 
					    for phrase in phrases:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        input_content = InputTextMessageContent(phrase)
 | 
				
			||||||
 | 
					        result_id: str = hashlib.md5(phrase.encode()).hexdigest()
 | 
				
			||||||
 | 
					        item = InlineQueryResultArticle(
 | 
				
			||||||
 | 
					            id=result_id,
 | 
				
			||||||
 | 
					            title=phrase,
 | 
				
			||||||
 | 
					            input_message_content=input_content,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        items.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await inline_query.answer(results=items)
 | 
				
			||||||
@ -65,5 +65,5 @@ def main():
 | 
				
			|||||||
    runner = web.AppRunner(app)
 | 
					    runner = web.AppRunner(app)
 | 
				
			||||||
    loop.run_until_complete(runner.setup())
 | 
					    loop.run_until_complete(runner.setup())
 | 
				
			||||||
    logger.info("Server initialization done")
 | 
					    logger.info("Server initialization done")
 | 
				
			||||||
    site = web.TCPSite(runner, host=ServerSettings.app_host(), port=ServerSettings.app_port(), ssl_context=context)
 | 
					    site = web.TCPSite(runner, port=ServerSettings.app_port(), ssl_context=context)
 | 
				
			||||||
    return site
 | 
					    return site
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user