mirror of
https://github.com/qiyanaitsme/QIYANASAutoPumpLOLZ.git
synced 2026-09-23 04:37:43 +00:00
Add files via upload
This commit is contained in:
@@ -306,6 +306,7 @@ class APIClient:
|
|||||||
wait = self._rate_limit_wait_seconds(resp.headers, body)
|
wait = self._rate_limit_wait_seconds(resp.headers, body)
|
||||||
wait = wait if wait is not None else DEFAULT_429_WAIT_SECONDS
|
wait = wait if wait is not None else DEFAULT_429_WAIT_SECONDS
|
||||||
wait = min(wait, MAX_RATE_LIMIT_WAIT_SECONDS)
|
wait = min(wait, MAX_RATE_LIMIT_WAIT_SECONDS)
|
||||||
|
last_error = f"rate limited (429), waited {wait:.0f}s"
|
||||||
logger.warning(f"Batch rate-limited (429), waiting {wait:.0f}s (attempt {attempt + 1})")
|
logger.warning(f"Batch rate-limited (429), waiting {wait:.0f}s (attempt {attempt + 1})")
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
continue
|
continue
|
||||||
@@ -357,6 +358,7 @@ class APIClient:
|
|||||||
wait = self._rate_limit_wait_seconds(resp.headers, body)
|
wait = self._rate_limit_wait_seconds(resp.headers, body)
|
||||||
wait = wait if wait is not None else DEFAULT_429_WAIT_SECONDS
|
wait = wait if wait is not None else DEFAULT_429_WAIT_SECONDS
|
||||||
wait = min(wait, MAX_RATE_LIMIT_WAIT_SECONDS)
|
wait = min(wait, MAX_RATE_LIMIT_WAIT_SECONDS)
|
||||||
|
last_error = f"rate limited (429), waited {wait:.0f}s"
|
||||||
logger.warning(f"GET {path} rate-limited (429), waiting {wait:.0f}s")
|
logger.warning(f"GET {path} rate-limited (429), waiting {wait:.0f}s")
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -68,18 +68,22 @@ class AuthMiddleware(BaseMiddleware):
|
|||||||
event: TelegramObject,
|
event: TelegramObject,
|
||||||
data: dict[str, Any],
|
data: dict[str, Any],
|
||||||
) -> Any:
|
) -> Any:
|
||||||
from_user = getattr(event, "from_user", None)
|
# `event` is the raw Update; `.event` resolves it to whichever concrete
|
||||||
if from_user is None:
|
# sub-object is actually set (message, callback_query, chat_member, ...).
|
||||||
cq = getattr(event, "callback_query", None)
|
try:
|
||||||
if cq is not None:
|
actual_event = event.event
|
||||||
from_user = cq.from_user
|
except Exception:
|
||||||
else:
|
actual_event = event
|
||||||
msg = getattr(event, "message", None)
|
from_user = getattr(actual_event, "from_user", None)
|
||||||
if msg is not None:
|
|
||||||
from_user = msg.from_user
|
|
||||||
|
|
||||||
if from_user is None:
|
if from_user is None:
|
||||||
return await handler(event, data)
|
# Unknown identity (e.g. channel_post, poll, message_reaction) — fail
|
||||||
|
# closed instead of letting it through unauthenticated.
|
||||||
|
logger.warning(
|
||||||
|
f"AuthMiddleware: update without from_user "
|
||||||
|
f"(type={getattr(event, 'event_type', '?')}), denying"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
if from_user.id != self._admin_user_id:
|
if from_user.id != self._admin_user_id:
|
||||||
chat_id = None
|
chat_id = None
|
||||||
@@ -267,8 +271,8 @@ class AutoBumpBot:
|
|||||||
f"⏰ Автоподнятие каждые <b>{_format_interval(interval)}</b>"
|
f"⏰ Автоподнятие каждые <b>{_format_interval(interval)}</b>"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
except TelegramBadRequest as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send start message: {e}")
|
logger.error(f"Failed to send start message: {e}", exc_info=True)
|
||||||
await message.answer("❌ Ошибка отправки сообщения. Попробуйте /start снова.")
|
await message.answer("❌ Ошибка отправки сообщения. Попробуйте /start снова.")
|
||||||
|
|
||||||
# ─── Add Thread ─────────────────────────────────────────────
|
# ─── Add Thread ─────────────────────────────────────────────
|
||||||
@@ -783,12 +787,13 @@ class AutoBumpBot:
|
|||||||
f"✅ BUMP SUCCESS | Thread: {result.thread_id} | "
|
f"✅ BUMP SUCCESS | Thread: {result.thread_id} | "
|
||||||
f"Status: {result.status.value} | Message: {result.message}"
|
f"Status: {result.status.value} | Message: {result.message}"
|
||||||
)
|
)
|
||||||
await self._db.update_last_bumped(result.thread_id)
|
await self._db.record_bump_success(result.thread_id)
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"❌ BUMP FAILED | Thread: {result.thread_id} | "
|
f"❌ BUMP FAILED | Thread: {result.thread_id} | "
|
||||||
f"Status: {result.status.value} | Message: {result.message}"
|
f"Status: {result.status.value} | Message: {result.message}"
|
||||||
)
|
)
|
||||||
|
await self._db.record_bump_failure(result.thread_id)
|
||||||
|
|
||||||
total = len(results)
|
total = len(results)
|
||||||
await self._db.increment_bump_stats(success_count, total)
|
await self._db.increment_bump_stats(success_count, total)
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ class Config:
|
|||||||
|
|
||||||
scheduling = SchedulingConfig(
|
scheduling = SchedulingConfig(
|
||||||
bump_interval_minutes=interval_minutes,
|
bump_interval_minutes=interval_minutes,
|
||||||
bump_delay_seconds=float(os.getenv("BUMP_DELAY_SECONDS", "2")),
|
bump_delay_seconds=float(os.getenv("BUMP_DELAY_SECONDS", "1")),
|
||||||
enable_auto_bump=os.getenv("ENABLE_AUTO_BUMP", "true").lower() == "true",
|
enable_auto_bump=os.getenv("ENABLE_AUTO_BUMP", "true").lower() == "true",
|
||||||
scheduler_tick_seconds=tick,
|
scheduler_tick_seconds=tick,
|
||||||
)
|
)
|
||||||
|
|||||||
+65
-7
@@ -73,15 +73,32 @@ class Database:
|
|||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL,
|
||||||
last_bumped TEXT,
|
last_bumped TEXT,
|
||||||
|
last_attempt TEXT,
|
||||||
|
fail_streak INTEGER NOT NULL DEFAULT 0,
|
||||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
# Migration for DBs created before last_attempt/fail_streak existed.
|
||||||
|
async with self._connection.execute("PRAGMA table_info(threads)") as cursor:
|
||||||
|
existing_columns = {row[1] for row in await cursor.fetchall()}
|
||||||
|
if "last_attempt" not in existing_columns:
|
||||||
|
await self._connection.execute("ALTER TABLE threads ADD COLUMN last_attempt TEXT")
|
||||||
|
if "fail_streak" not in existing_columns:
|
||||||
|
await self._connection.execute(
|
||||||
|
"ALTER TABLE threads ADD COLUMN fail_streak INTEGER NOT NULL DEFAULT 0"
|
||||||
|
)
|
||||||
|
|
||||||
await self._connection.execute("""
|
await self._connection.execute("""
|
||||||
CREATE INDEX IF NOT EXISTS idx_last_bumped
|
CREATE INDEX IF NOT EXISTS idx_last_bumped
|
||||||
ON threads(last_bumped)
|
ON threads(last_bumped)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
await self._connection.execute("""
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_last_attempt
|
||||||
|
ON threads(last_attempt)
|
||||||
|
""")
|
||||||
|
|
||||||
await self._connection.execute("""
|
await self._connection.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS settings (
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
key TEXT PRIMARY KEY,
|
key TEXT PRIMARY KEY,
|
||||||
@@ -228,7 +245,12 @@ class Database:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
async def get_threads_to_bump(self, interval_minutes: float) -> list[Thread]:
|
async def get_threads_to_bump(self, interval_minutes: float) -> list[Thread]:
|
||||||
"""Threads due for a bump: the interval has passed since the last one."""
|
"""Threads due for a bump: the interval has passed since the last attempt.
|
||||||
|
|
||||||
|
Threads that keep failing back off exponentially (interval * 2**fail_streak,
|
||||||
|
capped at 60x) so a permanently broken thread doesn't get retried on every
|
||||||
|
single tick and burn through the batch rate limit.
|
||||||
|
"""
|
||||||
self._ensure_connected()
|
self._ensure_connected()
|
||||||
if interval_minutes < 0:
|
if interval_minutes < 0:
|
||||||
raise ValueError("interval_minutes must be non-negative")
|
raise ValueError("interval_minutes must be non-negative")
|
||||||
@@ -236,9 +258,20 @@ class Database:
|
|||||||
async with self._connection.execute(
|
async with self._connection.execute(
|
||||||
"""
|
"""
|
||||||
SELECT id, title, last_bumped FROM threads
|
SELECT id, title, last_bumped FROM threads
|
||||||
WHERE last_bumped IS NULL
|
WHERE last_attempt IS NULL
|
||||||
OR datetime(last_bumped, '+' || ? || ' minutes') <= datetime('now')
|
OR datetime(
|
||||||
ORDER BY last_bumped ASC NULLS FIRST
|
last_attempt,
|
||||||
|
'+' || (? * CASE
|
||||||
|
WHEN fail_streak <= 0 THEN 1
|
||||||
|
WHEN fail_streak = 1 THEN 2
|
||||||
|
WHEN fail_streak = 2 THEN 4
|
||||||
|
WHEN fail_streak = 3 THEN 8
|
||||||
|
WHEN fail_streak = 4 THEN 16
|
||||||
|
WHEN fail_streak = 5 THEN 32
|
||||||
|
ELSE 60
|
||||||
|
END) || ' minutes'
|
||||||
|
) <= datetime('now')
|
||||||
|
ORDER BY last_attempt ASC NULLS FIRST
|
||||||
""",
|
""",
|
||||||
(interval_minutes,),
|
(interval_minutes,),
|
||||||
) as cursor:
|
) as cursor:
|
||||||
@@ -248,16 +281,41 @@ class Database:
|
|||||||
logger.error(f"Error fetching threads to bump: {e}")
|
logger.error(f"Error fetching threads to bump: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def update_last_bumped(self, thread_id: str) -> None:
|
async def record_bump_success(self, thread_id: str) -> None:
|
||||||
|
"""Mark a thread as successfully bumped and reset its failure backoff."""
|
||||||
self._ensure_connected()
|
self._ensure_connected()
|
||||||
try:
|
try:
|
||||||
await self._connection.execute(
|
await self._connection.execute(
|
||||||
"UPDATE threads SET last_bumped = datetime('now') WHERE id = ?",
|
"""
|
||||||
|
UPDATE threads SET
|
||||||
|
last_bumped = datetime('now'),
|
||||||
|
last_attempt = datetime('now'),
|
||||||
|
fail_streak = 0
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
(thread_id,),
|
(thread_id,),
|
||||||
)
|
)
|
||||||
await self._connection.commit()
|
await self._connection.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error updating last_bumped for thread {thread_id}: {e}")
|
logger.error(f"Error recording bump success for thread {thread_id}: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
async def record_bump_failure(self, thread_id: str) -> None:
|
||||||
|
"""Record a failed bump attempt and increase its retry backoff."""
|
||||||
|
self._ensure_connected()
|
||||||
|
try:
|
||||||
|
await self._connection.execute(
|
||||||
|
"""
|
||||||
|
UPDATE threads SET
|
||||||
|
last_attempt = datetime('now'),
|
||||||
|
fail_streak = fail_streak + 1
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(thread_id,),
|
||||||
|
)
|
||||||
|
await self._connection.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error recording bump failure for thread {thread_id}: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def delete_thread(self, thread_id: str) -> bool:
|
async def delete_thread(self, thread_id: str) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user