add pause series

This commit is contained in:
Yuriy Yuriev
2026-05-20 15:43:42 +07:00
parent df184a4f20
commit f0f500dfdd
2 changed files with 101 additions and 33 deletions
+98 -30
View File
@@ -2114,26 +2114,55 @@ class BotInterface:
await asyncio.sleep(2)
await asyncio.sleep(1)
# Роут: [задержка] → клик1 → [задержка] → клик2 → ... → кликN → повтор
# Пауза: текущий клик завершается, серия останавливается на N
# Resume < 5 мин: продолжает с N+1 без задержки
# Resume > 5 мин: серия этой ссылки отбрасывается, ждём следующий цикл
remaining_clicks = 0
series_paused_at: Optional[float] = None
SERIES_EXPIRY = 5 * 60
try:
while params.successful_visits < max_v and not params.stopped:
# --- пауза перед циклом ---
await _sleep_interruptible(params.get_delay())
# Ждём паузу если активна перед новым циклом
while params.paused and not params.stopped:
await asyncio.sleep(2)
if params.stopped:
break
# размер цикла выбирается случайно каждый раз
series_size = params.get_series_size()
# Определяем размер серии
if remaining_clicks > 0:
elapsed = datetime.now().timestamp() - (series_paused_at or 0)
if elapsed > SERIES_EXPIRY:
# Серия устарела — отбрасываем, ждём следующую задержку
logger.info(f"⏰ Series expired ({elapsed:.0f}s > {SERIES_EXPIRY//60}min), skipping remaining {remaining_clicks} clicks")
remaining_clicks = 0
series_paused_at = None
# Стандартная задержка перед следующей серией
await _sleep_interruptible(params.get_delay())
if params.stopped:
break
series_size = params.get_series_size()
else:
# Продолжаем с сохранённого места — без задержки
series_size = remaining_clicks
remaining_clicks = 0
series_paused_at = None
logger.info(f"▶️ Resuming: {series_size} clicks left ({elapsed:.0f}s paused)")
else:
# Новая серия: задержка → рандомный размер
await _sleep_interruptible(params.get_delay())
if params.stopped:
break
series_size = params.get_series_size()
# --- цикл кликов ---
# --- Серия кликов ---
for click_num in range(series_size):
if params.stopped or params.successful_visits >= max_v:
break
while params.paused and not params.stopped:
await asyncio.sleep(2)
if params.stopped:
break
balance = await self.balance_storage.get_balance(params.user_id)
if balance <= 0:
await self.task_manager.stop_task(task_id)
@@ -2156,8 +2185,16 @@ class BotInterface:
logger.error(f"URL visit error: {e}")
params.total_visits += 1
# задержка между кликами внутри цикла (кроме последнего)
if click_num < series_size - 1 and not params.stopped and params.successful_visits < max_v:
# Клик завершён — проверяем паузу
if params.paused or params.stopped:
remaining_clicks = series_size - click_num - 1
if remaining_clicks > 0 and not params.stopped:
series_paused_at = datetime.now().timestamp()
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())
await self.task_manager.complete_task(task_id)
@@ -2197,48 +2234,79 @@ class BotInterface:
viewers = await get_viewer_count(params.channel)
if viewers and viewers > 0:
visits_count = max(1, round(viewers * ctr / 100))
logger.info(f"visits: {viewers} viewers × {ctr:.2f}% = {visits_count}")
calc_info = f"{viewers} зрителей × {ctr:.2f}% = {visits_count} кликов"
else:
visits_count = max(1, round(params.visits_per_link * ctr / 100))
logger.info(f"visits: fallback {params.visits_per_link} × {ctr:.2f}% = {visits_count}")
calc_info = f"зрители недоступны, фикс {params.visits_per_link} × {ctr:.2f}% = {visits_count} кликов"
logger.info(f"🔗 @{username}: {url}")
logger.info(f"🔗 @{username} {url}{visits_count} clicks ({calc_info})")
await send_message_safe(
message.bot, message.chat.id,
f"🔗 @{username}: {url[:60]}"
f"🔗 @{username}: {url[:60]}\n"
f"📊 {calc_info}"
)
for i in range(visits_count):
while params.paused:
await asyncio.sleep(1)
# Роут: [задержка] → клик1 → [задержка] → клик2 → ... → кликN
# Пауза: текущий клик завершается, серия останавливается
# Resume < 5 мин: продолжает с сохранённого клика
# Resume > 5 мин: серия этой ссылки полностью отбрасывается
SERIES_EXPIRY = 5 * 60
remaining = visits_count
paused_at: Optional[float] = None
first_click = True
while remaining > 0 and not params.stopped:
# Проверяем не истекла ли серия после паузы
if paused_at is not None:
elapsed = datetime.now().timestamp() - paused_at
if elapsed > SERIES_EXPIRY:
logger.info(f"⏰ Серия устарела ({elapsed:.0f}s), ссылка {url[:40]} отброшена")
return
paused_at = None
logger.info(f"▶️ Продолжаем серию: {remaining} кликов осталось")
# Задержка перед каждым кликом (включая первый)
if not params.stopped and not params.paused:
delay = params.get_delay()
if first_click:
logger.info(f"⏳ Задержка перед первым кликом: {delay}с")
await asyncio.sleep(delay)
first_click = False
if params.stopped:
break
return
reading = params.get_reading_time()
try:
result = await self.browser_service.visit_page_from_twitch(
url=url,
channel=params.channel,
reading_time=reading
)
result = await self.browser_service.visit_page(url, reading)
params.total_visits += 1
if result.success:
params.successful_visits += 1
remaining -= 1
if params.user_id:
new_balance = await self.balance_storage.deduct(params.user_id, 1)
if new_balance <= 0:
params.stopped = True
await send_message_safe(
message.bot, params.chat_id,
"⚠️ Баланс исчерпан — задача остановлена.\n"
"Обратитесь к администратору для пополнения."
"⚠️ Баланс исчерпан — задача остановлена."
)
break
return
else:
remaining -= 1
except Exception as e:
logger.error(f"Visit error: {e}")
remaining -= 1
# Клик завершён — проверяем паузу
if params.paused and remaining > 0:
paused_at = datetime.now().timestamp()
logger.info(f"⏸️ Серия на паузе, {remaining} кликов сохранено (истечёт через {SERIES_EXPIRY//60}мин)")
while params.paused and not params.stopped:
await asyncio.sleep(2)
if params.stopped:
return
if i < visits_count - 1:
await asyncio.sleep(params.get_delay())
except Exception as e:
logger.error(f"Process URL error: {e}")
+3 -3
View File
@@ -127,7 +127,7 @@ class Socks5ToHttpProxy:
return
request_text = request_line.decode('utf-8', errors='ignore').strip()
logger.info(f"Tunnel request: {request_text[:80]}")
logger.debug(f"Tunnel request: {request_text[:80]}")
if request_text.upper().startswith('CONNECT'):
await self._handle_connect(request_text, reader, writer)
@@ -183,7 +183,7 @@ class Socks5ToHttpProxy:
from python_socks import ProxyType
if self.username:
logger.info(f"SOCKS5 connect {host}:{port} via {self.socks5_host}:{self.socks5_port} auth=yes user={self.username[:4]}***")
logger.debug(f"SOCKS5 connect {host}:{port} via {self.socks5_host}:{self.socks5_port} auth=yes")
proxy = Proxy.create(
ProxyType.SOCKS5,
self.socks5_host, self.socks5_port,
@@ -209,7 +209,7 @@ class Socks5ToHttpProxy:
await writer.drain()
return
logger.info(f"SOCKS5 connected {host}:{port} OK")
logger.debug(f"SOCKS5 connected {host}:{port} OK")
writer.write(b'HTTP/1.1 200 Connection Established\r\n\r\n')
await writer.drain()