-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain-extra.js
More file actions
157 lines (131 loc) · 4.28 KB
/
Copy pathmain-extra.js
File metadata and controls
157 lines (131 loc) · 4.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// This script reads sing-box-subscribe/config.json and:
// 1) Ensures any outbound object with tag === "my" or "au" has detour: "all".
// 2) Removes entries "au" or "my" from any selector outbound whose tag === "all".
const fs = require("fs");
const path = require("path");
const CONFIG_PATH = path.join(__dirname, "sing-box-subscribe", "config.json");
function loadConfig(filePath) {
const raw = fs.readFileSync(filePath, "utf8");
return JSON.parse(raw);
}
function saveConfig(filePath, data) {
const formatted = JSON.stringify(data, null, 2);
fs.writeFileSync(filePath, formatted + "\n", "utf8");
}
function ensureDetourForMyAu(config) {
let updated = 0;
const visit = (node) => {
if (!node || typeof node !== "object") return;
// If this node appears to be an outbound object with a tag
if (typeof node.tag === "string" && (node.tag === "my" || node.tag === "au")) {
if (node.detour !== "all") {
node.detour = "all";
updated += 1;
}
}
// Recurse into object properties to catch nested structures.
for (const key of Object.keys(node)) {
const value = node[key];
if (value && typeof value === "object") {
if (Array.isArray(value)) {
for (const item of value) visit(item);
} else {
visit(value);
}
}
}
};
visit(config);
return updated;
}
function removeAuMyFromAllSelector(config) {
let removed = 0;
const visit = (node) => {
if (!node || typeof node !== "object") return;
// Match an outbound selector with tag === "all"
if (node.tag === "all" && Array.isArray(node.outbounds)) {
const before = node.outbounds.length;
node.outbounds = node.outbounds.filter((v) => {
// Only filter string entries exactly matching "au" or "my"
return !(typeof v === "string" && (v === "au" || v === "my"));
});
removed += before - node.outbounds.length;
}
// Recurse into object properties to catch nested structures.
for (const key of Object.keys(node)) {
const value = node[key];
if (value && typeof value === "object") visit(value);
}
};
visit(config);
return removed;
}
async function updateGist(gistId, options) {
const { files, description, token } = options;
const formattedFiles = {};
for (const [filename, content] of Object.entries(files)) {
formattedFiles[filename] = {
content: typeof content === "string" ? content : JSON.stringify(content, null, 2),
};
}
const payload = {
description,
files: formattedFiles,
public: false,
};
const apiUrl = "https://api.github.com/gists";
try {
const response = await fetch(`${apiUrl}/${gistId}`, {
method: "PATCH",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/vnd.github.v3+json",
"Content-Type": "application/json",
"X-GitHub-Api-Version": "2022-11-28",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
let msg = `${response.status}`;
try {
const err = await response.json();
if (err && err.message) msg += ` - ${err.message}`;
} catch (_) {}
throw new Error(`GitHub API error: ${msg}`);
}
return await response.json();
} catch (error) {
throw new Error(`Failed to update gist: ${error.message}`);
}
}
function main() {
const config = loadConfig(CONFIG_PATH);
const detourUpdates = ensureDetourForMyAu(config);
const removed = removeAuMyFromAllSelector(config);
if (detourUpdates > 0 || removed > 0) {
saveConfig(CONFIG_PATH, config);
}
console.log(
`Detour updates: ${detourUpdates}; removed from 'all': ${removed}.`
);
// Always upload the current config.json to Gist as "singbox".
const gistId = process.env.GIST_ID;
const token = process.env.GIST_TOKEN;
if (!gistId || !token) {
console.error("GIST_ID or GIST_TOKEN not set; skipping Gist upload.");
return;
}
try {
const raw = fs.readFileSync(CONFIG_PATH, "utf8");
updateGist(gistId, {
files: { singbox: raw },
description: "",
token,
})
.then(() => console.log("Gist updated with singbox config"))
.catch((err) => console.error("Error updating gist:", err.message));
} catch (e) {
console.error("Failed to read config for upload:", e.message);
}
}
main();