본문으로 건너뛰기

사용자 스크립트

기본 제공 스크립트는 일반적인 흐름을 대부분 다룹니다. 그것으로 부족할 때 — 순서를 바꾸고 싶을 때, 기본 스크립트가 건드리지 않는 화면을 다뤄야 할 때, 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 — 기기가 그대로 넘어옵니다. 앱을 실행하지 않고, 계정을 전환하지 않으며, 입력기를 확인하지도 않고, 끝난 뒤에 무엇도 닫지 않습니다. TikMatrix가 자동화하지 않는 앱을 자동화할 때 사용합니다.

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

Threads에서는 Settings → Switch accounts 시트를 통해 계정이 전환되며, 그 후 프로필 페이지에서 핸들이 읽혀집니다. 기기에 로그인하지 않은 계정을 지정하는 작업은 현재 활성화되어 있는 계정으로 실행되는 대신 실패합니다.

환경 변수​

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

변수의미
TIKMATRIX_API_BASE서버 URL. 예: http://127.0.0.1:50809
TIKMATRIX_SESSION_ID이미 여러분을 대신해 보유 중인 임대
TIKMATRIX_SERIAL이 작업이 배포된 기기
TIKMATRIX_PACKAGE해석된 앱 패키지
TIKMATRIX_PLATFORMtiktok, instagram, threads, 또는 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=)일치하는 노드. 각각 bounds와 center 포함
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이 아닙니다. hierarchy와 screenshot은 원시 본문을 반환합니다.

예시​

# 기기 임대
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이 아닌 종료는 — 성공적으로 끝난 뒤 던져진 미처리 예외라도 — 자동화 자체가 잘 동작했더라도 작업을 실패 처리합니다.

다음 단계​