diff --git a/docs/source/additional.rst b/docs/source/additional.rst
index 7e4de35..f742141 100644
--- a/docs/source/additional.rst
+++ b/docs/source/additional.rst
@@ -19,6 +19,9 @@ Dash:
История изменений
----------------
+- `2022-08-01` Защита от флуда
+- `2022-07-23` Автоответчик не пишет сообщение лишний раз
+- `2022-07-04` Поддержка двух ботов в одном чате
- `2022-06-25` Поддержка HTML\Markdown в стартовом сообщении и автоответчике
- `2022-06-25` Пересылка отредактированных сообщений
- `2022-06-16` User info по возможности отправляется в том же сообщении, что и сообщение пользователя
diff --git a/docs/source/options.rst b/docs/source/options.rst
index c4c6ec0..dead073 100644
--- a/docs/source/options.rst
+++ b/docs/source/options.rst
@@ -41,3 +41,11 @@ Olgram пересылает сообщения так, чтобы сообщен
Включение этой опции меняет текст политики конфиденциальности вашего feedback бота (команда /security_policy)
и может отпугнуть некоторых пользователей. Не включайте эту опцию без необходимости.
+
+.. _antiflood:
+
+Защита от флуда
+---------------
+
+При включении этой опции пользователю запрещается отправлять больше одного сообщения в минуту. Используйте её, если
+не успеваете обрабатывать входящие сообщения.
diff --git a/locales/en/LC_MESSAGES/olgram.po b/locales/en/LC_MESSAGES/olgram.po
index 66f190d..bad1c2c 100644
--- a/locales/en/LC_MESSAGES/olgram.po
+++ b/locales/en/LC_MESSAGES/olgram.po
@@ -643,3 +643,9 @@ msgstr ""
msgid "Не удаётся связаться с владельцем бота"
msgstr "Cannot contact the owner of the bot"
+
+msgid "Слишком много сообщений, подождите одну минуту"
+msgstr "Too many messages, wait one minute"
+
+msgid "Антифлуд"
+msgstr "Antiflood"
diff --git a/locales/uk/LC_MESSAGES/olgram.po b/locales/uk/LC_MESSAGES/olgram.po
index e7f361d..c7311d2 100644
--- a/locales/uk/LC_MESSAGES/olgram.po
+++ b/locales/uk/LC_MESSAGES/olgram.po
@@ -654,3 +654,9 @@ msgstr ""
msgid "Не удаётся связаться с владельцем бота"
msgstr "Не вдається зв'язатися з власником бота"
+
+msgid "Слишком много сообщений, подождите одну минуту"
+msgstr "Забагато повідомлень, зачекайте одну хвилину"
+
+msgid "Антифлуд"
+msgstr "Антифлуд"
diff --git a/olgram/commands/bot_actions.py b/olgram/commands/bot_actions.py
index ec20f93..8bb3027 100644
--- a/olgram/commands/bot_actions.py
+++ b/olgram/commands/bot_actions.py
@@ -86,3 +86,8 @@ async def olgram_text(bot: Bot, call: types.CallbackQuery):
if await bot.is_promo():
bot.enable_olgram_text = not bot.enable_olgram_text
await bot.save(update_fields=["enable_olgram_text"])
+
+
+async def antiflood(bot: Bot, call: types.CallbackQuery):
+ bot.enable_antiflood = not bot.enable_antiflood
+ await bot.save(update_fields=["enable_antiflood"])
diff --git a/olgram/commands/menu.py b/olgram/commands/menu.py
index 079c239..0ba160d 100644
--- a/olgram/commands/menu.py
+++ b/olgram/commands/menu.py
@@ -162,6 +162,11 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="additional_info",
chat=empty))
)
+ keyboard.insert(
+ types.InlineKeyboardButton(text=_("Антифлуд"),
+ callback_data=menu_callback.new(level=3, bot_id=bot.id, operation="antiflood",
+ chat=empty))
+ )
is_promo = await bot.is_promo()
if is_promo:
keyboard.insert(
@@ -178,10 +183,12 @@ async def send_bot_settings_menu(bot: Bot, call: types.CallbackQuery):
thread_turn = _("включены") if bot.enable_threads else _("выключены")
info_turn = _("включены") if bot.enable_additional_info else _("выключены")
+ antiflood_turn = _("включен") if bot.enable_antiflood else _("выключен")
text = dedent(_("""
Потоки сообщений: {0}
Данные пользователя: {1}
- """)).format(thread_turn, info_turn)
+ Антифлуд: {2}
+ """)).format(thread_turn, info_turn, antiflood_turn)
if is_promo:
olgram_turn = _("включена") if bot.enable_olgram_text else _("выключена")
@@ -425,6 +432,9 @@ async def callback(call: types.CallbackQuery, callback_data: dict, state: FSMCon
if operation == "threads":
await bot_actions.threads(bot, call)
return await send_bot_settings_menu(bot, call)
+ if operation == "antiflood":
+ await bot_actions.antiflood(bot, call)
+ return await send_bot_settings_menu(bot, call)
if operation == "additional_info":
await bot_actions.additional_info(bot, call)
return await send_bot_settings_menu(bot, call)
diff --git a/olgram/models/models.py b/olgram/models/models.py
index 6fab6ed..2209bd1 100644
--- a/olgram/models/models.py
+++ b/olgram/models/models.py
@@ -45,6 +45,7 @@ class Bot(Model):
enable_threads = fields.BooleanField(default=False)
enable_additional_info = fields.BooleanField(default=False)
enable_olgram_text = fields.BooleanField(default=True)
+ enable_antiflood = fields.BooleanField(default=False)
def decrypted_token(self):
cryptor = DatabaseSettings.cryptor()
diff --git a/server/custom.py b/server/custom.py
index 4c8f86b..8ae69e8 100644
--- a/server/custom.py
+++ b/server/custom.py
@@ -46,6 +46,10 @@ def _last_message_uid(bot_id: int, chat_id: int) -> str:
return f"lm_{bot_id}_{chat_id}"
+def _antiflood_marker_uid(bot_id: int, chat_id: int) -> str:
+ return f"af_{bot_id}_{chat_id}"
+
+
def _on_security_policy(message: types.Message, bot):
_ = _get_translator(message)
text = _("Политика конфиденциальности\n\n"
@@ -130,6 +134,13 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
return SendMessage(chat_id=message.chat.id,
text=_("Вы заблокированы в этом боте"))
+ # Проверить анти-флуд
+ if bot.enable_antiflood:
+ if await _redis.get(_antiflood_marker_uid(bot.pk, message.chat.id)):
+ return SendMessage(chat_id=message.chat.id,
+ text=_("Слишком много сообщений, подождите одну минуту"))
+ await _redis.setex(_antiflood_marker_uid(bot.pk, message.chat.id), 60, 1)
+
# Пересылаем сообщение в супер-чат
try:
await send_to_superchat(is_super_group, message, super_chat_id, bot)
@@ -144,8 +155,8 @@ async def handle_user_message(message: types.Message, super_chat_id: int, bot):
# И отправить пользователю специальный текст, если он указан и если давно не отправляли
if bot.second_text:
- send_auto = not await _redis.get(_last_message_uid(message.bot.id, message.chat.id))
- await _redis.setex(_last_message_uid(message.bot.id, message.chat.id), 60 * 60 * 3, 1)
+ send_auto = not await _redis.get(_last_message_uid(bot.pk, message.chat.id))
+ await _redis.setex(_last_message_uid(bot.pk, message.chat.id), 60 * 60 * 3, 1)
if send_auto:
return SendMessage(chat_id=message.chat.id, text=bot.second_text, parse_mode="HTML")