본문으로 건너뛰기

사용자 스크립트

기본 제공 스크립트는 일반적인 흐름을 대부분 다룹니다. 그것으로 부족할 때 — 순서를 바꾸고 싶을 때, 기본 스크립트가 건드리지 않는 화면을 다뤄야 할 때, TikTok이나 Instagram이 아닌 앱을 자동화해야 할 때 — 원하는 언어로 직접 작성하면 TikMatrix가 기기를 넘겨줍니다.

요구 사항

라이선스 요건

사용자 스크립트는 Pro, Team, Business 플랜에서만 사용할 수 있습니다. Starter 플랜에서는 사용할 수 없습니다.

플랜의 기기 수가 곧 동시 실행 한도입니다. Pro 플랜(20대)은 기본 작업이든 사용자 스크립트든, 혹은 둘을 섞어서든 동시에 20대를 제어할 수 있습니다.

실행 방식 두 가지

독립 실행

프로그램은 여러분이 직접 실행합니다. TikMatrix는 기기만 빌려줍니다.

from tikmatrix import TikMatrix

client = TikMatrix()

for device in client.devices():
if device["busy"]:
continue
with client.device(device["serial"], label="my crawler") as d:
d.press("home")
print(d.info())

일회성 작업, 데이터 수집, 자체 스케줄러로 돌리고 싶은 작업에 적합합니다.

관리형 실행

프로그램을 TikMatrix에 등록하면 일반 작업과 똑같아집니다. 작업 큐, 플랜별 동시 실행 제한, 자동 재시도, 작업 로그, 스케줄 템플릿을 그대로 사용할 수 있습니다. TikMatrix는 프로그램을 시작하기 전에 기기를 임대하고 임대 ID를 환경 변수로 전달합니다.

from tikmatrix import TikMatrix

with TikMatrix.from_env() as d: # 기기는 이미 임대된 상태
d.click(text="Log in")
print("done") # 이 줄은 작업 로그에 남습니다

반복 실행, 예약 실행, 여러 기기에 걸친 실행에 적합합니다.

어느 쪽을 고를까

독립 실행관리형 실행
실행 주체여러분TikMatrix 작업 큐
기기 임대직접 획득시작 시점에 이미 보유
재시도·예약·작업 로그직접 구현기본 제공
여러 기기에서 실행직접 반복문 작성기기당 작업 하나씩 병렬 배포
적합한 용도탐색, 크롤러, 일회성 작업반복하고 싶은 모든 것

먼저 독립 실행으로 흐름을 다듬은 뒤 같은 파일을 관리형 스크립트로 등록해도 됩니다. 바뀌는 건 TikMatrix.from_env() 한 줄뿐입니다.

시작하기

1. 클라이언트 라이브러리 설치

pip install requests

그런 다음 SDK 디렉터리tikmatrix.py를 스크립트 옆에 복사합니다. 이 라이브러리는 단일 파일이며 다른 의존성이 없습니다.

꼭 쓸 필요는 없습니다. API는 HTTP 위의 평범한 JSON이며, 원시 엔드포인트는 아래에 정리해 두었습니다.

2. 스크립트 작성

from tikmatrix import TikMatrix

client = TikMatrix()
with client.device("192.168.1.5:5555") as d:
d.press("home")
d.adb("shell", "am", "start", "-a", "android.settings.SETTINGS")
d.wait_for(text="Settings", timeout=15)
d.screenshot("settings.png")

TikMatrix를 켜 두고 기기를 연결한 상태에서 실행하세요. 기기 정보 딕셔너리가 출력되면 연결은 정상입니다.

3. 등록하기 (관리형 전용)

기기 → 사용자 스크립트 → 스크립트 추가 로 이동합니다.

항목의미
이름스크립트 목록과 작업 로그에 표시됩니다
명령실행할 프로그램 줄. 예: python C:/scripts/my_flow.py
작업 디렉터리선택 사항. 프로그램이 시작될 위치
플랫폼아래 플랫폼 모드 참고
타임아웃몇 초 후 스크립트를 종료하고 작업을 실패 처리할지. 기본값 1800
추가 환경 변수선택 사항. 프로그램 환경에 병합되는 JSON 객체
활성화삭제하지 않고 끌 수 있습니다. 비활성 스크립트는 배포되지 않습니다

이후 스크립트 행의 ▶ 를 누르고 기기를 고르면 기본 스크립트와 완전히 동일하게 실행됩니다.

AI 어시스턴트에게 맡기기

AI 어시스턴트는 평범한 말로 된 설명으로 사용자 스크립트를 작성하고 한 번에 등록까지 해줍니다. 디스크에 무언가 기록되기 전에 파일 전체를 보여줍니다.

기기 임대

한 기기는 한 번에 하나만 제어할 수 있습니다. 임대는 TikMatrix에게 "이 기기는 사용 중"이라고 알리는 것이며, 그 결과:

  • 작업 큐가 같은 화면에 작업을 배포하지 않고,
  • 여러분의 JSON-RPC 호출이 기본 스크립트와 똑같이 에이전트 상태를 보고하므로, 워치독은 침묵하는 에이전트가 아니라 바쁜 에이전트를 보게 됩니다.

임대는 플랜의 기기 슬롯도 하나 차지합니다.

임대는 만료됩니다 — 기본 120초, 최대 600초. Python 라이브러리는 백그라운드 스레드에서 자동 갱신하고 with 블록이 끝날 때 해제하므로, 스크립트가 죽어도 앱을 재시작할 때까지 붙잡고 있는 대신 몇 초 안에 기기가 풀립니다. API를 직접 호출한다면 하트비트를 직접 보내야 합니다.

살아 있는 모든 임대는 설정 → Developer API → 활성 기기 세션 에서 확인하고 강제 해제할 수 있습니다.

플랫폼 모드

등록된 스크립트는 대상 플랫폼을 선언합니다.

Generic — 기기가 그대로 넘어옵니다. 앱을 실행하지 않고, 계정을 전환하지 않으며, 입력기를 확인하지도 않고, 끝난 뒤에 무엇도 닫지 않습니다. TikTok / Instagram 이외의 모든 것을 자동화할 때 사용합니다.

TikTok / Instagram — 프로그램이 시작되기 전에 앱을 열고 계정을 전환하며, 끝나면 앱을 닫습니다. 기본 스크립트와 동일합니다. 해석된 패키지는 TIKMATRIX_PACKAGE로 알 수 있습니다. 기본 스크립트에 없는 단계를 더할 때 사용합니다.

환경 변수

관리형 스크립트가 받는 값:

변수의미
TIKMATRIX_API_BASE서버 URL. 예: http://127.0.0.1:50809
TIKMATRIX_SESSION_ID이미 여러분을 대신해 보유 중인 임대
TIKMATRIX_SERIAL이 작업이 배포된 기기
TIKMATRIX_PACKAGE해석된 앱 패키지
TIKMATRIX_PLATFORMtiktok, instagram, generic

TikMatrix.from_env()가 이 값들을 대신 읽어줍니다.

독립 실행 스크립트는 이 값들을 받지 못합니다. 기기를 직접 임대하세요.

추가 환경 변수에 넣은 내용은 그 위에 덮어써서 병합됩니다. 파일을 고치지 않고 등록된 스크립트 하나에 실행별 설정을 넘기는 일반적인 방법입니다.

Python 라이브러리 레퍼런스

TikMatrix — 연결

호출하는 일
TikMatrix(base_url=None, timeout=30.0)연결. TIKMATRIX_API_BASE, 이어서 http://127.0.0.1:50809로 폴백
client.devices()온라인 기기. 각 항목에 serial, real_serial, busy
client.sessions()살아 있는 모든 임대(다른 프로세스 것 포함)
client.device(serial, label=..., ttl_secs=120)기기를 임대하고 Device 반환
TikMatrix.from_env()관리형 스크립트가 시작될 때 받은 기기를 이어받음

Device — 기기

호출하는 일
d.info()UIAutomator2 기기 정보
d.window_size()(너비, 높이)
d.screenshot(path=None)PNG 바이트. path를 주면 저장
d.hierarchy()현재 UI 트리(XML)
d.find(text=, resource_id=, description=, class_name=)일치하는 노드. 각각 boundscenter 포함
d.exists(**criteria)일치하는 것이 있는지
d.wait_for(timeout=10.0, interval=1.0, **criteria)나타날 때까지 대기 후 반환
d.click(timeout=10.0, **criteria)요소를 기다린 뒤 중앙을 탭
d.click_xy(x, y)좌표 탭
d.swipe(sx, sy, ex, ey, steps=20)스와이프
d.press(key)back, home, recent, enter
d.input_text(text)내장 빠른 입력기로 포커스된 입력란에 입력
d.jsonrpc(method, params=None, timeout=10)임의의 UIAutomator2 메서드
d.adb(*args, timeout_ms=None)ADB 명령 실행
d.release()임대 해제. with를 쓰면 자동

find는 덤프한 UI 트리를 대상으로 매칭하므로, 셀렉터가 빗나갔을 때 print(d.hierarchy())로 실제로 무엇을 검색했는지 볼 수 있습니다. 기기 화면의 요소 검사기는 같은 트리를 시각적으로 보여주며, resource-id를 찾는 가장 빠른 방법인 경우가 많습니다.

input_text에는 ADB가 필요합니다

내장 입력기로 브로드캐스트를 보내는데, 이는 adb shell을 거칩니다. 사용 전에 ADB 접근을 켜세요. 켜지 않으면 403으로 실패합니다.

오류

라이브러리는 두 가지 예외를 던지며, 둘 다 RuntimeError의 하위 클래스입니다.

예외발생 시점
DeviceBusyErrorHTTP 409 — 기기가 이미 임대 중이거나 플랜에 남은 기기 슬롯이 없음
TikMatrixError그 외 전부: 플랜 부족, 임대 만료, ADB 비활성, 셀렉터 불일치 등
from tikmatrix import TikMatrix, TikMatrixError, DeviceBusyError

client = TikMatrix()
try:
with client.device("192.168.1.5:5555") as d:
d.click(text="Log in", timeout=20)
except DeviceBusyError:
print("그 기기는 다른 곳에서 쓰는 중입니다 — 다른 기기를 쓰세요")
except TikMatrixError as exc:
print("실패:", exc)

관리형 스크립트에서는 예외를 그대로 밖으로 흘려보내는 편이 보통 옳습니다. 0이 아닌 종료 코드가 작업을 실패로 표시하고, 트레이스백이 작업 로그에 남습니다.

HTTP 엔드포인트

기기 조작에는 살아 있는 임대를 가리키는 x-session-id 헤더가 필요합니다. API 키는 없습니다. 로컬 API의 나머지와 마찬가지로 이 엔드포인트들은 인증을 하지 않으며, 네트워크에서 이 컴퓨터에 도달할 수 있다는 것 자체가 접근 제어입니다. CORS 헤더를 전혀 보내지 않으므로 브라우저 페이지가 아니라 프로그램(curl, Python, 서버 측 코드)에서 호출하세요.

메서드경로용도
GET/api/v1/rpc/devices온라인 기기와 사용 중 여부 목록
POST/api/v1/rpc/session기기 임대 → session_id
POST/api/v1/rpc/session/{id}/heartbeat임대 연장
DELETE/api/v1/rpc/session/{id}임대 해제
GET/api/v1/rpc/session살아 있는 임대 목록
POST/api/v1/rpc/jsonrpcUIAutomator2 메서드 호출
POST/api/v1/rpc/adbADB 명령 실행
GET/api/v1/rpc/hierarchy?serial=현재 UI 트리(XML)
GET/api/v1/rpc/screenshot?serial=현재 화면(PNG)

JSON 응답은 로컬 API의 나머지와 동일한 봉투 구조를 씁니다 — {"code": 0, "message": "success", "data": ...}, 실패 시 code가 0이 아닙니다. hierarchyscreenshot은 원시 본문을 반환합니다.

예시

# 기기 임대
curl -X POST http://127.0.0.1:50809/api/v1/rpc/session \
-H "Content-Type: application/json" \
-d '{"serial":"192.168.1.5:5555","label":"curl test","ttl_secs":120}'

# {"code":0,"message":"success","data":{"session_id":"ff3ae079-...","serial":"192.168.1.5:5555", ...}}

# 제어
curl -X POST http://127.0.0.1:50809/api/v1/rpc/jsonrpc \
-H "x-session-id: ff3ae079-..." \
-H "Content-Type: application/json" \
-d '{"serial":"192.168.1.5:5555","method":"deviceInfo","params":[]}'

# 작업하는 동안 임대 유지
curl -X POST http://127.0.0.1:50809/api/v1/rpc/session/ff3ae079-.../heartbeat \
-H "Content-Type: application/json" \
-d '{"ttl_secs":120}'

# 반납
curl -X DELETE http://127.0.0.1:50809/api/v1/rpc/session/ff3ae079-...

오류

상태 코드의미
403플랜이 Pro 미만, 임대 없음, 임대 만료, 또는 ADB 접근이 꺼짐
409기기가 이미 임대 중이거나 플랜에 남은 기기 슬롯이 없음

다른 언어로 작성하기

여기에 Python 전용인 것은 없습니다. HTTP 요청을 보낼 수 있는 런타임이면 무엇이든 됩니다. 관리형 모드의 계약은 "환경 변수 세 개를 읽고, 성공하면 0으로 종료한다"뿐입니다.

// my_flow.js — 등록 명령: node C:/scripts/my_flow.js
const base = process.env.TIKMATRIX_API_BASE || "http://127.0.0.1:50809";
const serial = process.env.TIKMATRIX_SERIAL;
const session = process.env.TIKMATRIX_SESSION_ID;

async function jsonrpc(method, params = []) {
const res = await fetch(`${base}/api/v1/rpc/jsonrpc`, {
method: "POST",
headers: { "content-type": "application/json", "x-session-id": session },
body: JSON.stringify({ serial, method, params }),
});
const body = await res.json();
if (!res.ok || body.code !== 0) throw new Error(body.message || res.statusText);
return body.data;
}

console.log(await jsonrpc("deviceInfo"));

인터프리터가 PATH에 없다면 명령에 전체 경로를 적으세요. 예: C:/Program Files/nodejs/node.exe C:/scripts/my_flow.js

API로 사용자 스크립트 실행하기

등록된 스크립트는 작업 관리 API로도 시작할 수 있어서, 한 스크립트가 후속 작업을 큐에 넣을 수 있습니다.

curl -X POST http://127.0.0.1:50809/api/v1/task \
-H "Content-Type: application/json" \
-d '{
"serials": ["192.168.1.5:5555"],
"script_name": "custom_script",
"script_config": {
"custom_script_id": 1,
"custom_script_platform": "generic"
}
}'

custom_script_id는 등록한 스크립트의 ID입니다.

ADB 접근

/api/v1/rpc/adb는 스크립트에 기기 셸을 제공합니다. 미디어 푸시, APK 설치, 시스템 설정 변경에 필요합니다. 다만 API 키가 없는 엔드포인트 위의 완전한 셸이므로 기본적으로 꺼진 채로 배포됩니다. 이것이 필요한 스크립트가 생겼을 때 설정 → Developer API → ADB 명령 허용 에서 켜세요. /rpc/jsonrpc를 통한 UI 자동화는 켜지 않아도 동작합니다.

꺼져 있는 동안 /api/v1/rpc/adb는 403을 반환하며 나머지 API는 정상 동작합니다. 스크립트가 실행한 모든 ADB 명령은 로그 파일에 기록됩니다.

오래 버티는 스크립트 작성법

  • 정해진 시간을 자지 말고 화면을 기다리세요. d.wait_for(...)는 요소가 나타나는 즉시 반환합니다. 고정 sleep은 필요 이상으로 느리거나, 상태가 나쁜 날에는 너무 짧습니다.
  • 탭하기 전에 확인하세요. 동의 대화상자나 "나중에" 같은 안내에 d.exists(...)를 한 번 쓰는 비용은 트리 덤프 한 번이고, 허공을 탭해 망칠 실행 한 번을 살립니다.
  • 한 일을 출력하세요. 관리형 모드에서 stdout이 곧 작업 로그이며, 아무도 지켜보지 않은 실행에 대한 유일한 기록입니다.
  • 재실행이 안전하도록 만드세요. 재시도는 프로그램 전체를 처음부터 다시 돌립니다. 게시하는 스크립트라면 처음부터 시작한다고 가정하지 말고 이미 게시했는지 확인해야 합니다.
  • 스크립트 하나에 일 하나. 동시 실행은 기기 단위이므로, 열 대에 작은 작업 열 개를 돌리는 편이 한 스크립트가 열 대를 반복 순회하는 것보다 훨씬 빨리 끝납니다.

참고 사항과 제한

  • 명령은 셸을 거치지 않고 직접 실행되므로 &&|는 연산자가 아니라 인자로 취급됩니다. 셸 동작이 필요하면 cmd /c "..."(Windows) 또는 sh -c "..."(macOS)를 등록하세요.
  • 공백이 포함된 경로는 따옴표로 감싸세요: "C:/Program Files/Python/python.exe" my_script.py
  • 타임아웃을 넘긴 스크립트는 강제 종료되고 작업은 실패 처리됩니다.
  • 0이 아닌 종료 코드는 작업을 실패로 표시합니다. 스크립트가 stdout과 stderr에 쓴 내용은 모두 작업 로그에 들어갑니다.
  • 스크립트는 TikMatrix와 동일한 권한으로 실행됩니다. 직접 작성했거나 신뢰하는 프로그램만 등록하세요.

문제 해결

API access requires Pro or higher plan (403) 이 컴퓨터의 라이선스가 Starter이거나 비활성입니다. 설정 → 라이선스 를 확인하세요.

127.0.0.1:50809 연결 거부 TikMatrix가 실행 중이 아니거나 다른 사용자 계정에서 실행 중입니다. 서버는 앱이 열려 있는 동안에만 존재합니다.

임대 시도마다 409 기기가 실제로 사용 중이거나(설정 → Developer API → 활성 기기 세션 확인), 플랜의 기기 슬롯이 실행 중인 작업으로 모두 찬 상태입니다.

긴 단계 도중에 임대가 만료됨 기본 TTL은 120초이고 라이브러리가 백그라운드에서 갱신하므로, 보통 이는 스크립트가 메인 스레드를 TTL보다 오래 붙잡았다는 뜻입니다. ttl_secs를 올리거나(최대 600) 오래 걸리는 작업을 그 스레드 밖으로 옮기세요.

d.adb(...)가 403으로 실패 ADB 접근이 꺼져 있습니다. 설정 → Developer API → ADB 명령 허용 에서 켜세요.

셀렉터가 전혀 일치하지 않음 print(d.hierarchy())find가 실제로 검색한 트리를 보여줍니다. 텍스트는 정확히 일치해야 하므로 끝의 공백이나 현지화된 문구가 흔한 원인입니다. text보다 resource_id로 매칭하는 편이 안정적입니다.

작업은 실패인데 기기는 멀쩡해 보임 작업 로그를 읽어보세요. 0이 아닌 종료는 — 성공적으로 끝난 뒤 던져진 미처리 예외라도 — 자동화 자체가 잘 동작했더라도 작업을 실패 처리합니다.

다음 단계