fix click

This commit is contained in:
Yuriy Yuriev
2026-06-05 20:56:26 +07:00
parent e438f09693
commit f262616aad
3 changed files with 124 additions and 32 deletions
+80 -7
View File
@@ -483,9 +483,14 @@ class BotInterface:
if not await require_admin(callback):
return
tid = callback.data.replace("tdelete_", "", 1)
params = await interface.task_manager.get_task(tid)
is_visit = params and params.task_type in ("visit", "user_visit")
await interface.task_manager.remove_task(tid)
await callback.answer("🗑️ Удалена")
await interface._show_streamers_list(callback.message, edit=True)
if is_visit:
await interface._show_tasks_list(callback.message, edit=True)
else:
await interface._show_streamers_list(callback.message, edit=True)
@dp.callback_query(F.data.startswith("treset_"))
@@ -1690,9 +1695,67 @@ class BotInterface:
await self._edit_or_send(message, text, builder.as_markup(), edit)
async def _show_tasks_list(self, message: Message, edit: bool = False):
"""Все задачи."""
text = await self.task_manager.format_task_list()
"""Все задачи — посещения и мониторинг."""
active = await self.task_manager.get_active_tasks()
completed = await self.task_manager.get_completed_tasks()
visit_active = {tid: p for tid, p in active.items() if p.task_type in ("visit", "user_visit")}
visit_done = {tid: p for tid, p in completed.items() if p.task_type in ("visit", "user_visit")}
twitch_active = {tid: p for tid, p in active.items() if p.task_type == "twitch_irc"}
twitch_done = {tid: p for tid, p in completed.items() if p.task_type == "twitch_irc"}
builder = InlineKeyboardBuilder()
if not any([visit_active, visit_done, twitch_active, twitch_done]):
text = "📊 Задачи\n\nЗадач нет."
else:
text = "📊 Задачи\n\n"
if twitch_active:
text += "📺 Twitch — активные:\n"
for tid, p in list(twitch_active.items())[:10]:
em = p.get_status_emoji()
domains = ", ".join(p.allowed_domains) if p.allowed_domains else "все"
text += f"{em} {p.channel}\n 🔗{p.links_found} | 📊{p.total_visits} | 🌐{domains}\n"
builder.row(InlineKeyboardButton(
text=f"{em} 📺 {p.channel} (🔗{p.links_found})",
callback_data=f"tdetail_{tid}"
))
if twitch_done:
text += "\n📺 Twitch — завершённые:\n"
for tid, p in list(twitch_done.items())[:5]:
em = p.get_status_emoji()
text += f"{em} {p.channel}\n"
builder.row(InlineKeyboardButton(
text=f"{em} 📺 {p.channel} (завершена)",
callback_data=f"tdetail_{tid}"
))
if visit_active:
text += "\n🔗 Посещения — активные:\n"
for tid, p in list(visit_active.items())[:10]:
em = p.get_status_emoji()
url_s = (p.url or "")[:35]
uid_tag = f" | uid:{p.user_id}" if p.user_id else ""
text += f"{em} {url_s}{uid_tag}\n{p.successful_visits}/{p.max_visits} | 📊{p.total_visits}\n"
builder.row(InlineKeyboardButton(
text=f"{em} 🔗 {url_s[:28]} ({p.successful_visits}/{p.max_visits})",
callback_data=f"tdetail_{tid}"
))
if visit_done:
text += "\n🔗 Посещения — завершённые:\n"
for tid, p in list(visit_done.items())[:5]:
em = p.get_status_emoji()
url_s = (p.url or "")[:35]
uid_tag = f" | uid:{p.user_id}" if p.user_id else ""
text += f"{em} {url_s}{uid_tag}\n"
builder.row(InlineKeyboardButton(
text=f"{em} 🔗 {url_s[:28]} (завершена)",
callback_data=f"tdetail_{tid}"
))
builder.row(InlineKeyboardButton(text="🔄 Обновить", callback_data="menu_tasks"))
builder.row(InlineKeyboardButton(text="🔙 В меню", callback_data="menu_main"))
await self._edit_or_send(message, text, builder.as_markup(), edit)
@@ -1802,7 +1865,8 @@ class BotInterface:
InlineKeyboardButton(text="🗑️ Удалить", callback_data=f"tdelete_{task_id}")
)
builder.row(InlineKeyboardButton(text="🔙 К списку", callback_data="menu_streamers"))
back_target = "menu_tasks" if params.task_type in ("visit", "user_visit") else "menu_streamers"
builder.row(InlineKeyboardButton(text="🔙 К списку", callback_data=back_target))
try:
await callback.message.edit_text(text, reply_markup=builder.as_markup())
@@ -2230,6 +2294,7 @@ class BotInterface:
break
# --- Серия кликов ---
loop = asyncio.get_event_loop()
for click_num in range(series_size):
if params.stopped or params.successful_visits >= max_v:
break
@@ -2240,6 +2305,11 @@ class BotInterface:
await send_message_safe(bot, params.chat_id, "⚠️ Баланс исчерпан — задача остановлена")
return
# Фиксируем старт тика до посещения
is_last = (click_num == series_size - 1)
click_delay = 0 if is_last else params.get_click_delay()
tick_start = loop.time()
try:
visitor = self.browser_pool or self.browser_service
result = await visitor.visit_page(params.url, params.get_reading_time())
@@ -2267,9 +2337,12 @@ class BotInterface:
logger.info(f"⏸️ Paused after click {click_num+1}/{series_size}, {remaining_clicks} saved")
break
# Задержка между кликами (кроме последнего)
if click_num < series_size - 1 and params.successful_visits < max_v:
await _sleep_interruptible(params.get_click_delay())
# Задержка между кликами строго по таймеру: спим только остаток
if not is_last and params.successful_visits < max_v:
elapsed = loop.time() - tick_start
remaining_wait = int(click_delay - elapsed)
if remaining_wait > 0:
await _sleep_interruptible(remaining_wait)
await self.task_manager.complete_task(task_id)
remaining = await self.balance_storage.get_balance(params.user_id)
+17 -7
View File
@@ -272,8 +272,12 @@ class TaskManager:
return await self.get_tasks_by_type("twitch_irc")
async def get_visit_tasks(self) -> Dict[str, TaskParams]:
"""Только задачи посещения."""
return await self.get_tasks_by_type("visit")
"""Задачи посещения (admin и user)."""
async with self._lock:
return {
tid: p for tid, p in self._tasks.items()
if p.task_type in ("visit", "user_visit")
}
async def get_stats(self) -> dict:
"""Общая статистика."""
@@ -281,7 +285,7 @@ class TaskManager:
for p in self._tasks.values():
if p.task_type == "twitch_irc":
twitch += 1
elif p.task_type == "visit":
elif p.task_type in ("visit", "user_visit"):
visit += 1
if p.paused:
paused += 1
@@ -300,12 +304,18 @@ class TaskManager:
status = params.get_status_emoji()
if params.task_type == "twitch_irc":
user_tag = f" | 👤uid:{params.user_id}" if params.user_id else ""
return (
f"{status} 📺 **{params.channel}**\n"
f" `{task_id[:12]}...`\n"
f" 👤 @{params.target_username} | "
f"🔗{params.links_found} | "
f"📊{params.total_visits}\n"
f" `{task_id[:12]}...`{user_tag}\n"
f" 🔗{params.links_found} | 📊{params.total_visits}\n"
)
elif params.task_type == "user_visit":
user_tag = f" | 👤uid:{params.user_id}" if params.user_id else ""
return (
f"{status} 🔗 `{params.url[:40]}`\n"
f" `{task_id[:12]}...`{user_tag}\n"
f"{params.successful_visits}/{params.max_visits} | 📊{params.total_visits}\n"
)
else:
return (
+13 -4
View File
@@ -56,6 +56,7 @@ class VisitScheduler:
Statistics dict
"""
total_reading_time = 0
loop = asyncio.get_event_loop()
try:
while True:
@@ -63,15 +64,18 @@ class VisitScheduler:
logger.info(f"Visit limit reached: {self.max_visits}")
break
# Randomize parameters
# Randomize parameters once per cycle
current_reading = random.randint(self.min_reading, self.max_reading)
current_delay = random.randint(self.min_delay, self.max_delay)
self.visit_count += 1
# Record start time so the next visit fires strictly on the timer
tick_start = loop.time()
logger.info(
f"Visit {self.visit_count}/{'' if not self.max_visits else self.max_visits}: "
f"reading={current_reading}s"
f"reading={current_reading}s delay={current_delay}s"
)
# Visit page
@@ -94,11 +98,16 @@ class VisitScheduler:
if self.max_visits and self.visit_count >= self.max_visits:
break
# Wait before next visit
# Wait only the remaining time so that the period between visit
# starts is exactly current_delay (independent of visit duration)
elapsed = loop.time() - tick_start
remaining = current_delay - elapsed
if self.on_progress:
await self.on_progress(self.visit_count, current_delay)
await asyncio.sleep(current_delay)
if remaining > 0:
await asyncio.sleep(remaining)
except asyncio.CancelledError:
logger.info("Visit scheduler cancelled")