add pause series
This commit is contained in:
+100
-32
@@ -2114,26 +2114,55 @@ class BotInterface:
|
|||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
await asyncio.sleep(1)
|
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:
|
try:
|
||||||
while params.successful_visits < max_v and not params.stopped:
|
while params.successful_visits < max_v and not params.stopped:
|
||||||
# --- пауза перед циклом ---
|
|
||||||
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:
|
while params.paused and not params.stopped:
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
if params.stopped:
|
if params.stopped:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Определяем размер серии
|
||||||
|
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
|
||||||
|
|
||||||
balance = await self.balance_storage.get_balance(params.user_id)
|
balance = await self.balance_storage.get_balance(params.user_id)
|
||||||
if balance <= 0:
|
if balance <= 0:
|
||||||
await self.task_manager.stop_task(task_id)
|
await self.task_manager.stop_task(task_id)
|
||||||
@@ -2156,8 +2185,16 @@ class BotInterface:
|
|||||||
logger.error(f"URL visit error: {e}")
|
logger.error(f"URL visit error: {e}")
|
||||||
params.total_visits += 1
|
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 _sleep_interruptible(params.get_click_delay())
|
||||||
|
|
||||||
await self.task_manager.complete_task(task_id)
|
await self.task_manager.complete_task(task_id)
|
||||||
@@ -2197,48 +2234,79 @@ class BotInterface:
|
|||||||
viewers = await get_viewer_count(params.channel)
|
viewers = await get_viewer_count(params.channel)
|
||||||
if viewers and viewers > 0:
|
if viewers and viewers > 0:
|
||||||
visits_count = max(1, round(viewers * ctr / 100))
|
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:
|
else:
|
||||||
visits_count = max(1, round(params.visits_per_link * ctr / 100))
|
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(
|
await send_message_safe(
|
||||||
message.bot, message.chat.id,
|
message.bot, message.chat.id,
|
||||||
f"🔗 @{username}: {url[:60]}"
|
f"🔗 @{username}: {url[:60]}\n"
|
||||||
|
f"📊 {calc_info}"
|
||||||
)
|
)
|
||||||
|
|
||||||
for i in range(visits_count):
|
# Роут: [задержка] → клик1 → [задержка] → клик2 → ... → кликN
|
||||||
while params.paused:
|
# Пауза: текущий клик завершается, серия останавливается
|
||||||
await asyncio.sleep(1)
|
# 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:
|
if params.stopped:
|
||||||
break
|
return
|
||||||
|
|
||||||
reading = params.get_reading_time()
|
reading = params.get_reading_time()
|
||||||
try:
|
try:
|
||||||
result = await self.browser_service.visit_page_from_twitch(
|
result = await self.browser_service.visit_page(url, reading)
|
||||||
url=url,
|
|
||||||
channel=params.channel,
|
|
||||||
reading_time=reading
|
|
||||||
)
|
|
||||||
params.total_visits += 1
|
params.total_visits += 1
|
||||||
if result.success:
|
if result.success:
|
||||||
params.successful_visits += 1
|
params.successful_visits += 1
|
||||||
|
remaining -= 1
|
||||||
if params.user_id:
|
if params.user_id:
|
||||||
new_balance = await self.balance_storage.deduct(params.user_id, 1)
|
new_balance = await self.balance_storage.deduct(params.user_id, 1)
|
||||||
if new_balance <= 0:
|
if new_balance <= 0:
|
||||||
params.stopped = True
|
params.stopped = True
|
||||||
await send_message_safe(
|
await send_message_safe(
|
||||||
message.bot, params.chat_id,
|
message.bot, params.chat_id,
|
||||||
"⚠️ Баланс исчерпан — задача остановлена.\n"
|
"⚠️ Баланс исчерпан — задача остановлена."
|
||||||
"Обратитесь к администратору для пополнения."
|
|
||||||
)
|
)
|
||||||
break
|
return
|
||||||
|
else:
|
||||||
|
remaining -= 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Visit error: {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:
|
except Exception as e:
|
||||||
logger.error(f"Process URL error: {e}")
|
logger.error(f"Process URL error: {e}")
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class Socks5ToHttpProxy:
|
|||||||
return
|
return
|
||||||
|
|
||||||
request_text = request_line.decode('utf-8', errors='ignore').strip()
|
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'):
|
if request_text.upper().startswith('CONNECT'):
|
||||||
await self._handle_connect(request_text, reader, writer)
|
await self._handle_connect(request_text, reader, writer)
|
||||||
@@ -183,7 +183,7 @@ class Socks5ToHttpProxy:
|
|||||||
from python_socks import ProxyType
|
from python_socks import ProxyType
|
||||||
|
|
||||||
if self.username:
|
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(
|
proxy = Proxy.create(
|
||||||
ProxyType.SOCKS5,
|
ProxyType.SOCKS5,
|
||||||
self.socks5_host, self.socks5_port,
|
self.socks5_host, self.socks5_port,
|
||||||
@@ -209,7 +209,7 @@ class Socks5ToHttpProxy:
|
|||||||
await writer.drain()
|
await writer.drain()
|
||||||
return
|
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')
|
writer.write(b'HTTP/1.1 200 Connection Established\r\n\r\n')
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user