برمجة JavaScript
يدعم Chute برمجة JavaScript لتعديل الطلبات/الاستجابات المتقدم، مطابقة القواعد المخصصة، تحليل DNS، والمهام المجدولة. تستخدم السكريبتات محرك Apple JavaScriptCore وتتبع واجهة برمجة السكريبتات المتوافقة مع Surge.
تعرف السكريبتات في القسم [Script] من ملف الإعدادات.
الإعدادات
[Script]
MyScript = type=http-request, script-path=/path/to/script.js, pattern=^https?://example\.com, requires-body=true, max-size=262144, timeout=10, argument=myArg
CronJob = type=cron, script-path=/path/to/cron.js, cron-expression=*/30 * * * *
معاملات السكريبت
| المعامل | مطلوب | الافتراضي | الوصف |
|---|---|---|---|
type |
نعم | http-request |
نوع تشغيل السكريبت (انظر أدناه) |
script-path |
نعم | — | مسار ملف محلي أو رابط HTTP(S) لسكريبت JS |
pattern |
لا | (مطابقة الكل) | نمط رابط بتعبير نمطي لتصفية وقت تشغيل السكريبت |
requires-body |
لا | اكتشاف تلقائي | فرض استلام السكريبت لمحتوى الطلب/الاستجابة الكامل |
max-size |
لا | 131072 (128KB) | الحد الأقصى لحجم المحتوى بالبايت للسكريبتات التي تصل للمحتوى. إذا تجاوز المحتوى المجمّع هذا الحجم، يُتخطى السكريبت لذلك الطلب (لا يتم اقتطاع المحتوى) |
timeout |
لا | 5.0 ثانية | مهلة تنفيذ لكل سكريبت. القيم فوق 30 ثانية تُخفض إلى 30 |
argument |
لا | — | نص وسيط مخصص متاح كـ $argument في JS |
debug |
لا | false | محجوز؛ لا تأثير له حالياً |
cron-expression |
لا | — | تعبير جدولة cron (لنوع cron فقط) |
wake-system |
لا | false | محجوز؛ يُحلَّل لكن لا تأثير له حالياً |
enable |
لا | true | تفعيل أو تعطيل هذا السكريبت |
script-update-interval |
لا | 0 | استطلاع تحديث السكريبتات البعيدة (انظر نموذج التنفيذ) |
أنواع السكريبتات
| النص النوعي | التعداد | الوصف |
|---|---|---|
http-request |
HTTP Request | اعتراض وتعديل طلبات HTTP قبل التوجيه |
http-response |
HTTP Response | اعتراض وتعديل استجابات HTTP قبل العميل |
http-request-before-send |
HTTP Request Before Send | تعديل الطلب بعد جمع المحتوى الكامل، قبل الإرسال |
rule |
Rule | منطق مطابقة قواعد مخصص |
dns |
DNS | تحليل DNS مخصص |
cron |
Cron | سكريبتات مجدولة/موقوتة |
event |
Event | معالجات أحداث النظام (مثال: network-changed) |
اكتشاف المحتوى التلقائي: إذا كان مصدر السكريبت يحتوي على
$request.bodyأو$response.body، سيتم توفير المحتوى تلقائياً حتىmax-size. استخدمrequires-body=trueلفرض هذا السلوك.
مرجع JavaScript API
تعمل السكريبتات في بيئة JavaScriptCore معزولة مع توفر الكائنات العامة التالية.
$request (للقراءة فقط)
متوفر في: http-request, http-response, http-request-before-send, rule
| الخاصية | النوع | الوصف |
|---|---|---|
.url |
String | رابط الطلب الكامل |
.method |
String | طريقة HTTP (GET, POST, إلخ) أو QUERY لـ DNS |
.headers |
Object | ترويسات الطلب كأزواج key-value |
.body |
String or null | محتوى الطلب (مفكوك UTF-8) |
.bodyBytes |
Uint8Array or null | بايتات محتوى الطلب الخام |
.hostname |
String | اسم المضيف الهدف |
.destPort |
Number | منفذ الوجهة |
.processPath |
String | مسار العملية الطالبة (macOS فقط) |
.userAgent |
String | قيمة ترويسة User-Agent |
.sourceIP |
String | عنوان IP المصدر |
.listenPort |
Number | منفذ استماع البروكسي |
.requestId |
String | معرف طلب فريد |
.dnsResult |
String | عنوان IP المحلل |
.srcPort |
Number | منفذ المصدر |
.protocol |
String | البروتوكول المكتشف: http, https, tcp, dns |
$response (للقراءة فقط)
متوفر في: http-response
| الخاصية | النوع | الوصف |
|---|---|---|
.status |
Number | رمز حالة HTTP |
.headers |
Object | ترويسات الاستجابة كأزواج key-value |
.body |
String or null | محتوى الاستجابة (مفكوك UTF-8) |
.bodyBytes |
Uint8Array or null | بايتات محتوى الاستجابة الخام |
.rawBody |
Uint8Array or null | اسم بديل لـ .bodyBytes |
$done(value) — معالج الإكمال
يجب استدعاؤه مرة واحدة بالضبط في نهاية السكريبت للإشارة إلى الاكتمال. يتوقف تنفيذ السكريبت حتى يتم استدعاء $done() أو انتهاء المهلة.
$done({}) // تمرير — لا تعديلات
$done() // إجهاض الاتصال
$done({matched: true}) // نتيجة مطابقة القاعدة (لسكريبتات القواعد فقط)
$done({address: "1.2.3.4"}) // نتيجة DNS (لسكريبتات DNS فقط)
قيم إرجاع سكريبت طلب HTTP:
$done({
url: "https://new.example.com/path", // إعادة كتابة الرابط
headers: {"X-Custom": "value"}, // تعديل الترويسات
body: "new request body", // تعديل المحتوى
response: { // إرجاع استجابة اصطناعية (تخطي الخادم العلوي)
status: 200,
headers: {"Content-Type": "text/html"},
body: "<html>Blocked</html>"
}
})
عند توفير response، يتم اختصار الطلب: يرجع Chute الاستجابة الاصطناعية مباشرة إلى العميل دون الاتصال بالخادم العلوي. هذا مفيد للحظر، محاكاة APIs، أو إرجاع محتوى مخزن مؤقتاً.
قيم إرجاع سكريبت استجابة HTTP:
$done({
status: 200, // تعديل رمز الحالة
headers: {"X-Custom": "value"}, // تعديل ترويسات الاستجابة
body: "new response body", // تعديل محتوى الاستجابة
url: "https://other.example.com" // تشغيل إعادة توجيه 302
})
عند توفير url، يرجع Chute إعادة توجيه 302 إلى الرابط المعطى بدلاً من الاستجابة الأصلية. يجب إرجاع url مع واحد على الأقل من status / headers / body؛ النتيجة التي تحتوي على url فقط تُعامل كتمرير.
قيم إرجاع سكريبت DNS:
$done({address: "1.2.3.4"}) // IP واحد
$done({addresses: ["1.2.3.4", "5.6.7.8"]}) // عدة IPs
$done({address: "10.0.0.1", ttl: 300}) // مع TTL مخصص (بالثواني، الافتراضي 60)
المفتاحان
server/serversمقبولان لكن يتم تجاهلهما حالياً؛ يسقط التحليل إلى DNS العادي.
$httpClient — عميل HTTP غير متزامن
إجراء طلبات HTTP من داخل السكريبتات. يتم إلغاء جميع الطلبات عند $done() أو المهلة.
$httpClient.get(url, function(error, response, data) {
if (error) {
console.log("Request failed: " + error)
} else {
console.log("Status: " + response.status)
console.log("Response: " + data)
}
})
$httpClient.post(url, {headers: {...}, body: "...", timeout: 5}, callback)
$httpClient.put(url, options, callback)
$httpClient.del(url, options, callback)
$httpClient.head(url, options, callback)
$httpClient.options(url, options, callback)
$httpClient.patch(url, options, callback)
توقيع callback: callback(error, response, data)
error: نص الخطأ أوnullresponse:{status: Number, headers: Object}أوnulldata: محتوى استجابة UTF-8 نصي أوnull
$httpClientمتوفر في سكريبتاتhttp-requestوhttp-responseوhttp-request-before-sendوcronوevent. وهو غير متوفر في سكريبتاتruleوdns.
$persistentStore — تخزين Key-Value
تخزين key-value دائم يبقى عبر إعادة تشغيل السكريبتات والعمليات. مدعوم بـ NSUserDefaults.
$persistentStore.write(data, key) // تخزين قيمة
$persistentStore.read(key) // استرجاع قيمة
$persistentStore.remove(key) // إزالة قيمة
$notification — الإشعارات المحلية
نشر إشعارات نظام محلية.
$notification.post("Title", "Subtitle", "Notification body text")
$network — معلومات الشبكة
معلومات حالة الشبكة للقراءة فقط.
$network.dns // مصفوفة من عناوين IP لخوادم DNS
$network.wifi // {ssid: "WiFiName", bssid: null}
bssidدائماًnull؛ ولا تُعبأssidإلا على iOS.تنبيه: في الإصدارات الحالية، الخاصيتان
$networkو$environmentغير مرئيتين للسكريبتات بسبب قيد في الجسر (bridging) وتُقرآن كـundefined. بيانات$scriptالوصفية متاحة في سكريبتات cron و event فقط.
$environment — معلومات وقت التشغيل
$environment.system // "iOS" أو "macOS"
$environment.appVersion // نص إصدار KLNEKit SDK
$utils — أدوات مساعدة
$utils.geoip("1.2.3.4") // رمز الدولة (مثال: "US")
$utils.ipasn("1.2.3.4") // رقم ASN (مثال: "13335")
$utils.ungzip(data) // فك ضغط بيانات gzip
$klne — واجهة التحكم بالبروكسي
التحكم في وقت تشغيل البروكسي من السكريبتات. $klne متوفر في جميع أنواع السكريبتات.
$klne.getPolicyGroups() // الحصول على جميع مجموعات السياسات
$klne.selectPolicy("Group", "Proxy") // تبديل سياسة لمجموعة
$klne.getActiveConnections() // سرد الاتصالات النشطة
$klne.closeConnection("id") // محجوزة — حالياً بلا تأثير
$klne.flushDNS() // مسح ذاكرة DNS المؤقتة
$klne.startURLTest("Group") // تشغيل اختبار رابط لمجموعة
$klne.reloadConfiguration() // إعادة تحميل جميع الإعدادات
$klne.setOutboundMode("rule") // تعيين النمط: "global"/"proxy", "direct", "rule"
$klne.setHTTPCaptureEnabled(true) // تفعيل/تعطيل MitM
تُرجع getPolicyGroups() مجموعات السياسات القابلة للاختيار:
{
count: 1, // Number — عدد المجموعات
policyGroups: [{
name: "MainGroup", // String — اسم المجموعة
type: 0, // Number — 0 select, 1 url-test, 2 fallback, 3 ssid, 5 load-balance
policyNames: ["A", "B"], // Array of String — السياسات المرشحة
selectedIndex: 0, // Number — فهرس السياسة النشطة (يغيب عندما تكون غير محسومة)
selectedPolicy: "A" // String — اسم السياسة النشطة (يغيب عندما تكون غير محسومة)
}]
}
تُرجع getActiveConnections() مصفوفة (Array) تصف الاتصالات الحالية:
[{
id: "0x600002f01230", // String — معرّف اتصال مبهم
host: "example.com", // String — المضيف الوجهة (يغيب عندما يكون مجهولاً)
port: 443 // Number — منفذ الوجهة
}]
أما بقية الدوال فتأخذ وسائط بسيطة ولا تُرجع شيئاً:
selectPolicy(group, policy)— تجعلpolicy(اسماً منpolicyNamesالخاصة بالمجموعة) السياسة النشطة لـgroup. اسم المجموعة أو السياسة غير المعروف يُتجاهل مع تحذير في السجل.closeConnection(id)— محجوزة. لا تستطيع النواة الحالية إغلاق اتصالات مفردة؛ استدعاؤها لا يفعل سوى تسجيل تحذير ولا يغلق شيئاً.flushDNS()— تمسح ذاكرة DNS المؤقتة.startURLTest(group)— تبدأ اختبار زمن استجابة غير متزامن لمجموعة من نوعurl-testأوfallback؛ أنواع المجموعات الأخرى والأسماء غير المعروفة تُتجاهل مع تحذير.reloadConfiguration()— تعيد تحميل الإعدادات الحالية.setOutboundMode(mode)—"global"و"proxy"كلاهما يوجّه كل الحركة عبر البروكسي، و"direct"يرسل كل الحركة مباشرةً، وأي قيمة أخرى تختار نمط القواعد.setHTTPCaptureEnabled(enabled)— تفعّل أو تعطّل فك تشفير HTTPS (MitM) أثناء التشغيل؛ تأخذ قيمة منطقية.
$script — بيانات السكريبت الوصفية
$script.name // اسم السكريبت من الإعدادات
$script.type // نص نوع السكريبت
$script.startTime // طابع زمني رتيب (ثوانٍ منذ مرجع إقلاع النظام، وليس Epoch)
globals لكل تنفيذ
يتم حقن المتغيرات التالية لكل تنفيذ سكريبت وهي خاصة بأنواع سكريبتات معينة.
$argument — وسيط السكريبت
القيمة النصية من معامل argument= في إعدادات السكريبت. متوفر في: http-request, http-response, http-request-before-send, rule, dns, cron, event.
console.log("Argument: " + $argument)
$domain — نطاق DNS (لسكريبتات DNS فقط)
اسم النطاق الذي يتم الاستعلام عنه. متوفر فقط في سكريبتات dns.
var domain = $domain // مثال: "example.com"
$cronexp — تعبير Cron (لسكريبتات Cron فقط)
تعبير جدولة cron من إعدادات السكريبت. متوفر فقط في سكريبتات cron.
console.log("Schedule: " + $cronexp) // مثال: "*/30 * * * *"
$event — معلومات الحدث (لسكريبتات Event فقط)
معلومات عن الحدث المشغل. حالياً فقط network-changed مدعوم.
console.log("Event: " + $event.name) // "network-changed"
console — التسجيل
console.log("Debug message") // سجل تفصيلي
console.warn("Warning message") // سجل تحذيري
console.error("Error message") // سجل تحذيري موسوم كخطأ JavaScript
setTimeout(fn, seconds) — مؤقت
جدولة دالة للتشغيل بعد تأخير.
setTimeout(function() {
console.log("Delayed execution")
}, 2.5) // 2.5 ثانية
$scriptImport(subScriptPath) — محمل السكريبتات الفرعية
تحميل وتقييم ملف JavaScript آخر. تُدعم مسارات الملفات المحلية فقط؛ روابط http(s):// و file:// مرفوضة. ($script محجوز لكائن بيانات السكريبت الوصفية.)
$scriptImport("/path/to/helper.js")
// تمرير البيانات بين السكريبتات باستخدام $persistentStore
تفاصيل أنواع السكريبتات
سكريبت طلب HTTP
ينفذ عند استلام ترويسات الطلب. يمكنه تعديل الرابط، الترويسات، والمحتوى قبل توجيه الطلب.
[Script]
ModifyHeaders = type=http-request, script-path=modify.js, pattern=^https://api\.example\.com
سكريبت استجابة HTTP
ينفذ عند استلام ترويسات الاستجابة. يمكنه تعديل الحالة، الترويسات، والمحتوى قبل الإرجاع إلى العميل.
[Script]
ModifyResponse = type=http-response, script-path=response.js, pattern=^https://api\.example\.com
سكريبت HTTP Request Before Send
ينفذ بعد جمع محتوى الطلب الكامل، قبل الإرسال إلى الخادم العلوي مباشرة. مفيد لتعديل محتويات طلبات POST/PUT.
[Script]
BeforeSend = type=http-request-before-send, script-path=before-send.js, pattern=^https://api\.example\.com, requires-body=true
سكريبت القاعدة
مطابقة قواعد مخصصة. يجب على السكريبت استدعاء $done({matched: true}) أو $done({matched: false}).
[Rule]
SCRIPT,MyRuleScript,DIRECT
[Script]
MyRuleScript = type=rule, script-path=rule.js
سكريبت DNS
تحليل DNS مخصص. يستقبل $domain ويرجع عنواناً (عناوين) محللة.
// dns.js
var domain = $domain
if (domain === "internal.example.com") {
$done({address: "10.0.0.1", ttl: 300})
} else {
$done({}) // تمرير إلى تحليل DNS العادي
}
سكريبت Cron
تنفيذ مجدول باستخدام تعبيرات cron. الحد الأدنى للفاصل هو 60 ثانية.
[Script]
HalfHourlyTask = type=cron, script-path=task.js, cron-expression=*/30 * * * *
يُحترم فقط حقل الدقائق من التعبير: */N يعمل كل N دقيقة. أي تعبير آخر ينطلق كل 60 ثانية، ويجب على السكريبت التحقق من الوقت الحالي بنفسه ليقرر ما إذا كان سيتصرف.
سكريبت الحدث
يتم تشغيله بواسطة أحداث النظام. حالياً يدعم حدث network-changed (ينطلق عند تغير شبكة WiFi أو الجوال).
[Script]
NetChange = type=event, script-path=network-changed.js
كائن $event متاح:
$event.name // "network-changed"
أمثلة عملية
إعادة توجيه الأجهزة المحمولة
سكريبت http-request يعيد توجيه مستخدمي الجوال بناءً على User-Agent:
[Script]
MobileRedirect = type=http-request, script-path=mobile-redirect.js, pattern=^https://example\.com
// mobile-redirect.js
var ua = $request.headers["User-Agent"] || ""
if (/Mobile|Android|iPhone/.test(ua)) {
$done({
response: {
status: 302,
headers: {"Location": "https://m.example.com" + $request.url.replace(/.*example\.com/, "")},
body: ""
}
})
} else {
$done({})
}
حظر المحتوى في استجابات API
سكريبت http-response يزيل الإعلانات والمحتوى المدعوم من استجابة JSON API:
[Script]
RemoveAds = type=http-response, script-path=remove-ads.js, pattern=^https://api\.example\.com/feed, requires-body=true
// remove-ads.js
var body = JSON.parse($response.body)
if (body.ads) {
delete body.ads
}
if (body.recommendations) {
body.recommendations = body.recommendations.filter(function(r) {
return !r.sponsored
})
}
$done({body: JSON.stringify(body)})
تعديل محتوى الطلب قبل الإرسال
سكريبت http-request-before-send ينقي حمولة POST:
[Script]
SanitizePayload = type=http-request-before-send, script-path=sanitize.js, pattern=^https://api\.example\.com/submit, requires-body=true
// sanitize.js
var body = JSON.parse($request.body)
body.clientSecret = "[REDACTED]"
body.timestamp = Math.floor(Date.now() / 1000)
$done({body: JSON.stringify(body)})
قاعدة مخصصة: التوجيه حسب الوقت
سكريبت rule يختار بروكسي مختلف حسب وقت اليوم:
[Rule]
SCRIPT,TimeBasedRule,ProxyA
[Script]
TimeBasedRule = type=rule, script-path=time-rule.js
// time-rule.js
var hour = new Date().getHours()
if (hour >= 9 && hour < 18) {
$done({matched: false}) // السقوط إلى القاعدة التالية خلال ساعات العمل
} else {
$done({matched: true}) // استخدام ProxyA خارج ساعات العمل
}
DNS مخصص للنطاقات الداخلية
سكريبت dns يحل أسماء المضيفين الداخلية إلى عناوين IP محلية:
[Script]
InternalDNS = type=dns, script-path=internal-dns.js
// internal-dns.js
var internalHosts = {
"gitlab.local": "10.0.0.10",
"registry.local": "10.0.0.11",
"monitor.local": "10.0.0.12"
}
if (internalHosts[$domain]) {
$done({address: internalHosts[$domain], ttl: 3600})
} else {
$done({}) // تمرير إلى DNS العادي
}
التبديل التلقائي للسياسة عند تغيّر الشبكة
سكريبت event يفحص الاتصال بعد تغيّر الشبكة ويبدل مجموعة السياسات وفقًا لذلك:
[Script]
NetSwitch = type=event, script-path=network-switch.js
// network-switch.js
$httpClient.head("https://www.google.com/generate_204", {timeout: 5},
function(error, response, data) {
if (error) {
// فشل الفحص على الشبكة الجديدة — التبديل إلى المجموعة الاحتياطية
$klne.selectPolicy("MainGroup", "BackupProxy")
console.log("Probe failed after " + $event.name)
} else {
$klne.selectPolicy("MainGroup", "MainProxy")
}
$done({})
})
تنبيه: قراءة SSID لشبكة Wi-Fi عبر
$networkهي الحل الطبيعي هنا، لكن$networkغير مرئي للسكريبتات حاليًا (انظر التنبيه في $network)؛ في الإصدارات الحالية يمكن الاعتماد على الفحص عبر$httpClient.
فحص دوري للصحة
سكريبت cron يتحقق من صحة البروكسي كل 30 دقيقة:
[Script]
HealthCheck = type=cron, script-path=health-check.js, cron-expression=*/30 * * * *
// health-check.js
$httpClient.head("https://www.google.com/generate_204", {timeout: 10},
function(error, response, data) {
if (error || response.status !== 204) {
console.error("Health check failed: " + (error || "status " + response.status))
$notification.post("Chute Alert", "Health Check", "Cannot reach Google")
} else {
console.log("Health check OK")
}
$done({})
}
)
استدعِ
$done({})داخل الـ callback — إكمال السكريبت يلغي جميع طلبات$httpClientالمعلقة، لذا فإن$done()متزامناً في نهاية السكريبت سيلغي فحص الصحة قبل وصول استجابته.
إثراء استجابات API ببيانات خارجية
سكريبت http-response يثري بيانات المستخدمين باستدعاء API ثانوي:
[Script]
EnrichUsers = type=http-response, script-path=enrich.js, pattern=^https://api\.example\.com/users, requires-body=true
// enrich.js
var users = JSON.parse($response.body)
var pending = users.length
if (pending === 0) { $done({}) }
users.forEach(function(user, index) {
$httpClient.get("https://internal-api.example.com/avatar/" + user.id,
function(error, resp, data) {
if (!error && resp.status === 200) {
users[index].avatar = JSON.parse(data).url
}
pending--
if (pending === 0) {
$done({body: JSON.stringify(users)})
}
}
)
})
نموذج التنفيذ
- تعمل جميع السكريبتات على طابور تسلسلي مخصص لسلامة الخيوط.
- كل تنفيذ سكريبت له مهلة خاصة به؛ إذا لم يتم استدعاء
$done()خلال المهلة، يعامل السكريبت كتمرير. - يُحتفظ بمجموعة من 3 سياقات JSContext مهيأة مسبقاً؛ يُتخلص من السياق بعد كل تنفيذ ويُستبدل بآخر جديد، لذا لا تتسرب globals أبداً بين التشغيلات.
- على iOS/tvOS، تخضع السكريبتات لحد ذاكرة 10MB؛ على macOS، 512MB.
- السكريبتات البعيدة (مسارات HTTP/HTTPS) تُجلب عند كل تنفيذ. عندما تكون
script-update-intervalغير صفرية، يستطلع Chute الرابط إضافياً بطلب HEAD شرطي كل 10 دقائق؛0(الافتراضي) يعطل الاستطلاع.
دمج سكريبتات الوحدات
يمكن أيضاً تعريف السكريبتات في ملفات Module (.sgmodule) تحت القسم [Script].
هذه الصفحة ترجمة للنسخة الإنجليزية. في حال وجود اختلاف، يُعتمد على النسخة الإنجليزية.
ملاحظة: التطبيق لا يدعم اللغة العربية حاليًا.