-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathauto-check-in.js
370 lines (352 loc) · 11 KB
/
auto-check-in.js
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/**
* @module auto_check_in
* @description The script was used to automatic check in.
* @param {string[]} [domains = []] - The site of domain will be check in.
* @param {string[]} [keep = []] - Value of keep.
* @param {string[]} [email = []] - Value of email.
* @param {string[]} [pwd = []] - Value of pwd.
*/
const {
readFileSync,
writeFileSync,
existsSync,
appendFileSync,
} = require("fs");
const { resolve, join } = require("path");
const { homedir } = require("os");
const variable_path = resolve(__dirname, "./variables.yaml");
const myDate = new Date();
const debug = false;
const homeDirectory = join(homedir(), ".config/clash");
// log file路径
const logFile = join(homeDirectory, "logs/cfw-autocheckin.log");
let newParse = true;
const maxLogLine = 20000;
// 检查日志行数,超过maxLogLine切半
const checkLog = function () {
if (!existsSync(logFile)) {
log(
"[warn]: doesn't find log file: cfw-autocheckin.log, Automatically create it."
);
}
const lines = readFileSync(logFile, "utf-8").toString().split("\n");
if (lines.length > maxLogLine) {
let start = Math.round(lines.length / 2);
//从有意义的日期开始切
while (!/-{2,}.*-{2,}/.test(lines[start]) && start < lines.length) {
start++;
}
//backup old file
writeFileSync(logFile + ".bak", lines.join("\n"), "utf-8");
//write new log
writeFileSync(logFile, lines.slice(start).join("\n"), "utf-8");
log(
`[info]: log line count is: ${lines.length} larger than ${maxLogLine}, cut it by half.`
);
} else if (debug) {
log(`[debug]: log line count is: ${lines.length}`);
}
};
const log = function (text) {
if (newParse) {
appendFileSync(
logFile,
`\n --------------${myDate.toLocaleString()}--------------\n`,
"utf-8"
);
newParse = false;
}
appendFileSync(logFile, text + "\n", "utf-8");
};
// 从日志获取历史流量,仅统计通过自动签到获取的流量
// 同时因为会有日志大小监控,日志过长会被切断,因此数据可能不准确,日志限制数在上面
const cal_data_used_fromlog = function (name) {
if (!existsSync(logFile)) {
log(
"[warn]: doesn't find log file: cfw-autocheckin.log, Automatically create it."
);
return 0;
}
let reg = new RegExp(
'"' + name + '".*?[你您]获得了\\s*(\\d+)\\s*MB流量',
"gi"
);
let matches = readFileSync(logFile, "utf-8").matchAll(reg);
let total = 0;
let count = 0;
for (const match of matches) {
total += parseInt(match[1]);
count++;
if (debug) {
log(
`[debug]: 迁移:match:${match[0]} num:${match[1]} start=${
match.index
} end=${match.index + match[0].length}.`
);
}
}
log(`[info]: calculate total data used from log: ${total}M`);
return [total, count];
};
let check_in = async (raw, { yaml, axios, notify }, variable) => {
try {
// iso 时区+8
var _time = new Date(+myDate + 8 * 3600 * 1000)
.toISOString()
.replace(/Z$/, "+08:00");
var today = _time.slice(0, 10);
var rawObj = yaml.parse(raw);
var check = false;
var sign = false;
var should_modify = false;
log(`[info]: start check in "${variable["name"]}".`);
//检查历史,是否有今天签到记录
if (variable["history"] && variable["history"].length > 0) {
if (variable["history"][0]["checkinDate"].slice(0, 10) === today) {
log(`[info]: "${variable["name"]}" has been already check in.`);
notify(
"Already check in",
`You has been already check in in "${variable["name"]}"`,
true
);
//跳过登陆和签到
check = true;
sign = true;
}
} else variable["history"] = [];
var domain = variable["domain"]
.replace(/^https?:\/\//, "")
.replace(/\/$/, "");
// check sign
if (!check && !sign) {
try {
log(`[info]: try check sign with "${variable["name"]}".`);
let resp = await axios.get(`https://${domain}/user`);
//数据量很大
if (debug) {
log(`[debug]: response of https://${domain}/user:`);
log(`${JSON.stringify(resp.data, null, 2)}`);
}
sign =
/(?<!登录)用户中心|节点列表|我的账号|退出登录|剩余流量|(?:复制((?!订阅).)*?)?订阅/.test(
resp.data
);
log(`[info]: signed?: ${sign}.`);
} catch (e) {
// 检查失败,跳过登陆和签到
check = true;
log(`[error]: check sign "${variable["name"]}" failed: ${e}.`);
notify(`check sign in "${variable["name"]}" failed`, e.message, true);
}
}
//try auto sign
if (!check && !sign) {
try {
log(`[info]: try sign in "${variable["name"]}".`);
let resp = await axios.post(`https://${domain}/auth/login`, {
email: variable["email"],
passwd: variable["pwd"],
remember_me: variable["keep"],
});
if (debug) {
log(`[debug]: response of https://${domain}/auth/login:`);
log(`${JSON.stringify(resp.data, null, 2)}`);
}
if (/登[录陆]成功/.test(resp.data.msg)) sign = true;
log(`[info]: signed?: ${sign}.`);
if (!sign)
notify(
`sign in failed`,
`sign in "${variable["name"]}" failed`,
true
);
} catch (e) {
log(`[error]: sign in "${variable["name"]}" failed: ${e}.`);
notify(`sign in "${variable["name"]}" failed`, e.message, true);
}
}
//try auto check in
if (!check && sign) {
try {
log(`[info]: try check in "${variable["name"]}".`);
let resp = await axios.post(`https://${domain}/user/checkin`);
if (debug) {
log(`[debug]: response of https://${domain}/user/checkin:`);
log(`${JSON.stringify(resp.data, null, 2)}`);
}
if (
resp.data.msg &&
!/[您你](?:似乎)?已经签到过了/.test(resp.data.msg)
) {
// total data used
let total = 0;
// checkin days
let days = 0;
let total_text = variable["total"];
if (!total_text || !/\d+M/.test(total_text)) {
//没有total属性,版本迁移,从日志中统计全部签到流量
[total, days] = cal_data_used_fromlog(variable["name"]);
} else {
total = parseInt(/\d+/.exec(total_text)[0]);
days = parseInt(/\d+/.exec(variable["days"])[0]);
}
total += parseInt(/\d+/.exec(resp.data.msg)[0]);
days++;
total_text = total.toString() + "MB";
if (total > 1024 * 1024) {
total_text +=
", i.e., " + (total / (1024 * 1024)).toFixed(4) + "TB";
} else if (total > 1024) {
total_text += ", i.e., " + (total / 1024).toFixed(2) + "GB";
}
variable["total"] = total_text;
variable["days"] = days;
// history
let nowData = {};
nowData["checkinDate"] = _time;
nowData["checkinMessage"] = resp.data.msg;
variable["history"].unshift(nowData);
should_modify = true;
log(`[info]: "${variable["name"]}" checkinDate: ${_time}.`);
log(
`[info]: "${variable["name"]}" checkinMessage: ${resp.data.msg}.`
);
notify(
`check in "${variable["name"]}" successful`,
resp.data.msg,
true
);
log(`[info]: "${variable["name"]}" check in completely.`);
} else {
log(`[info]: "${variable["name"]}" has been already check in.`);
notify(
`You have checked in "${variable["name"]}" today.`,
resp.data.msg,
true
);
// 前面检查过签到记录,说明没有今天的记录,说明漏记录一次签到,可能是手动签到的,数据补不回来了
}
} catch (e) {
log(`[error]: check in "${variable["name"]}" failed: ${e}.`);
notify(`check in "${variable["name"]}" failed`, e.message, true);
}
} else if (!sign) log(`[warning]: "${variable["name"]}" need to sign in`);
// 保留5天记录
while (variable["history"].length > 5) {
variable["history"].pop();
}
//不管成没成功,添加信息到配置
if (variable["history"].length > 0) {
if (!rawObj["proxies"]) rawObj["proxies"] = [];
rawObj["proxies"].push(
{
name: `⏰ [${variable["name"]}]签到时间:${variable["history"][0]["checkinDate"]}`,
server: "server",
type: "http",
port: 443,
},
{
name: `🎁 [${variable["name"]}]签到消息:${variable["history"][0]["checkinMessage"]}`,
server: "server",
type: "http",
port: 443,
}
);
if (!rawObj["proxy-groups"]) rawObj["proxy-groups"] = [];
if (
rawObj["proxy-groups"].length === 0 ||
rawObj["proxy-groups"][rawObj["proxy-groups"].length - 1]["name"] !=
"🤚 CHECK-INFO"
) {
rawObj["proxy-groups"].push({
name: "🤚 CHECK-INFO",
type: "select",
proxies: [],
});
}
rawObj["proxy-groups"][rawObj["proxy-groups"].length - 1]["proxies"].push(
rawObj["proxies"][rawObj["proxies"].length - 2]["name"],
rawObj["proxies"][rawObj["proxies"].length - 1]["name"]
);
if (debug) {
log(`[debug]: rawObj['proxies']:`);
log(`${JSON.stringify(rawObj["proxies"], null, 2)}`);
log(`[debug]: rawObj['proxy-groups']:`);
log(`${JSON.stringify(rawObj["proxy-groups"], null, 2)}`);
}
}
return [yaml.stringify(rawObj), variable, should_modify];
} catch (e) {
log(`[error]: "${variable["name"]}" something happened: ${e}.`);
notify(`"${variable["name"]}" something happened`, e.message, true);
throw e;
}
};
let auto_check_in = async (raw, { yaml, axios, console, notify }, { url }) => {
// check yaml
try {
console.log(`see log in ${logFile}.`);
var rawObj = yaml.parse(raw);
} catch (e) {
if (
e.message === "Implicit map keys need to be on a single line" &&
!new RegExp("^((?!www.example.com).)*$").test(url)
) {
log("[warning]: raw is not yaml.");
rawObj = { proxies: [], "proxy-groups": [], rules: [] };
} else {
log(`[error]: check yaml fail: ${e}.`);
throw e;
}
}
// check log length
checkLog();
//check variables.yml
if (!existsSync(variable_path)) {
log('[warning]: no found "./variables.yaml".');
throw 'no found "./variables.yaml"';
}
var _variables = yaml.parse(readFileSync(variable_path, "utf-8"));
if (!_variables["auto_check_in"]) {
log("[warning]: no found auto_check_in variables.");
notify(`auto-check-in failed`, "no found auto_check_in variables", true);
return yaml.stringify(rawObj);
} else var auto_check_in = _variables["auto_check_in"];
// skip reserve auto check in
let reserve = auto_check_in.filter((item) => item["reserve"]);
auto_check_in = auto_check_in.filter((item) => !item["reserve"]);
// try check in
try {
if (debug) {
log("[debug]: auto_check_in variables:");
log(`${JSON.stringify(auto_check_in, null, 2)}`);
}
raw = yaml.stringify(rawObj);
let check_list = [...Array(auto_check_in.length)].map((_) => false);
for (let i = 0; i < auto_check_in.length; i++) {
[raw, auto_check_in[i], check_list[i]] = await check_in(
raw,
{ yaml, axios, notify },
auto_check_in[i]
);
}
if (check_list.some((v) => v)) {
if (debug) log("[debug]: have modified variables.");
delete _variables["auto_check_in"];
writeFileSync(
variable_path,
yaml.stringify(
{ ..._variables, auto_check_in: auto_check_in.concat(reserve) },
null,
2
),
"utf-8"
);
}
return raw;
} catch (e) {
log(`[error]: ${e}.`);
throw e;
}
};
module.exports.parse = auto_check_in;