Нормализовать переводы строк в LF
Решение по TD-STYLE-ANALYZERS: LF — инструменты проекта (Python/Node) пишут LF, CRLF-.sh не работают на Linux CI (sh scripts/ci.sh), большинство файлов уже были LF. Добавлен .gitattributes (* text=auto eol=lf, бинарные исключения), .editorconfig переведён на lf, 1029 файлов конвертированы, git add --renormalize. Из индекса убраны закравшиеся archive/**/__pycache__/*.pyc.
This commit is contained in:
@@ -1,80 +1,80 @@
|
||||
"""BanGuard: квоты, паузы и защита от flood для авто-вступлений Discovery.
|
||||
|
||||
Суточный лимит (discJoinLimit) считается по disc_log (event='join_auto')
|
||||
за текущие UTC-сутки; между вступлениями выдерживается случайная пауза
|
||||
(discJoinDelayMin/Max). При FloodWait от Telegram ставится блокировка до
|
||||
конца суток (discFloodDay), плюс есть ручной стоп-кран (discPaused).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import random
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from ..db import store
|
||||
|
||||
_KEY_JOIN_LIMIT = "discJoinLimit"
|
||||
_KEY_DELAY_MIN = "discJoinDelayMin"
|
||||
_KEY_DELAY_MAX = "discJoinDelayMax"
|
||||
_KEY_FLOOD_DAY = "discFloodDay"
|
||||
_KEY_PAUSED = "discPaused"
|
||||
|
||||
|
||||
def _start_of_day_ms() -> int:
|
||||
"""Начало текущих UTC-суток в миллисекундах."""
|
||||
start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
return int(start.timestamp() * 1000)
|
||||
|
||||
|
||||
def joins_today_auto() -> int:
|
||||
"""Авто-вступления за текущие UTC-сутки (disc_log, event='join_auto')."""
|
||||
row = store.scalar(
|
||||
"SELECT count(*) FROM disc_log WHERE event = 'join_auto' AND created_at >= ?",
|
||||
[_start_of_day_ms()],
|
||||
)
|
||||
return int(row or 0)
|
||||
|
||||
|
||||
def can_auto_join() -> bool:
|
||||
"""Разрешено ли авто-вступление: лимит не исчерпан, нет flood на сегодня, нет паузы."""
|
||||
limit = int(store.get_setting(_KEY_JOIN_LIMIT) or 0)
|
||||
return joins_today_auto() < limit and not flood_today() and not global_paused()
|
||||
|
||||
|
||||
async def wait_join_delay() -> None:
|
||||
"""Случайная пауза перед авто-вступлением (сек, из настроек).
|
||||
|
||||
Защита инварианта min <= max: настройки мог изменить один конец интервала
|
||||
(single-key PATCH), поэтому при инверсии концы меняются местами.
|
||||
"""
|
||||
low = float(store.get_setting(_KEY_DELAY_MIN) or 0)
|
||||
high = float(store.get_setting(_KEY_DELAY_MAX) or 0)
|
||||
if low <= 0 and high <= 0:
|
||||
return # обе настройки не заданы — паузы нет (крайний случай)
|
||||
if low > high:
|
||||
low, high = high, low
|
||||
await asyncio.sleep(random.uniform(low, high))
|
||||
|
||||
|
||||
def note_flood() -> None:
|
||||
"""Зафиксировать FloodWait: блокировка авто-вступлений до конца суток."""
|
||||
store.set_setting(_KEY_FLOOD_DAY, _start_of_day_ms())
|
||||
|
||||
|
||||
def flood_today() -> bool:
|
||||
"""Была ли flood-блокировка в текущие UTC-сутки."""
|
||||
return int(store.get_setting(_KEY_FLOOD_DAY) or 0) == _start_of_day_ms()
|
||||
|
||||
|
||||
def global_paused() -> bool:
|
||||
"""Ручной стоп-кран авто-вступлений (setting discPaused)."""
|
||||
return bool(store.get_setting(_KEY_PAUSED))
|
||||
|
||||
|
||||
def set_global_pause(v: bool) -> None:
|
||||
store.set_setting(_KEY_PAUSED, bool(v))
|
||||
|
||||
|
||||
def search_pause() -> float:
|
||||
"""Пауза между поисковыми запросами Telegram (сек)."""
|
||||
return random.uniform(2.0, 4.0)
|
||||
"""BanGuard: квоты, паузы и защита от flood для авто-вступлений Discovery.
|
||||
|
||||
Суточный лимит (discJoinLimit) считается по disc_log (event='join_auto')
|
||||
за текущие UTC-сутки; между вступлениями выдерживается случайная пауза
|
||||
(discJoinDelayMin/Max). При FloodWait от Telegram ставится блокировка до
|
||||
конца суток (discFloodDay), плюс есть ручной стоп-кран (discPaused).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import random
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from ..db import store
|
||||
|
||||
_KEY_JOIN_LIMIT = "discJoinLimit"
|
||||
_KEY_DELAY_MIN = "discJoinDelayMin"
|
||||
_KEY_DELAY_MAX = "discJoinDelayMax"
|
||||
_KEY_FLOOD_DAY = "discFloodDay"
|
||||
_KEY_PAUSED = "discPaused"
|
||||
|
||||
|
||||
def _start_of_day_ms() -> int:
|
||||
"""Начало текущих UTC-суток в миллисекундах."""
|
||||
start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
return int(start.timestamp() * 1000)
|
||||
|
||||
|
||||
def joins_today_auto() -> int:
|
||||
"""Авто-вступления за текущие UTC-сутки (disc_log, event='join_auto')."""
|
||||
row = store.scalar(
|
||||
"SELECT count(*) FROM disc_log WHERE event = 'join_auto' AND created_at >= ?",
|
||||
[_start_of_day_ms()],
|
||||
)
|
||||
return int(row or 0)
|
||||
|
||||
|
||||
def can_auto_join() -> bool:
|
||||
"""Разрешено ли авто-вступление: лимит не исчерпан, нет flood на сегодня, нет паузы."""
|
||||
limit = int(store.get_setting(_KEY_JOIN_LIMIT) or 0)
|
||||
return joins_today_auto() < limit and not flood_today() and not global_paused()
|
||||
|
||||
|
||||
async def wait_join_delay() -> None:
|
||||
"""Случайная пауза перед авто-вступлением (сек, из настроек).
|
||||
|
||||
Защита инварианта min <= max: настройки мог изменить один конец интервала
|
||||
(single-key PATCH), поэтому при инверсии концы меняются местами.
|
||||
"""
|
||||
low = float(store.get_setting(_KEY_DELAY_MIN) or 0)
|
||||
high = float(store.get_setting(_KEY_DELAY_MAX) or 0)
|
||||
if low <= 0 and high <= 0:
|
||||
return # обе настройки не заданы — паузы нет (крайний случай)
|
||||
if low > high:
|
||||
low, high = high, low
|
||||
await asyncio.sleep(random.uniform(low, high))
|
||||
|
||||
|
||||
def note_flood() -> None:
|
||||
"""Зафиксировать FloodWait: блокировка авто-вступлений до конца суток."""
|
||||
store.set_setting(_KEY_FLOOD_DAY, _start_of_day_ms())
|
||||
|
||||
|
||||
def flood_today() -> bool:
|
||||
"""Была ли flood-блокировка в текущие UTC-сутки."""
|
||||
return int(store.get_setting(_KEY_FLOOD_DAY) or 0) == _start_of_day_ms()
|
||||
|
||||
|
||||
def global_paused() -> bool:
|
||||
"""Ручной стоп-кран авто-вступлений (setting discPaused)."""
|
||||
return bool(store.get_setting(_KEY_PAUSED))
|
||||
|
||||
|
||||
def set_global_pause(v: bool) -> None:
|
||||
store.set_setting(_KEY_PAUSED, bool(v))
|
||||
|
||||
|
||||
def search_pause() -> float:
|
||||
"""Пауза между поисковыми запросами Telegram (сек)."""
|
||||
return random.uniform(2.0, 4.0)
|
||||
|
||||
Reference in New Issue
Block a user