1
0
mirror of https://github.com/civsocit/olgram.git synced 2025-12-17 08:26:17 +00:00

23 Commits

Author SHA1 Message Date
er8dd
2305590899 f 2024-03-22 04:16:38 +04:00
er8dd
a34e633b98 version bump 2024-03-22 04:14:07 +04:00
er8dd
16da3634db telegram anti-flood 2024-03-22 04:13:32 +04:00
er8dd
a0c6c9415e tags 2024-03-22 03:55:50 +04:00
er8dd
dab803a4e8 tags 2024-03-22 03:49:05 +04:00
er8dd
0ceea778fe merge 2024-03-22 03:46:34 +04:00
er8dd
7ce6df7fd9 bot tags 2024-03-22 03:44:34 +04:00
er8dd
59da56d463 locale 2024-03-17 14:35:34 +04:00
er8dd
1c12730a4e oh you are idiot 2024-03-17 14:25:07 +04:00
er8dd
d0b570baa9 version 2024-03-15 03:07:31 +04:00
er8dd
1b1fe239f8 tag first iteration 2024-03-15 02:59:49 +04:00
er8dd
214824db14 typo 2024-03-15 01:46:51 +04:00
er8dd
147fc2a665 docs 2024-03-03 10:21:00 +04:00
er8dd
c027ec656b minor fixes 2024-03-03 00:30:23 +04:00
er8dd
f79a9f317d Merge branch 'main' into stable 2024-03-03 00:10:32 +04:00
er8dd
eeb6c65f36 minor fixes 2024-03-03 00:07:45 +04:00
er8dd
7a0ce10c56 mailing size limit 2024-03-02 20:50:40 +04:00
er8dd
44f39e4de0 Merge branch 'main' into stable 2024-03-02 20:05:23 +04:00
jjki3d
7aacb2e38f Merge branch 'main' into stable 2024-02-17 03:56:11 +04:00
jjki3d
1dd4d4d7fd Merge branch 'main' into stable 2024-01-13 02:29:57 +04:00
jjki3d
02e91a596c Merge branch 'main' into stable 2024-01-13 02:20:53 +04:00
walker
a262d4e488 Merge branch 'main' into stable 2022-11-05 06:11:27 +04:00
mihalin
3e1f89034a fix translations 2022-09-16 21:30:38 +04:00
10 changed files with 111 additions and 15 deletions

View File

@@ -10,7 +10,10 @@ Bitcoin:
``bc1qlq7cm5chc8flr3fy8ewk967aknq3dwmxtwn9hl`` ``bc1qlq7cm5chc8flr3fy8ewk967aknq3dwmxtwn9hl``
Litecoin: Litecoin:
``LTC1QXAJSVZ0LW44AA5NYTUCH8CP2G8X7A4CDASE4Y7`` ``ltc1qxajsvz0lw44aa5nytuch8cp2g8x7a4cdase4y7``
Monero:
``84ymMfpw3vxFxsgmYbFURMiZLgQCmhKsZNiZiqZRbpH2WRka2UDjyDVZpX8XH1cZ9d5EghvPXrF5hEuzvK5NvHGE8za4Gmk``
Как убрать "Этот бот создан с помощью ..." Как убрать "Этот бот создан с помощью ..."
---------------- ----------------
@@ -20,6 +23,8 @@ Litecoin:
История изменений История изменений
---------------- ----------------
- `2024-03-15` Тэги
- `2024-03-02` Рассылки
- `2024-03-01` Непрерывные потоки сообщений (опция) - `2024-03-01` Непрерывные потоки сообщений (опция)
- `2024-02-17` Опция смены режима работы автоответчика: автоответчик отвечает на КАЖДОЕ сообщение - `2024-02-17` Опция смены режима работы автоответчика: автоответчик отвечает на КАЖДОЕ сообщение
- `2024-01-12` Мультиязычность (стартовое сообщение и автоответчик) - `2024-01-12` Мультиязычность (стартовое сообщение и автоответчик)

View File

@@ -115,3 +115,14 @@ BotFather - это официальный бот Telegram, создающий д
Если у вас возникли вопросы по использованию бота, или вы нашли ошибку - напишите Если у вас возникли вопросы по использованию бота, или вы нашли ошибку - напишите
нам `@civsocit_feedback_bot <https://t.me/civsocit_feedback_bot>`_. нам `@civsocit_feedback_bot <https://t.me/civsocit_feedback_bot>`_.
Тэги пользователей
------------------
Пользователям можно проставлять теги. Например, в ответ на сообщение пользователя написать:
```
/tag #important
```
Тогда в user info (см. раздел опции) помимо информации о пользователе будет тег #important

View File

@@ -7,9 +7,9 @@ from olgram.settings import TORTOISE_ORM, OlgramSettings
from olgram.utils.permissions import AccessMiddleware from olgram.utils.permissions import AccessMiddleware
from server.custom import init_redis from server.custom import init_redis
import olgram.commands.menu # noqa: F401
import olgram.commands.bots # noqa: F401 import olgram.commands.bots # noqa: F401
import olgram.commands.start # noqa: F401 import olgram.commands.start # noqa: F401
import olgram.commands.menu # noqa: F401
import olgram.commands.bot_actions # noqa: F401 import olgram.commands.bot_actions # noqa: F401
import olgram.commands.info # noqa: F401 import olgram.commands.info # noqa: F401
import olgram.commands.promo # noqa: F401 import olgram.commands.promo # noqa: F401

View File

@@ -140,6 +140,11 @@ async def mailing(bot: Bot, call: types.CallbackQuery):
await bot.save(update_fields=["enable_mailing"]) await bot.save(update_fields=["enable_mailing"])
async def tags(bot: Bot, call: types.CallbackQuery):
bot.enable_tags = not bot.enable_tags
await bot.save(update_fields=["enable_tags"])
async def go_mailing(bot: Bot, context: dict) -> int: async def go_mailing(bot: Bot, context: dict) -> int:
users = await bot.mailing_users users = await bot.mailing_users
a_bot = AioBot(bot.decrypted_token()) a_bot = AioBot(bot.decrypted_token())

View File

@@ -197,6 +197,12 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
operation="thread_interrupt", operation="thread_interrupt",
chat=empty)) chat=empty))
) )
keyboard.insert(
types.InlineKeyboardButton(text=_("Теги"),
callback_data=menu_callback.new(level=3, bot_id=bot.id,
operation="tags",
chat=empty))
)
is_promo = await bot.is_promo() is_promo = await bot.is_promo()
if is_promo: if is_promo:
keyboard.insert( keyboard.insert(
@@ -217,6 +223,7 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
enable_always_second_message = _("включён") if bot.enable_always_second_message else _("выключен") enable_always_second_message = _("включён") if bot.enable_always_second_message else _("выключен")
thread_interrupt = _("да") if bot.enable_thread_interrupt else _("нет") thread_interrupt = _("да") if bot.enable_thread_interrupt else _("нет")
mailing_turn = _("включена") if bot.enable_mailing else _("выключена") mailing_turn = _("включена") if bot.enable_mailing else _("выключена")
tags_turn = _("включены") if bot.enable_tags else _("выключены")
text = dedent(_(""" text = dedent(_("""
<a href="https://olgram.readthedocs.io/ru/latest/options.html#threads">Потоки сообщений</a>: <b>{0}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#threads">Потоки сообщений</a>: <b>{0}</b>
<a href="https://olgram.readthedocs.io/ru/latest/options.html#user-info">Данные пользователя</a>: <b>{1}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#user-info">Данные пользователя</a>: <b>{1}</b>
@@ -224,8 +231,9 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
<a href="https://olgram.readthedocs.io/ru/latest/options.html#always_second_message">Автоответчик всегда</a>: <b>{3}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#always_second_message">Автоответчик всегда</a>: <b>{3}</b>
<a href="https://olgram.readthedocs.io/ru/latest/options.html#thread_interrupt">Прерывать поток</a>: <b>{4}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#thread_interrupt">Прерывать поток</a>: <b>{4}</b>
<a href="https://olgram.readthedocs.io/ru/latest/options.html#mailing">Рассылка</a>: <b>{5}</b> <a href="https://olgram.readthedocs.io/ru/latest/options.html#mailing">Рассылка</a>: <b>{5}</b>
Теги: <b>{6}</b>
""")).format(thread_turn, info_turn, antiflood_turn, enable_always_second_message, thread_interrupt, """)).format(thread_turn, info_turn, antiflood_turn, enable_always_second_message, thread_interrupt,
mailing_turn) mailing_turn, tags_turn)
if is_promo: if is_promo:
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена") olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
@@ -358,6 +366,8 @@ async def mailing_text_received(message: types.Message, state: FSMContext):
obj = message.audio obj = message.audio
elif message.content_type == types.ContentType.VIDEO: elif message.content_type == types.ContentType.VIDEO:
obj = message.video obj = message.video
if obj.file_size and obj.file_size > 4194304:
return await message.answer(_("Слишком большой файл (4 Мб максимум)"))
try: try:
await obj.download(buffer, timeout=5) await obj.download(buffer, timeout=5)
@@ -366,10 +376,14 @@ async def mailing_text_received(message: types.Message, state: FSMContext):
logging.error(err, exc_info=True) logging.error(err, exc_info=True)
return await message.answer(_("Не удалось загрузить файл (слишком большой размер?)")) return await message.answer(_("Не удалось загрузить файл (слишком большой размер?)"))
proxy["mailing_data"] = buffer.getvalue() proxy["mailing_data"] = buffer.getvalue()
proxy["mailing_file_name"] = getattr(obj, "file_name") proxy["mailing_file_name"] = getattr(obj, "file_name", None)
_message_id = await send_stored_message(proxy, AioBot.get_current(), message.chat.id) _message_id = await send_stored_message(proxy, AioBot.get_current(), message.chat.id)
keyboard = types.InlineKeyboardMarkup(row_width=1) keyboard = types.InlineKeyboardMarkup(row_width=1)
keyboard.insert(
types.InlineKeyboardButton(text=_("<< Отменить рассылку"),
callback_data=menu_callback.new(level=1, bot_id=bot_id, operation=empty, chat=empty))
)
keyboard.insert( keyboard.insert(
types.InlineKeyboardButton(text=_("Да, начать рассылку"), types.InlineKeyboardButton(text=_("Да, начать рассылку"),
callback_data=menu_callback.new(level=3, bot_id=bot_id, operation="go_go_mailing", callback_data=menu_callback.new(level=3, bot_id=bot_id, operation="go_go_mailing",
@@ -666,6 +680,9 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
if operation == "mailing": if operation == "mailing":
await bot_actions.mailing(bot, call) await bot_actions.mailing(bot, call)
return await send_bot_settings_menu(bot, call) return await send_bot_settings_menu(bot, call)
if operation == "tags":
await bot_actions.tags(bot, call)
return await send_bot_settings_menu(bot, call)
if operation == "thread_interrupt": if operation == "thread_interrupt":
await bot_actions.thread_interrupt(bot, call) await bot_actions.thread_interrupt(bot, call)
return await send_bot_settings_menu(bot, call) return await send_bot_settings_menu(bot, call)

View File

@@ -0,0 +1,4 @@
-- upgrade --
ALTER TABLE "bot_start_message" ALTER COLUMN "locale" TYPE VARCHAR(15) USING "locale"::VARCHAR(15);
-- downgrade --
ALTER TABLE "bot_start_message" ALTER COLUMN "locale" TYPE VARCHAR(5) USING "locale"::VARCHAR(5);

View File

@@ -0,0 +1,4 @@
-- upgrade --
ALTER TABLE "bot" ADD "enable_tags" BOOL NOT NULL DEFAULT False;
-- downgrade --
ALTER TABLE "bot" DROP COLUMN "enable_tags";

View File

@@ -49,6 +49,7 @@ class Bot(Model):
enable_always_second_message = fields.BooleanField(default=False) enable_always_second_message = fields.BooleanField(default=False)
enable_thread_interrupt = fields.BooleanField(default=True) enable_thread_interrupt = fields.BooleanField(default=True)
enable_mailing = fields.BooleanField(default=False) enable_mailing = fields.BooleanField(default=False)
enable_tags = fields.BooleanField(default=False)
last_mailing_at = fields.DatetimeField(null=True, default=None) last_mailing_at = fields.DatetimeField(null=True, default=None)
def decrypted_token(self): def decrypted_token(self):
@@ -77,7 +78,7 @@ class Bot(Model):
class BotStartMessage(Model): class BotStartMessage(Model):
id = fields.IntField(pk=True) id = fields.IntField(pk=True)
bot = fields.ForeignKeyField("models.Bot", related_name="start_texts", on_delete=fields.CASCADE) bot = fields.ForeignKeyField("models.Bot", related_name="start_texts", on_delete=fields.CASCADE)
locale = fields.CharField(max_length=5) locale = fields.CharField(max_length=15)
text = fields.TextField() text = fields.TextField()
class Meta: class Meta:

View File

@@ -40,7 +40,7 @@ class OlgramSettings(AbstractSettings):
@classmethod @classmethod
def version(cls): def version(cls):
return "0.7.1" return "0.7.3"
@classmethod @classmethod
@lru_cache @lru_cache

View File

@@ -40,7 +40,11 @@ def _message_unique_id(bot_id: int, message_id: int) -> str:
return f"{bot_id}_{message_id}" return f"{bot_id}_{message_id}"
def _thread_uniqie_id(bot_id: int, chat_id: int) -> str: def _tag_uid(bot_id: int, user_id: int) -> str:
return f"tag_{bot_id}_{user_id}"
def _thread_unique_id(bot_id: int, chat_id: int) -> str:
return f"thread_{bot_id}_{chat_id}" return f"thread_{bot_id}_{chat_id}"
@@ -77,7 +81,7 @@ def _on_security_policy(message: types.Message, bot):
parse_mode="HTML") parse_mode="HTML")
async def send_user_message(message: types.Message, super_chat_id: int, bot): async def send_user_message(message: types.Message, super_chat_id: int, bot, tag: str = ""):
"""Переслать сообщение от пользователя, добавлять к нему user info при необходимости""" """Переслать сообщение от пользователя, добавлять к нему user info при необходимости"""
if bot.enable_additional_info: if bot.enable_additional_info:
user_info = _("Сообщение от пользователя ") user_info = _("Сообщение от пользователя ")
@@ -89,6 +93,9 @@ async def send_user_message(message: types.Message, super_chat_id: int, bot):
user_info += f" | lang: {message.from_user.locale}" user_info += f" | lang: {message.from_user.locale}"
if message.forward_sender_name: if message.forward_sender_name:
user_info += f" | fwd: {message.forward_sender_name}" user_info += f" | fwd: {message.forward_sender_name}"
tag = await _redis.get(_tag_uid(bot.pk, message.from_user.id), encoding="utf-8")
if tag:
user_info += f" | tag: {tag}"
# Добавлять информацию в конец текста # Добавлять информацию в конец текста
if message.content_type == types.ContentType.TEXT \ if message.content_type == types.ContentType.TEXT \
@@ -99,6 +106,15 @@ async def send_user_message(message: types.Message, super_chat_id: int, bot):
new_message_2 = await message.copy_to(super_chat_id, reply_to_message_id=new_message.message_id) new_message_2 = await message.copy_to(super_chat_id, reply_to_message_id=new_message.message_id)
await _redis.set(_message_unique_id(bot.pk, new_message_2.message_id), message.chat.id, await _redis.set(_message_unique_id(bot.pk, new_message_2.message_id), message.chat.id,
pexpire=ServerSettings.redis_timeout_ms()) pexpire=ServerSettings.redis_timeout_ms())
elif tag:
# добавлять тег в конец текста
if message.content_type == types.ContentType.TEXT and len(message.text) + len(tag) < 4093:
new_message = await message.bot.send_message(super_chat_id, message.text + "\n\n" + tag)
else:
new_message = await message.bot.send_message(super_chat_id, text=tag)
new_message_2 = await message.copy_to(super_chat_id, reply_to_message_id=new_message.message_id)
await _redis.set(_message_unique_id(bot.pk, new_message_2.message_id), message.chat.id,
pexpire=ServerSettings.redis_timeout_ms())
else: else:
try: try:
new_message = await message.forward(super_chat_id) new_message = await message.forward(super_chat_id)
@@ -112,29 +128,50 @@ async def send_user_message(message: types.Message, super_chat_id: int, bot):
async def send_to_superchat(is_super_group: bool, message: types.Message, super_chat_id: int, bot): async def send_to_superchat(is_super_group: bool, message: types.Message, super_chat_id: int, bot):
"""Пересылка сообщения от пользователя оператору (логика потоков сообщений)""" """Пересылка сообщения от пользователя оператору (логика потоков сообщений)"""
if bot.enable_tags:
tag = await _redis.get(_tag_uid(bot.pk, message.chat.id), encoding="utf-8")
else:
tag = ""
if tag:
tag = str(tag)
if is_super_group and bot.enable_threads: if is_super_group and bot.enable_threads:
if bot.enable_thread_interrupt: if bot.enable_thread_interrupt:
thread_timeout = ServerSettings.thread_timeout_ms() thread_timeout = ServerSettings.thread_timeout_ms()
else: else:
thread_timeout = ServerSettings.redis_timeout_ms() thread_timeout = ServerSettings.redis_timeout_ms()
thread_first_message = await _redis.get(_thread_uniqie_id(bot.pk, message.chat.id)) thread_first_message = await _redis.get(_thread_unique_id(bot.pk, message.chat.id))
if thread_first_message: if thread_first_message:
# переслать в супер-чат, отвечая на предыдущее сообщение # переслать в супер-чат, отвечая на предыдущее сообщение
try: try:
if tag:
if message.content_type == types.ContentType.TEXT and len(message.text) + len(tag) < 4093:
new_message = await message.bot.send_message(
super_chat_id,
message.text + "\n\n" + tag,
reply_to_message_id=int(thread_first_message))
else:
new_message = await message.copy_to(super_chat_id,
reply_to_message_id=int(thread_first_message))
new_message_2 = await message.bot.send_message(
super_chat_id, reply_to_message_id=new_message.message_id, text=tag)
await _redis.set(_message_unique_id(bot.pk, new_message_2.message_id), message.chat.id,
pexpire=thread_timeout)
else:
new_message = await message.copy_to(super_chat_id, reply_to_message_id=int(thread_first_message)) new_message = await message.copy_to(super_chat_id, reply_to_message_id=int(thread_first_message))
await _redis.set(_message_unique_id(bot.pk, new_message.message_id), message.chat.id, await _redis.set(_message_unique_id(bot.pk, new_message.message_id), message.chat.id,
pexpire=thread_timeout) pexpire=thread_timeout)
except exceptions.BadRequest: except exceptions.BadRequest:
new_message = await send_user_message(message, super_chat_id, bot) new_message = await send_user_message(message, super_chat_id, bot, tag)
await _redis.set( await _redis.set(
_thread_uniqie_id(bot.pk, message.chat.id), new_message.message_id, pexpire=thread_timeout) _thread_unique_id(bot.pk, message.chat.id), new_message.message_id, pexpire=thread_timeout)
else: else:
# переслать супер-чат # переслать супер-чат
new_message = await send_user_message(message, super_chat_id, bot) new_message = await send_user_message(message, super_chat_id, bot, tag)
await _redis.set(_thread_uniqie_id(bot.pk, message.chat.id), new_message.message_id, await _redis.set(_thread_unique_id(bot.pk, message.chat.id), new_message.message_id,
pexpire=thread_timeout) pexpire=thread_timeout)
else: # личные сообщения не поддерживают потоки сообщений: просто отправляем сообщение else: # личные сообщения не поддерживают потоки сообщений: просто отправляем сообщение
await send_user_message(message, super_chat_id, bot) await send_user_message(message, super_chat_id, bot, tag)
async def _increase_count(_bot): async def _increase_count(_bot):
@@ -168,6 +205,9 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
await send_to_superchat(is_super_group, message, super_chat_id, bot) await send_to_superchat(is_super_group, message, super_chat_id, bot)
except (exceptions.Unauthorized, exceptions.ChatNotFound): except (exceptions.Unauthorized, exceptions.ChatNotFound):
return SendMessage(chat_id=message.chat.id, text=_("Не удаётся связаться с владельцем бота")) return SendMessage(chat_id=message.chat.id, text=_("Не удаётся связаться с владельцем бота"))
except exceptions.RetryAfter:
return SendMessage(chat_id=message.chat.id, text=_("Слишком много сообщений, подождите одну минуту"),
reply_to_message_id=message.message_id)
except exceptions.TelegramAPIError as err: except exceptions.TelegramAPIError as err:
_logger.error(f"(exception on forwarding) {err}") _logger.error(f"(exception on forwarding) {err}")
return return
@@ -216,6 +256,15 @@ async def handle_operator_message(message: types.Message, super_chat_id: int, bo
else: else:
await banned_user.delete() await banned_user.delete()
return SendMessage(chat_id=message.chat.id, text=_("Пользователь разбанен")) return SendMessage(chat_id=message.chat.id, text=_("Пользователь разбанен"))
if bot.enable_tags:
if message.text and message.text.startswith("/tag "):
tag = message.text.replace("/tag ", "")[:20].strip()
if tag:
await _redis.set(_tag_uid(bot.pk, chat_id), tag, pexpire=ServerSettings.redis_timeout_ms())
return SendMessage(chat_id=message.chat.id, text=_("Тег выставлен"))
else:
await _redis.delete(_tag_uid(bot.pk, chat_id))
return SendMessage(chat_id=message.chat.id, text=_("Тег убран"))
try: try:
await message.copy_to(chat_id) await message.copy_to(chat_id)