برمجة 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, debug=true
CronJob = type=cron, script-path=/path/to/cron.js, cron-expression=* * * * *, wake-system=true
معاملات السكريبت
| المعامل | مطلوب | الافتراضي | الوصف |
|---|---|---|---|
type |
نعم | http-request |
نوع تشغيل السكريبت (انظر أدناه) |
script-path |
نعم | — | مسار ملف محلي أو رابط HTTP(S) لسكريبت JS |
pattern |
لا | (مطابقة الكل) | نمط رابط بتعبير نمطي لتصفية وقت تشغيل السكريبت |
requires-body |
لا | اكتشاف تلقائي | فرض استلام السكريبت لمحتوى الطلب/الاستجابة الكامل |
max-size |
لا | 131072 (128KB) | الحد الأقصى لحجم المحتوى بالبايت للسكريبتات التي تصل للمحتوى |
timeout |
لا | 5.0 ثانية | مهلة تنفيذ لكل سكريبت |
argument |
لا | — | نص وسيط مخصص متاح كـ $argument في JS |
debug |
لا | false | تخطي ذاكرة التخزين المؤقتة للترجمة للتصحيح |
cron-expression |
لا | — | تعبير جدولة cron (لنوع cron فقط) |
wake-system |
لا | false | إيقاظ النظام لتنفيذ سكريبتات cron (iOS فقط) |
enable |
لا | true | تفعيل أو تعطيل هذا السكريبت |
script-update-interval |
لا | 0 | ثواني بين تحديثات السكريبتات البعيدة (-1 = أبداً، 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) |
.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) |
$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 إلى الرابط المعطى بدلاً من الاستجابة الأصلية.
قيم إرجاع سكريبت 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)
$done({server: "8.8.8.8"}) // توجيه إلى خادم 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
$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: "aa:bb:cc:dd:ee:ff"}
$environment — معلومات وقت التشغيل
$environment.system // "iOS" أو "macOS"
$environment.appVersion // نص إصدار التطبيق
$utils — أدوات مساعدة
$utils.geoip("1.2.3.4") // رمز الدولة (مثال: "US")
$utils.ipasn("1.2.3.4") // رقم ASN (مثال: "13335")
$utils.ungzip(data) // فك ضغط بيانات gzip
$klne — واجهة التحكم بالبروكسي
التحكم في وقت تشغيل البروكسي من السكريبتات.
$klne.policyGroups // الحصول على جميع مجموعات السياسات
$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
$script — بيانات السكريبت الوصفية
$script.name // اسم السكريبت من الإعدادات
$script.type // نص نوع السكريبت
$script.startTime // طابع زمني epoch
globals لكل تنفيذ
يتم حقن المتغيرات التالية لكل تنفيذ سكريبت وهي خاصة بأنواع سكريبتات معينة.
$argument — وسيط السكريبت
القيمة النصية من معامل argument= في إعدادات السكريبت. متوفر في: http-request, http-response, http-request-before-send, rule, dns, cron.
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") // سجل تحذيري ببادئة [JS-ERROR]
setTimeout(fn, seconds) — مؤقت
جدولة دالة للتشغيل بعد تأخير.
setTimeout(function() {
console.log("Delayed execution")
}, 2.5) // 2.5 ثانية
$script(subScriptPath) — محمل السكريبتات الفرعية
تحميل وتقييم ملف JavaScript آخر.
$script("/path/to/helper.js")
$script("https://example.com/remote-script.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]
HourlyTask = type=cron, script-path=hourly.js, cron-expression=0 * * * *
صيغة cron المبسطة مدعومة (مثال: */30 * * * * لكل 30 دقيقة، قيم مفصولة بفواصل).
سكريبت الحدث
يتم تشغيله بواسطة أحداث النظام. حالياً يدعم حدث network-changed (ينطلق عند تغير شبكة WiFi أو الجوال).
[Script]
NetChange = type=event, script-path=network-changed.js
كائن $event متاح:
$event.name // "network-changed"
أمثلة عملية
Redirect Mobile Devices
سكريبت 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({})
}
Block Content in API Responses
سكريبت 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)})
Modify Request Body Before Sending
سكريبت 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)})
Custom Rule: Time-Based Routing
سكريبت 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 خارج ساعات العمل
}
Custom DNS for Internal Domains
سكريبت 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 العادي
}
Auto-Switch Policy on Network Change
سكريبت event يبدل إلى مجموعة سياسات محافظة عند استخدام الجوال:
[Script]
NetSwitch = type=event, script-path=network-switch.js
// network-switch.js
if (!$network.wifi.ssid) {
// على الجوال — استخدام مجموعة بيانات منخفضة
$klne.selectPolicy("MainGroup", "LowDataProxy")
console.log("Switched to cellular profile")
} else if ($network.wifi.ssid === "Office") {
$klne.selectPolicy("MainGroup", "DIRECT")
console.log("Switched to office profile")
}
Periodic Health Check
سكريبت 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()
Enrich API Responses with External Data
سكريبت 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()خلال المهلة، يعامل السكريبت كتمرير. - يتم استخدام مجموعة JSContext (حجم 3) للأداء؛ يتم إعادة استخدام السياقات عبر التنفيذات.
- ترجمة السكريبتات مخزنة مؤقتاً افتراضياً؛ استخدم
debug=trueلتجاوز التخزين المؤقت. - على iOS/tvOS، تخضع السكريبتات لحد ذاكرة 10MB؛ على macOS، 512MB.
- السكريبتات البعيدة (مسارات HTTP/HTTPS) تجلب عند البدء ويمكن إعادة جلبها اختيارياً كل
script-update-intervalثانية.
دمج سكريبتات الوحدات
يمكن أيضاً تعريف السكريبتات في ملفات Module (.sgmodule) تحت القسم [Script].