Buscar

Guias

LDPlayer macro loop: condições de parada e agendamento

Coloque uma macro em loop no LDPlayer ou BlueStacks com condições de parada, guarde o progresso entre execuções, saia com um código de status e agende no MAS.

  • Windows
  • Mac
  • Emulador
  • Studio
  • SDK Python
Iniciante Atualizado em 7 min de leitura
Nesta página
  1. Antes de começar
  2. Padrões de macro loop para LDPlayer e BlueStacks
  3. Condições de parada do macro loop
  4. Pausas e tempo humanizado
  5. Guarde o progresso com o armazenamento
  6. Saia com um código para os webhooks informarem o status
  7. Um exemplo completo
  8. Agende a macro no Agendador do MAS no BlueStacks ou LDPlayer
  9. Rode em um grupo
  10. O que pode dar errado
  11. O loop nunca termina
  12. O agendamento não disparou
  13. Time slot is occupied on this port
  14. A execução mostra Falhou mas a macro terminou

Uma macro que roda sem supervisão precisa de três coisas: um loop que sabe quando parar, um jeito de lembrar onde chegou e um agendamento que a inicia sem você. Este guia cobre as três para uma macro LDPlayer ou BlueStacks no Macro Automation Studio (MAS). Você recebe os padrões de loop, as condições de parada, o armazenamento entre execuções, os códigos de saída que os webhooks podem informar e o Agendador. Ele é para quem está levando uma macro de “eu clico em Executar” para “ela roda toda manhã”.

Antes de começar

Padrões de macro loop para LDPlayer e BlueStacks

Três formas cobrem quase toda macro.

While, com uma condição de parada. O loop roda até algo na tela ou nos seus contadores mandar parar.

python
import mas

images = mas.images({"collect": 401, "done": 402})

collected = 0
while collected < 50:
    if mas.find_object(images.done):
        break
    button = mas.find_object_retry(images.collect, total_tries=3, time_sleep=2.0)
    if button is None:
        continue
    mas.click(button.x, button.y, delay_ms=1000)
    collected += 1

For, com uma contagem fixa. Você sabe quantas vezes fazer a coisa.

python
for round_no in range(1, 11):
    mas.log(f"Round {round_no} of 10")
    button = mas.find_object_retry(images.collect)
    if button is None:
        mas.log("Nothing to collect, stopping early", level="warning")
        break
    mas.click(button.x, button.y)

Até um template aparecer. Uma espera com prazo, usada para telas de carregamento e timers longos.

python
import time

def wait_for(image, timeout_s=120, every_s=3.0):
    deadline = time.monotonic() + timeout_s
    while time.monotonic() < deadline:
        match = mas.find_object(image)
        if match:
            return match
        time.sleep(every_s)
    return None

Todo loop acima tem uma saída. find_object retorna None quando falha e nunca lança exceção, então um template ausente sozinho não consegue encerrar um while True. Dê a todo loop um limite.

Condições de parada do macro loop

Combine pelo menos duas destas. Uma protege contra o jogo, a outra contra o seu próprio bug.

  • Um contador. while collected < 50. Barato e previsível.
  • Um limite de tempo. time.monotonic() conta segundos e nunca pula quando o relógio muda. Use-o no loop externo para uma execução agendada sempre terminar antes de a próxima começar.
  • Um estado de tela. Um template como “sem energia”, “inventário cheio” ou uma mensagem de limite diário. Capture-o no Asset Lab como qualquer outro template e confira-o no topo de cada iteração.
  • Falhas consecutivas. Conte quantas iterações seguidas não encontraram nada e desista depois de cinco. Uma tela que você não previu se parece exatamente com isso.
python
started = time.monotonic()
misses = 0
while time.monotonic() - started < 20 * 60:
    if mas.find_object(images.out_of_energy):
        break
    button = mas.find_object_retry(images.collect)
    if button is None:
        misses += 1
        if misses >= 5:
            break
        continue
    misses = 0
    mas.click(button.x, button.y)

Pausas e tempo humanizado

click já espera delay_ms=1000 depois do toque e swipe leva duration_ms=1000 por padrão. Entre as iterações, adicione uma pausa que varia.

python
import random

def pause(low=0.8, high=2.5):
    time.sleep(random.uniform(low, high))

Intervalos fixos parecem mecânicos e também disputam com o jogo. Um pop-up que aparece 900 ms depois de um toque escapa de um atraso fixo de 1000 ms em um dia lento. Uma pausa aleatória suaviza os dois. Mantenha delay_ms no click para o tempo de reação do app e use pause() para o ritmo humano.

Guarde o progresso com o armazenamento

Uma execução agendada que para no meio deve retomar de onde parou na próxima vez. As funções de armazenamento guardam um pequeno documento JSON por tarefa, indexado por este computador, pela porta do dispositivo e por um nome de tarefa que você escolhe.

python
TASK = "daily_collect"

state = mas.retrieve(TASK)          # {} on the first run
collected = state.get("collected", 0)
last_day = state.get("day")

mas.save(TASK, {"collected": collected, "day": today})   # after each pass of the loop

if collected >= 50:
    mas.clear(TASK)                 # start fresh next time

save(task_name, data) grava ou substitui a entrada; retrieve(task_name) retorna o dicionário ou um vazio; clear(task_name) o apaga. data precisa ser serializável em JSON. Como a porta faz parte da chave, duas instâncias de emulador rodando a mesma macro nunca compartilham um contador. retrieve_all(task_name) retorna a entrada de toda instância quando você quer um total. Veja Armazenamento.

Saia com um código para os webhooks informarem o status

O MAS lê o código de saída do processo quando o seu script termina. Zero significa que a execução foi concluída; qualquer outro valor a marca como falha. Os inscritos em webhooks recebem macro.completed ou macro.failed conforme o caso, com o exit_code no payload.

python
import sys

if energy is None:
    mas.log("Could not read energy", level="error")
    sys.exit(2)      # macro.failed, exit_code 2

mas.log("All done")
sys.exit(0)          # macro.completed

Uma exceção não tratada também sai com valor diferente de zero, então uma falha é informada como falha sem nenhum código extra. Para eventos que só o seu script conhece, mas.webhook("level.reached", {"level": 40}) envia um evento custom.level.reached aos endpoints inscritos em eventos personalizados. Configure os endpoints na página Webhooks; veja Webhooks.

Um exemplo completo

A macro coleta até cinquenta recompensas por dia, lembra a contagem, para em “sem energia” ou depois de vinte minutos e informa pelo código de saída. Troque os IDs pelos seus.

python
import datetime
import random
import sys
import time

import mas

images = mas.images({
    "collect": 401,
    "close_popup": 402,
    "out_of_energy": 403,
})

TASK = "daily_collect"
DAILY_LIMIT = 50
TIME_BUDGET_S = 20 * 60


def pause(low=0.8, high=2.5):
    time.sleep(random.uniform(low, high))


def main():
    today = datetime.date.today().isoformat()
    state = mas.retrieve(TASK)
    collected = state.get("collected", 0) if state.get("day") == today else 0
    mas.log(f"{today}: starting at {collected} of {DAILY_LIMIT}")

    started = time.monotonic()
    misses = 0
    while collected < DAILY_LIMIT:
        if time.monotonic() - started > TIME_BUDGET_S:
            mas.log("Time budget reached", level="warning")
            break
        popup = mas.find_object(images.close_popup)
        if popup:
            mas.click(popup.x, popup.y, delay_ms=800)
            continue
        if mas.find_object(images.out_of_energy):
            mas.log("Out of energy")
            break
        button = mas.find_object_retry(images.collect, total_tries=3, time_sleep=2.0)
        if button is None:
            misses += 1
            if misses >= 5:
                mas.log("Five misses in a row, giving up", level="error")
                mas.save(TASK, {"collected": collected, "day": today})
                sys.exit(2)
            continue
        misses = 0
        mas.click(button.x, button.y, delay_ms=1000)
        collected += 1
        mas.save(TASK, {"collected": collected, "day": today})
        pause()

    mas.log(f"Finished at {collected} of {DAILY_LIMIT}")
    sys.exit(0)


if __name__ == "__main__":
    main()

Rode uma vez pelo Code Editor com Executar (F5) e confirme que ela termina sozinha antes de agendá-la.

Agende a macro no Agendador do MAS no BlueStacks ou LDPlayer

O Agendador vive dentro do app de desktop e inicia macros nos seus emuladores nos horários que você define.

  1. Abra o Agendador e clique em Criar novo agendamento.
  2. Digite um Nome (3 a 100 caracteres) e escolha a Macro.
  3. Em Porta do emulador, clique em Escanear e escolha a porta da instância, ou digite-a.
  4. Defina Data e Hora. A hora é a hora local do seu computador.
  5. Defina a Recorrência: Nenhuma para uma execução única, Diária, Semanal com os Dias da semana marcados, ou Mensal.
  6. Deixe Contagem de repetições em 1, a menos que queira que a macro rode várias vezes seguidas a cada disparo.
  7. Mantenha Ativo ligado e clique em Criar agendamento.

O que saber antes de confiar nele:

  • O app precisa ficar aberto e com login feito. O agendador não é um serviço do sistema e não há sintaxe de cron.
  • Os jobs são verificados a cada 30 segundos, então uma execução pode começar até meio minuto depois do horário.
  • Um agendamento tem uma porta como alvo. Se a porta estiver ocupada com outra execução na hora, o agendador pula esse ciclo e tenta de novo no próximo.
  • Dois agendamentos não podem compartilhar um horário na mesma porta; o app recusa com “Time slot is occupied on this port”.
  • Até 20 jobs agendados podem rodar ao mesmo tempo.
  • Com Contagem de repetições acima de 1, uma execução com falha (saída diferente de zero) interrompe as repetições restantes e marca o job como Falhou.
  • As execuções agendadas usam os argumentos salvos no card do dispositivo, ou o perfil de configurações que o dispositivo segue. Veja Perfis de configurações.
  • Ver histórico em um job mostra toda execução com status e logs.

A página Agendador cobre editar, pular e cancelar jobs.

Rode em um grupo

Para rodar a mesma macro em várias instâncias à mão, coloque-as em um grupo de dispositivos, atribua a macro a cada dispositivo e clique em Iniciar todos. Os dispositivos iniciam um após o outro, com meio segundo de intervalo, e cada um tem a própria execução, os próprios logs e o próprio código de saída. Para agendar o grupo, crie um job agendado por porta com a mesma macro e o mesmo horário. O guia de multi-instância cobre argumentos, proxies e armazenamento por dispositivo.

O que pode dar errado

O loop nunca termina

Um while True sem limite, ou uma condição de parada que depende de um template que nunca aparece. Adicione um limite de tempo com time.monotonic() e um contador de falhas consecutivas; esses dois encerram qualquer loop.

O agendamento não disparou

O app estava fechado ou suspenso na hora, a porta estava ocupada, ou o job não está Ativo. Confira Ver histórico para uma entrada pulada ou com falha, e mantenha o computador acordado na janela agendada.

Time slot is occupied on this port

Outro job na mesma porta já é dono desse horário. Mova um job alguns minutos, ou coloque a segunda macro dentro da primeira com Contagem de repetições.

A execução mostra Falhou mas a macro terminou

O script terminou com um código de saída diferente de zero ou uma exceção não tratada depois da última ação. Leia as últimas linhas do log; um sys.exit(1) em um caminho de limpeza ou um KeyError no estado guardado são as causas habituais.

Nenhuma ferramenta de automação é 100% livre de riscos, por isso automatize com responsabilidade e a seu próprio critério.

Próximos passos

Páginas relacionadas

Esta página foi útil?

Dúvidas? Pergunte no Discord