النصوص البرمجية المخصصة
تغطي النصوص المدمجة المسارات الشائعة. وعندما تحتاج إلى شيء لا توفره — خطوة بترتيب مختلف، أو شاشة لا تلمسها أبدًا، أو تطبيق ليس 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 الجهاز قبل بدء برنامجك، ويمرّر معرّف الحجز عبر بيئة التشغيل.
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
ثم انسخ tikmatrix.py من دليل SDK بجانب نصك البرمجي. المكتبة ملف واحد بلا اعتماديات أخرى.
لست مضطرًا لاستخدامها — الواجهة مجرد JSON عبر HTTP، ونقاط النهاية الخام موثّقة أدناه.
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 اختياري يُدمج في بيئة البرنامج |
| مفعّل | لإيقاف نص دون حذفه. النص المعطّل لا يمكن إرساله للتنفيذ |
ثم اضغط ▶ في صف النص واختر أجهزتك، تمامًا كما مع أي نص مدمج.
يستطيع المساعد الذكي صياغة نص مخصص انطلاقًا من وصف بلغة عادية وتسجيله في خطوة واحدة. وهو يعرض عليك الملف كاملًا قبل كتابة أي شيء على القرص.
حجوزات الأجهزة
لا يمكن التحكم في الهاتف إلا من جهة واحدة في الوقت نفسه. حجزه يخبر TikMatrix أن الجهاز مشغول، وبالتالي:
- لن يرسل طابور المهام مهمة إلى الشاشة نفسها، و
- ستُبلغ استدعاءات JSON-RPC عن صحة الوكيل تمامًا كما يفعل نص مدمج، فيرى المراقب وكيلًا مشغولًا لا وكيلًا صامتًا.
كما يستهلك الحجز مقعد جهاز واحدًا من خطتك.
تنتهي صلاحية الحجوزات — 120 ثانية افتراضيًا، و600 كحد أقصى. تجدّد مكتبة بايثون حجزك في خيط خلفي وتحرّره عند انتهاء كتلة with، فينعتق الجهاز خلال ثوانٍ إذا انهار النص بدل بقائه محجوزًا حتى تعيد تشغيل التطبيق. وإن كنت تستدعي الواجهة مباشرة فعليك إرسال نبضات الحياة بنفسك.
يمكنك رؤية كل الحجوزات النشطة — وتحرير أي منها قسرًا — من الإعدادات ← Developer API ← جلسات الأجهزة النشطة.
أوضاع المنصة
يعلن النص المسجّل عن هدفه:
Generic — يُسلَّم الجهاز كما هو. لا يُفتح أي تطبيق، ولا تبديل حسابات، ولا فحص لطريقة الإدخال، ولا يُغلق شيء بعد الانتهاء. استخدمه لأتمتة أي تطبيق لا يوجد برنامج TikMatrix مدمج له.
TikTok / Instagram / Threads — يُفتح التطبيق ويُجعل الحساب الصحيح نشطًا قبل بدء برنامجك، ويُغلق التطبيق عند الانتهاء، تمامًا كما مع نص مدمج. ويخبرك TIKMATRIX_PACKAGE بالحزمة التي جرى تحديدها. استخدمه لإضافة خطوة لا تغطيها النصوص المدمجة.
على Threads يتم التبديل من خلال الإعدادات → تبديل الحسابات في التطبيق، وتتم قراءة المقبض على صفحة الملف الشخصي بعد ذلك. تفشل المهمة التي تسمي حسابًا لم يتم تسجيل الدخول إليه على الجهاز، بدلًا من التشغيل كمن يصادف أن يكون نشطًا.
متغيرات البيئة
يستقبل النص المُدار:
| المتغير | المعنى |
|---|---|
TIKMATRIX_API_BASE | عنوان الخادم، مثل http://127.0.0.1:50809 |
TIKMATRIX_SESSION_ID | الحجز المحفوظ لك بالفعل |
TIKMATRIX_SERIAL | الجهاز الذي أُرسلت إليه هذه المهمة |
TIKMATRIX_PACKAGE | حزمة التطبيق المحدَّدة |
TIKMATRIX_PLATFORM | tiktok أو instagram أو threads أو generic |
تقرأ TikMatrix.from_env() كل ذلك نيابة عنك.
النصوص المستقلة لا تحصل على أي منها — احجز جهازًا صراحةً بدلًا من ذلك.
وكل ما تضعه في متغيرات بيئة إضافية يُدمج فوق ذلك، وهي الطريقة المعتادة لتمرير إعدادات خاصة بكل تشغيل إلى نص مسجّل واحد دون تعديل الملف.
مرجع مكتبة بايثون
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() | شجرة الواجهة الحالية بصيغة 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 على شجرة الواجهة المستخرجة، لذا حين يخطئ المحدِّد يمكنك تنفيذ print(d.hierarchy()) لترى بالضبط ما جرى البحث فيه. ويعرض فاحص العناصر في واجهة الجهاز الشجرة نفسها بصريًا، وهو عادةً أسرع طريقة للعثور على resource-id.
input_text يحتاج إلى ADBيرسل بثًا إلى لوحة الإدخال المرفقة عبر adb shell. فعّل وصول ADB قبل استخدامه، وإلا فشل برمز 403.
الأخطاء
ترفع المكتبة استثناءين، وكلاهما يرث من RuntimeError:
| الاستثناء | متى |
|---|---|
DeviceBusyError | HTTP 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)
في النص المُدار يكون ترك الاستثناء يخرج هو التصرف الصحيح عادةً: فالخروج بقيمة غير صفرية يعلّم المهمة كفاشلة، ويصل التتبّع إلى سجل المهمة.
نقاط نهاية HTTP
تحتاج عمليات الجهاز إلى ترويسة x-session-id تشير إلى حجز نشط. لا يوجد مفتاح API: فكما هي حال بقية الواجهة المحلية، هذه النقاط غير مصادَق عليها — والقدرة على الوصول إلى الجهاز عبر الشبكة هي ضابط الوصول. وهي لا ترسل أي ترويسات CORS، لذا استدعِها من برنامج (curl أو بايثون أو أي كود من جهة الخادم) لا من صفحة في المتصفح.
| الطريقة | المسار | الغرض |
|---|---|---|
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/jsonrpc | استدعاء دالة UIAutomator2 |
POST | /api/v1/rpc/adb | تنفيذ أمر ADB |
GET | /api/v1/rpc/hierarchy?serial= | شجرة الواجهة الحالية بصيغة XML |
GET | /api/v1/rpc/screenshot?serial= | الشاشة الحالية بصيغة PNG |
تستخدم استجابات JSON ا لغلاف نفسه المستخدم في بقية الواجهة المحلية — {"code": 0, "message": "success", "data": ...} — مع code غير صفري عند الفشل. أما 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-...