fix click
This commit is contained in:
+79
-6
@@ -483,8 +483,13 @@ class BotInterface:
|
|||||||
if not await require_admin(callback):
|
if not await require_admin(callback):
|
||||||
return
|
return
|
||||||
tid = callback.data.replace("tdelete_", "", 1)
|
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 interface.task_manager.remove_task(tid)
|
||||||
await callback.answer("🗑️ Удалена")
|
await callback.answer("🗑️ Удалена")
|
||||||
|
if is_visit:
|
||||||
|
await interface._show_tasks_list(callback.message, edit=True)
|
||||||
|
else:
|
||||||
await interface._show_streamers_list(callback.message, edit=True)
|
await interface._show_streamers_list(callback.message, edit=True)
|
||||||
|
|
||||||
|
|
||||||
@@ -1690,9 +1695,67 @@ class BotInterface:
|
|||||||
await self._edit_or_send(message, text, builder.as_markup(), edit)
|
await self._edit_or_send(message, text, builder.as_markup(), edit)
|
||||||
|
|
||||||
async def _show_tasks_list(self, message: Message, edit: bool = False):
|
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()
|
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_tasks"))
|
||||||
builder.row(InlineKeyboardButton(text="🔙 В меню", callback_data="menu_main"))
|
builder.row(InlineKeyboardButton(text="🔙 В меню", callback_data="menu_main"))
|
||||||
await self._edit_or_send(message, text, builder.as_markup(), edit)
|
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}")
|
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:
|
try:
|
||||||
await callback.message.edit_text(text, reply_markup=builder.as_markup())
|
await callback.message.edit_text(text, reply_markup=builder.as_markup())
|
||||||
@@ -2230,6 +2294,7 @@ class BotInterface:
|
|||||||
break
|
break
|
||||||
|
|
||||||
# --- Серия кликов ---
|
# --- Серия кликов ---
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
for click_num in range(series_size):
|
for click_num in range(series_size):
|
||||||
if params.stopped or params.successful_visits >= max_v:
|
if params.stopped or params.successful_visits >= max_v:
|
||||||
break
|
break
|
||||||
@@ -2240,6 +2305,11 @@ class BotInterface:
|
|||||||
await send_message_safe(bot, params.chat_id, "⚠️ Баланс исчерпан — задача остановлена")
|
await send_message_safe(bot, params.chat_id, "⚠️ Баланс исчерпан — задача остановлена")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Фиксируем старт тика до посещения
|
||||||
|
is_last = (click_num == series_size - 1)
|
||||||
|
click_delay = 0 if is_last else params.get_click_delay()
|
||||||
|
tick_start = loop.time()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
visitor = self.browser_pool or self.browser_service
|
visitor = self.browser_pool or self.browser_service
|
||||||
result = await visitor.visit_page(params.url, params.get_reading_time())
|
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")
|
logger.info(f"⏸️ Paused after click {click_num+1}/{series_size}, {remaining_clicks} saved")
|
||||||
break
|
break
|
||||||
|
|
||||||
# Задержка между кликами (кроме последнего)
|
# Задержка между кликами строго по таймеру: спим только остаток
|
||||||
if click_num < series_size - 1 and params.successful_visits < max_v:
|
if not is_last and params.successful_visits < max_v:
|
||||||
await _sleep_interruptible(params.get_click_delay())
|
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)
|
await self.task_manager.complete_task(task_id)
|
||||||
remaining = await self.balance_storage.get_balance(params.user_id)
|
remaining = await self.balance_storage.get_balance(params.user_id)
|
||||||
|
|||||||
@@ -272,8 +272,12 @@ class TaskManager:
|
|||||||
return await self.get_tasks_by_type("twitch_irc")
|
return await self.get_tasks_by_type("twitch_irc")
|
||||||
|
|
||||||
async def get_visit_tasks(self) -> Dict[str, TaskParams]:
|
async def get_visit_tasks(self) -> Dict[str, TaskParams]:
|
||||||
"""Только задачи посещения."""
|
"""Задачи посещения (admin и user)."""
|
||||||
return await self.get_tasks_by_type("visit")
|
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:
|
async def get_stats(self) -> dict:
|
||||||
"""Общая статистика."""
|
"""Общая статистика."""
|
||||||
@@ -281,7 +285,7 @@ class TaskManager:
|
|||||||
for p in self._tasks.values():
|
for p in self._tasks.values():
|
||||||
if p.task_type == "twitch_irc":
|
if p.task_type == "twitch_irc":
|
||||||
twitch += 1
|
twitch += 1
|
||||||
elif p.task_type == "visit":
|
elif p.task_type in ("visit", "user_visit"):
|
||||||
visit += 1
|
visit += 1
|
||||||
if p.paused:
|
if p.paused:
|
||||||
paused += 1
|
paused += 1
|
||||||
@@ -300,12 +304,18 @@ class TaskManager:
|
|||||||
status = params.get_status_emoji()
|
status = params.get_status_emoji()
|
||||||
|
|
||||||
if params.task_type == "twitch_irc":
|
if params.task_type == "twitch_irc":
|
||||||
|
user_tag = f" | 👤uid:{params.user_id}" if params.user_id else ""
|
||||||
return (
|
return (
|
||||||
f"{status} 📺 **{params.channel}**\n"
|
f"{status} 📺 **{params.channel}**\n"
|
||||||
f" `{task_id[:12]}...`\n"
|
f" `{task_id[:12]}...`{user_tag}\n"
|
||||||
f" 👤 @{params.target_username} | "
|
f" 🔗{params.links_found} | 📊{params.total_visits}\n"
|
||||||
f"🔗{params.links_found} | "
|
)
|
||||||
f"📊{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:
|
else:
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class VisitScheduler:
|
|||||||
Statistics dict
|
Statistics dict
|
||||||
"""
|
"""
|
||||||
total_reading_time = 0
|
total_reading_time = 0
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
@@ -63,15 +64,18 @@ class VisitScheduler:
|
|||||||
logger.info(f"Visit limit reached: {self.max_visits}")
|
logger.info(f"Visit limit reached: {self.max_visits}")
|
||||||
break
|
break
|
||||||
|
|
||||||
# Randomize parameters
|
# Randomize parameters once per cycle
|
||||||
current_reading = random.randint(self.min_reading, self.max_reading)
|
current_reading = random.randint(self.min_reading, self.max_reading)
|
||||||
current_delay = random.randint(self.min_delay, self.max_delay)
|
current_delay = random.randint(self.min_delay, self.max_delay)
|
||||||
|
|
||||||
self.visit_count += 1
|
self.visit_count += 1
|
||||||
|
|
||||||
|
# Record start time so the next visit fires strictly on the timer
|
||||||
|
tick_start = loop.time()
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Visit {self.visit_count}/{'∞' if not self.max_visits else self.max_visits}: "
|
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
|
# Visit page
|
||||||
@@ -94,11 +98,16 @@ class VisitScheduler:
|
|||||||
if self.max_visits and self.visit_count >= self.max_visits:
|
if self.max_visits and self.visit_count >= self.max_visits:
|
||||||
break
|
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:
|
if self.on_progress:
|
||||||
await self.on_progress(self.visit_count, current_delay)
|
await self.on_progress(self.visit_count, current_delay)
|
||||||
|
|
||||||
await asyncio.sleep(current_delay)
|
if remaining > 0:
|
||||||
|
await asyncio.sleep(remaining)
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.info("Visit scheduler cancelled")
|
logger.info("Visit scheduler cancelled")
|
||||||
|
|||||||
Reference in New Issue
Block a user