oicq-icalingua-plus-plus 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +73 -0
- package/bin/oicq +2 -0
- package/http-api/README.md +187 -0
- package/http-api/api.js +217 -0
- package/http-api/config.sample.js +44 -0
- package/http-api/core.js +480 -0
- package/http-api/cq-notice.js +39 -0
- package/http-api/filter.js +128 -0
- package/http-api/index.js +39 -0
- package/index.d.ts +1214 -0
- package/index.js +34 -0
- package/lib/algo/jce/index.js +63 -0
- package/lib/algo/jce/jce.js +341 -0
- package/lib/algo/pb.js +159 -0
- package/lib/algo/protobuf.min.js +8 -0
- package/lib/algo/tea.js +121 -0
- package/lib/client-ext.js +312 -0
- package/lib/client-net.js +147 -0
- package/lib/client.js +594 -0
- package/lib/common.js +252 -0
- package/lib/core/friendlist.js +415 -0
- package/lib/core/nessy.js +148 -0
- package/lib/core/ref.d.ts +1 -0
- package/lib/core/sysmsg.js +388 -0
- package/lib/core/troop.js +586 -0
- package/lib/device.js +181 -0
- package/lib/exception.js +57 -0
- package/lib/message/builder.js +1093 -0
- package/lib/message/chat.js +276 -0
- package/lib/message/face.js +293 -0
- package/lib/message/file.js +425 -0
- package/lib/message/history.js +73 -0
- package/lib/message/image.js +533 -0
- package/lib/message/multi.js +242 -0
- package/lib/message/music.js +172 -0
- package/lib/message/parser.js +774 -0
- package/lib/message/ptt.js +389 -0
- package/lib/oicq.js +427 -0
- package/lib/onlinepush.js +616 -0
- package/lib/ref.d.ts +177 -0
- package/lib/service.js +204 -0
- package/lib/wtlogin/ecdh.js +15 -0
- package/lib/wtlogin/login-password.js +215 -0
- package/lib/wtlogin/login-qrcode.js +249 -0
- package/lib/wtlogin/tlv.js +331 -0
- package/lib/wtlogin/writer.js +88 -0
- package/lib/wtlogin/wt.js +422 -0
- package/package.json +50 -0
- package/util.js +115 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api
|
|
3
|
+
*/
|
|
4
|
+
"use strict";
|
|
5
|
+
const version = require("../package.json");
|
|
6
|
+
const { EventEmitter } = require("events");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const { exec } = require("child_process");
|
|
11
|
+
const { randomBytes } = require("crypto");
|
|
12
|
+
const log4js = require("log4js");
|
|
13
|
+
const Network = require("./client-net");
|
|
14
|
+
const { getApkInfo, getDeviceInfo } = require("./device");
|
|
15
|
+
const { timestamp, md5, BUF0 } = require("./common");
|
|
16
|
+
const { onlineListener, offlineListener, packetListener, networkErrorListener } = require("./oicq");
|
|
17
|
+
const frdlst = require("./core/friendlist");
|
|
18
|
+
const sysmsg = require("./core/sysmsg");
|
|
19
|
+
const troop = require("./core/troop");
|
|
20
|
+
const nessy = require("./core/nessy");
|
|
21
|
+
const { WtLogin } = require("./wtlogin/wt");
|
|
22
|
+
const chat = require("./message/chat");
|
|
23
|
+
const multi = require("./message/multi");
|
|
24
|
+
const { Gfs } = require("./message/file");
|
|
25
|
+
const { getErrorMessage, TimeoutError } = require("./exception");
|
|
26
|
+
|
|
27
|
+
function buildApiRet(retcode, data = null, error = null) {
|
|
28
|
+
data = data ? data : null;
|
|
29
|
+
error = error ? error : null;
|
|
30
|
+
const status = retcode ? (retcode === 1 ? "async" : "failed") : "ok";
|
|
31
|
+
return {
|
|
32
|
+
retcode, data, status, error
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const platforms = {
|
|
37
|
+
1: "Android",
|
|
38
|
+
2: "aPad",
|
|
39
|
+
3: "Watch",
|
|
40
|
+
4: "MacOS",
|
|
41
|
+
5: "iPad",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** 客户端已上线状态 */
|
|
45
|
+
const STATUS_ONLINE = Symbol("ONLINE");
|
|
46
|
+
/** socket未连接状态 */
|
|
47
|
+
const STATUS_OFFLINE = Symbol("OFFLINE");
|
|
48
|
+
/** socket已连接,但客户端未上线状态 */
|
|
49
|
+
const STATUS_PENDING = Symbol("PENDING");
|
|
50
|
+
|
|
51
|
+
class Client extends EventEmitter {
|
|
52
|
+
|
|
53
|
+
status = STATUS_OFFLINE;
|
|
54
|
+
online_status = 0;
|
|
55
|
+
nickname = "";
|
|
56
|
+
age = 0;
|
|
57
|
+
sex = "unknown";
|
|
58
|
+
|
|
59
|
+
fl = new Map; //friendList
|
|
60
|
+
sl = new Map; //strangerList
|
|
61
|
+
gl = new Map; //groupList
|
|
62
|
+
gml = new Map; //groupMemberList
|
|
63
|
+
|
|
64
|
+
seq_id = 0;
|
|
65
|
+
handlers = new Map; //存放响应包的回调
|
|
66
|
+
seq_cache = new Map; //一分钟内的缓存
|
|
67
|
+
|
|
68
|
+
sig = {
|
|
69
|
+
srm_token: BUF0,
|
|
70
|
+
tgt: BUF0,
|
|
71
|
+
tgt_key: BUF0,
|
|
72
|
+
st_key: BUF0,
|
|
73
|
+
st_web_sig: BUF0,
|
|
74
|
+
skey: BUF0,
|
|
75
|
+
d2: BUF0,
|
|
76
|
+
d2key: BUF0,
|
|
77
|
+
sig_key: BUF0,
|
|
78
|
+
ticket_key: BUF0,
|
|
79
|
+
device_token: BUF0,
|
|
80
|
+
emp_time: timestamp(),
|
|
81
|
+
};
|
|
82
|
+
_cookies = { };
|
|
83
|
+
|
|
84
|
+
sync_finished = false;
|
|
85
|
+
sync_cookie;
|
|
86
|
+
const1 = randomBytes(4).readUInt32BE();
|
|
87
|
+
const2 = randomBytes(4).readUInt32BE();
|
|
88
|
+
const3 = randomBytes(1)[0];
|
|
89
|
+
|
|
90
|
+
_stat = {
|
|
91
|
+
start_time: timestamp(),
|
|
92
|
+
lost_times: 0,
|
|
93
|
+
recv_pkt_cnt: 0,
|
|
94
|
+
sent_pkt_cnt: 0,
|
|
95
|
+
lost_pkt_cnt: 0,
|
|
96
|
+
recv_msg_cnt: 0,
|
|
97
|
+
sent_msg_cnt: 0,
|
|
98
|
+
msg_cnt_per_min: 0,
|
|
99
|
+
remote_ip: "",
|
|
100
|
+
remote_port: 0,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
blacklist = new Set;
|
|
104
|
+
|
|
105
|
+
storage = {};
|
|
106
|
+
|
|
107
|
+
_socket = new Network(this);
|
|
108
|
+
_wt = new WtLogin(this);
|
|
109
|
+
|
|
110
|
+
get [Symbol.toStringTag]() {
|
|
111
|
+
return "OicqClient";
|
|
112
|
+
}
|
|
113
|
+
get uin() {
|
|
114
|
+
return this._uin;
|
|
115
|
+
}
|
|
116
|
+
get stat() {
|
|
117
|
+
this._stat.msg_cnt_per_min = this._calcMsgCnt();
|
|
118
|
+
this._stat.remote_ip = this._socket.remoteAddress;
|
|
119
|
+
this._stat.remote_port = this._socket.remotePort;
|
|
120
|
+
return this._stat;
|
|
121
|
+
}
|
|
122
|
+
get bkn() {
|
|
123
|
+
let bkn = 5381;
|
|
124
|
+
for (let v of this.sig.skey)
|
|
125
|
+
bkn = bkn + (bkn << 5) + v;
|
|
126
|
+
bkn &= 2147483647;
|
|
127
|
+
return bkn;
|
|
128
|
+
}
|
|
129
|
+
cookies = new Proxy(this._cookies, {
|
|
130
|
+
get: (obj, domain) => {
|
|
131
|
+
const cookie = `uin=o${this.uin}; skey=${this.sig.skey};`;
|
|
132
|
+
if (!obj[domain])
|
|
133
|
+
return cookie;
|
|
134
|
+
return `${cookie} p_uin=o${this.uin}; p_skey=${obj[domain]};`;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @param {number} uin
|
|
140
|
+
* @param {import("./ref").ConfBot} config
|
|
141
|
+
*/
|
|
142
|
+
constructor(uin, config) {
|
|
143
|
+
super();
|
|
144
|
+
this._uin = uin;
|
|
145
|
+
|
|
146
|
+
config = {
|
|
147
|
+
platform: 1,
|
|
148
|
+
log_level: "info",
|
|
149
|
+
kickoff: false,
|
|
150
|
+
brief: false,
|
|
151
|
+
ignore_self: true,
|
|
152
|
+
resend: true,
|
|
153
|
+
reconn_interval: 5,
|
|
154
|
+
internal_cache_life: 3600,
|
|
155
|
+
auto_server: true,
|
|
156
|
+
data_dir: path.join(require.main ? require.main.path : process.cwd(), "data"),
|
|
157
|
+
ffmpeg_path: "ffmpeg",
|
|
158
|
+
ffprobe_path: "ffprobe",
|
|
159
|
+
...config
|
|
160
|
+
};
|
|
161
|
+
this.config = config;
|
|
162
|
+
this.dir = createDataDir(config.data_dir, uin);
|
|
163
|
+
this.logger = log4js.getLogger(`[${platforms[config.platform]||"Android"}:${uin}]`);
|
|
164
|
+
this.logger.level = config.log_level;
|
|
165
|
+
|
|
166
|
+
this.logger.mark("----------");
|
|
167
|
+
this.logger.mark(`Package Version: oicq@${version.version} (Released on ${version.upday})`);
|
|
168
|
+
this.logger.mark("View Changelogs:https://github.com/takayama-lily/oicq/releases");
|
|
169
|
+
this.logger.mark("----------");
|
|
170
|
+
|
|
171
|
+
const filepath = path.join(this.dir, `device-${uin}.json`);
|
|
172
|
+
if (!fs.existsSync(filepath))
|
|
173
|
+
this.logger.mark("创建了新的设备文件:" + filepath);
|
|
174
|
+
this.device = getDeviceInfo(filepath, this.uin);
|
|
175
|
+
this.apk = getApkInfo(config.platform);
|
|
176
|
+
|
|
177
|
+
this.on("internal.offline", offlineListener);
|
|
178
|
+
this.on("internal.login", onlineListener);
|
|
179
|
+
this.on("internal.packet", packetListener);
|
|
180
|
+
this.on("internal.network", networkErrorListener);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 连接服务器并执行回调
|
|
185
|
+
* 如果已经连接则立刻执行回调
|
|
186
|
+
* @private
|
|
187
|
+
* @param {Function} cb
|
|
188
|
+
*/
|
|
189
|
+
_connect(cb) {
|
|
190
|
+
if (this.status !== STATUS_OFFLINE)
|
|
191
|
+
return cb();
|
|
192
|
+
this._socket.join(() => {
|
|
193
|
+
this.status = STATUS_PENDING;
|
|
194
|
+
cb();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 调用api前的一层封装
|
|
200
|
+
* @private
|
|
201
|
+
* @param {Function} fn
|
|
202
|
+
* @param {Array} params
|
|
203
|
+
*/
|
|
204
|
+
async _useProtocol(fn, params) {
|
|
205
|
+
if (!this.isOnline() || !this.sync_finished) {
|
|
206
|
+
this.logger.error("Invoke failed, because -> client is not online")
|
|
207
|
+
return buildApiRet(104, null, { code: -1, message: "client not online" });
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const rsp = await fn.apply(this, params);
|
|
211
|
+
if (!rsp)
|
|
212
|
+
return buildApiRet(1);
|
|
213
|
+
if (rsp.result !== 0)
|
|
214
|
+
return buildApiRet(102, null,
|
|
215
|
+
{
|
|
216
|
+
code: rsp.result,
|
|
217
|
+
message: rsp.emsg ? rsp.emsg : getErrorMessage(fn, rsp.result)
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
else
|
|
221
|
+
return buildApiRet(0, rsp.data);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
this.logger.error("Invoke failed, because -> " + e.message);
|
|
224
|
+
if (e instanceof TimeoutError)
|
|
225
|
+
return buildApiRet(103, null, { code: -1, message: "packet timeout" });
|
|
226
|
+
this.logger.debug(e);
|
|
227
|
+
return buildApiRet(100, null, { code: -1, message: e.message });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 计算每分钟消息数
|
|
233
|
+
* @private
|
|
234
|
+
*/
|
|
235
|
+
_calcMsgCnt() {
|
|
236
|
+
let cnt = 0;
|
|
237
|
+
for (let [time, set] of this.seq_cache) {
|
|
238
|
+
if (timestamp() - time >= 60)
|
|
239
|
+
this.seq_cache.delete(time);
|
|
240
|
+
else
|
|
241
|
+
cnt += set.size;
|
|
242
|
+
}
|
|
243
|
+
return cnt;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
///////////////////////////////////////////////////
|
|
247
|
+
|
|
248
|
+
login(password) {
|
|
249
|
+
if (this.isOnline())
|
|
250
|
+
return;
|
|
251
|
+
if (password) {
|
|
252
|
+
let password_md5;
|
|
253
|
+
if (typeof password === "string")
|
|
254
|
+
password_md5 = Buffer.from(password, "hex");
|
|
255
|
+
else if (password instanceof Uint8Array)
|
|
256
|
+
password_md5 = Buffer.from(password);
|
|
257
|
+
if (password_md5 && password_md5.length === 16)
|
|
258
|
+
this.password_md5 = password_md5;
|
|
259
|
+
else
|
|
260
|
+
this.password_md5 = md5(String(password));
|
|
261
|
+
}
|
|
262
|
+
this._connect(() => {
|
|
263
|
+
this._wt.passwordLogin();
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
captchaLogin() { }
|
|
268
|
+
|
|
269
|
+
sliderLogin(ticket) {
|
|
270
|
+
this._connect(() => {
|
|
271
|
+
this._wt.sliderLogin(ticket);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
sendSMSCode() {
|
|
276
|
+
this._connect(() => {
|
|
277
|
+
this._wt.sendSMSCode();
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
submitSMSCode(code) {
|
|
282
|
+
this._connect(() => {
|
|
283
|
+
this._wt.submitSMSCode(code);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
terminate() {
|
|
288
|
+
if (this.status === STATUS_ONLINE)
|
|
289
|
+
this.status = STATUS_PENDING;
|
|
290
|
+
this._socket.destroy();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async logout() {
|
|
294
|
+
if (this.isOnline()) {
|
|
295
|
+
try {
|
|
296
|
+
await this._wt.register(true);
|
|
297
|
+
} catch { }
|
|
298
|
+
}
|
|
299
|
+
this.terminate();
|
|
300
|
+
await new Promise(resolve => this.once("internal.logout", resolve));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
isOnline() {
|
|
304
|
+
return this.status === STATUS_ONLINE;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
///////////////////////////////////////////////////
|
|
308
|
+
|
|
309
|
+
setOnlineStatus(status) {
|
|
310
|
+
return this._useProtocol(nessy.setStatus, arguments);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
getFriendList() {
|
|
314
|
+
return buildApiRet(0, this.fl);
|
|
315
|
+
}
|
|
316
|
+
getStrangerList() {
|
|
317
|
+
return buildApiRet(0, this.sl);
|
|
318
|
+
}
|
|
319
|
+
getGroupList() {
|
|
320
|
+
return buildApiRet(0, this.gl);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async reloadFriendList() {
|
|
324
|
+
const ret = await this._useProtocol(frdlst.initFL, arguments);
|
|
325
|
+
this.sync_finished = true;
|
|
326
|
+
this.pbGetMsg();
|
|
327
|
+
return ret;
|
|
328
|
+
}
|
|
329
|
+
async reloadGroupList() {
|
|
330
|
+
const ret = await this._useProtocol(frdlst.initGL, arguments);
|
|
331
|
+
this.sync_finished = true;
|
|
332
|
+
this.pbGetMsg();
|
|
333
|
+
return ret;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
getGroupMemberList(group_id, no_cache = false) {
|
|
337
|
+
return this._useProtocol(frdlst.getGML, arguments);
|
|
338
|
+
}
|
|
339
|
+
getStrangerInfo(user_id, no_cache = false) {
|
|
340
|
+
return this._useProtocol(frdlst.getSI, arguments);
|
|
341
|
+
}
|
|
342
|
+
getGroupInfo(group_id, no_cache = false) {
|
|
343
|
+
return this._useProtocol(frdlst.getGI, arguments);
|
|
344
|
+
}
|
|
345
|
+
getGroupMemberInfo(group_id, user_id, no_cache = false) {
|
|
346
|
+
return this._useProtocol(frdlst.getGMI, arguments);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
///////////////////////////////////////////////////
|
|
350
|
+
|
|
351
|
+
sendPrivateMsg(user_id, message = "", auto_escape = false) {
|
|
352
|
+
return this._useProtocol(chat.sendMsg, [user_id, message, auto_escape, 0]);
|
|
353
|
+
}
|
|
354
|
+
sendGroupMsg(group_id, message = "", auto_escape = false) {
|
|
355
|
+
return this._useProtocol(chat.sendMsg, [group_id, message, auto_escape, 1]);
|
|
356
|
+
}
|
|
357
|
+
sendDiscussMsg(discuss_id, message = "", auto_escape = false) {
|
|
358
|
+
return this._useProtocol(chat.sendMsg, [discuss_id, message, auto_escape, 2]);
|
|
359
|
+
}
|
|
360
|
+
sendTempMsg(group_id, user_id, message = "", auto_escape = false) {
|
|
361
|
+
return this._useProtocol(chat.sendTempMsg, arguments);
|
|
362
|
+
}
|
|
363
|
+
deleteMsg(message_id) {
|
|
364
|
+
return this._useProtocol(chat.recallMsg, arguments);
|
|
365
|
+
}
|
|
366
|
+
reportReaded(message_id) {
|
|
367
|
+
return this._useProtocol(chat.reportReaded, arguments);
|
|
368
|
+
}
|
|
369
|
+
getMsg(message_id) {
|
|
370
|
+
return this._useProtocol(chat.getOneMsg, arguments);
|
|
371
|
+
}
|
|
372
|
+
getChatHistory(message_id, count = 10) {
|
|
373
|
+
return this._useProtocol(chat.getMsgs, arguments);
|
|
374
|
+
}
|
|
375
|
+
getForwardMsg(id) {
|
|
376
|
+
return this._useProtocol(chat.getForwardMsg, arguments);
|
|
377
|
+
}
|
|
378
|
+
makeForwardMsg(fakes, dm = false) {
|
|
379
|
+
return this._useProtocol(multi.makeForwardMsg, arguments);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
///////////////////////////////////////////////////
|
|
383
|
+
|
|
384
|
+
setGroupAnonymousBan(group_id, flag, duration = 1800) {
|
|
385
|
+
return this._useProtocol(troop.muteAnonymous, arguments);
|
|
386
|
+
}
|
|
387
|
+
setGroupAnonymous(group_id, enable = true) {
|
|
388
|
+
return this._useProtocol(troop.setAnonymous, arguments);
|
|
389
|
+
}
|
|
390
|
+
setGroupWholeBan(group_id, enable = true) {
|
|
391
|
+
return this.setGroupSetting(group_id, "shutupTime", enable ? 0xffffffff : 0);
|
|
392
|
+
}
|
|
393
|
+
setGroupName(group_id, group_name) {
|
|
394
|
+
return this.setGroupSetting(group_id, "ingGroupName", String(group_name));
|
|
395
|
+
}
|
|
396
|
+
sendGroupNotice(group_id, content) {
|
|
397
|
+
return this.setGroupSetting(group_id, "ingGroupMemo", String(content));
|
|
398
|
+
}
|
|
399
|
+
setGroupSetting(group_id, k, v) {
|
|
400
|
+
return this._useProtocol(troop.setting, arguments);
|
|
401
|
+
}
|
|
402
|
+
setGroupAdmin(group_id, user_id, enable = true) {
|
|
403
|
+
return this._useProtocol(troop.setAdmin, arguments);
|
|
404
|
+
}
|
|
405
|
+
setGroupSpecialTitle(group_id, user_id, special_title = "", duration = -1) {
|
|
406
|
+
return this._useProtocol(troop.setTitle, arguments);
|
|
407
|
+
}
|
|
408
|
+
setGroupCard(group_id, user_id, card = "") {
|
|
409
|
+
return this._useProtocol(troop.setCard, arguments);
|
|
410
|
+
}
|
|
411
|
+
setGroupKick(group_id, user_id, reject_add_request = false) {
|
|
412
|
+
return this._useProtocol(troop.kickMember, arguments);
|
|
413
|
+
}
|
|
414
|
+
setGroupBan(group_id, user_id, duration = 1800) {
|
|
415
|
+
return this._useProtocol(troop.muteMember, arguments);
|
|
416
|
+
}
|
|
417
|
+
setGroupLeave(group_id, is_dismiss = false) {
|
|
418
|
+
return this._useProtocol(troop.quitGroup, arguments);
|
|
419
|
+
}
|
|
420
|
+
sendGroupPoke(group_id, user_id) {
|
|
421
|
+
return this._useProtocol(troop.pokeMember, arguments);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
///////////////////////////////////////////////////
|
|
425
|
+
|
|
426
|
+
setFriendAddRequest(flag, approve = true, remark = "", block = false) {
|
|
427
|
+
return this._useProtocol(sysmsg.friendAction, arguments);
|
|
428
|
+
}
|
|
429
|
+
setGroupAddRequest(flag, approve = true, reason = "", block = false) {
|
|
430
|
+
return this._useProtocol(sysmsg.groupAction, arguments);
|
|
431
|
+
}
|
|
432
|
+
getSystemMsg() {
|
|
433
|
+
return this._useProtocol(sysmsg.getSysMsg, arguments);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
addGroup(group_id, comment = "") {
|
|
437
|
+
return this._useProtocol(troop.addGroup, arguments);
|
|
438
|
+
}
|
|
439
|
+
addFriend(group_id, user_id, comment = "") {
|
|
440
|
+
return this._useProtocol(troop.addFriend, arguments);
|
|
441
|
+
}
|
|
442
|
+
deleteFriend(user_id, block = true) {
|
|
443
|
+
return this._useProtocol(troop.delFriend, arguments);
|
|
444
|
+
}
|
|
445
|
+
inviteFriend(group_id, user_id) {
|
|
446
|
+
return this._useProtocol(troop.inviteFriend, arguments);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
sendLike(user_id, times = 1) {
|
|
450
|
+
return this._useProtocol(nessy.sendLike, arguments);
|
|
451
|
+
}
|
|
452
|
+
setNickname(nickname) {
|
|
453
|
+
return this._useProtocol(troop.setProfile, [0x14E22, String(nickname)]);
|
|
454
|
+
}
|
|
455
|
+
setDescription(description = "") {
|
|
456
|
+
return this._useProtocol(troop.setProfile, [0x14E33, String(description)]);
|
|
457
|
+
}
|
|
458
|
+
setGender(gender) {
|
|
459
|
+
gender = parseInt(gender);
|
|
460
|
+
if (![0, 1, 2].includes(gender))
|
|
461
|
+
return buildApiRet(100);
|
|
462
|
+
return this._useProtocol(troop.setProfile, [0x14E29, Buffer.from([gender])]);
|
|
463
|
+
}
|
|
464
|
+
async setBirthday(birthday) {
|
|
465
|
+
try {
|
|
466
|
+
birthday = String(birthday).replace(/[^\d]/g, "");
|
|
467
|
+
const buf = Buffer.alloc(4);
|
|
468
|
+
buf.writeUInt16BE(parseInt(birthday.substr(0, 4)));
|
|
469
|
+
buf.writeUInt8(parseInt(birthday.substr(4, 2)), 2);
|
|
470
|
+
buf.writeUInt8(parseInt(birthday.substr(6, 2)), 3);
|
|
471
|
+
return this._useProtocol(troop.setProfile, [0x16593, buf]);
|
|
472
|
+
} catch (e) {
|
|
473
|
+
return buildApiRet(100);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
setSignature(signature = "") {
|
|
477
|
+
return this._useProtocol(troop.setSign, arguments);
|
|
478
|
+
}
|
|
479
|
+
setPortrait(file) {
|
|
480
|
+
return this._useProtocol(troop.setPortrait, arguments);
|
|
481
|
+
}
|
|
482
|
+
setGroupPortrait(group_id, file) {
|
|
483
|
+
return this._useProtocol(troop.setGroupPortrait, arguments);
|
|
484
|
+
}
|
|
485
|
+
getLevelInfo(user_id) {
|
|
486
|
+
return this._useProtocol(nessy.getLevelInfo, arguments);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
getRoamingStamp(no_cache = false) {
|
|
490
|
+
return this._useProtocol(nessy.getRoamingStamp, arguments);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
getGroupNotice(group_id) {
|
|
494
|
+
return this._useProtocol(troop.getGroupNotice, arguments);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
preloadImages(files) {
|
|
498
|
+
return this._useProtocol(chat.preloadImages, arguments);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
///////////////////////////////////////////////////
|
|
502
|
+
|
|
503
|
+
async getCookies(domain) {
|
|
504
|
+
const cookies = this.cookies[domain];
|
|
505
|
+
if (!cookies)
|
|
506
|
+
return buildApiRet(100, null, { code: -1, message: "unknown domain" });
|
|
507
|
+
return buildApiRet(0, { cookies });
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async getCsrfToken() {
|
|
511
|
+
return buildApiRet(0, { token: this.bkn });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* @param {String} type "image" or "record" or undefined
|
|
516
|
+
*/
|
|
517
|
+
async cleanCache(type = "") {
|
|
518
|
+
let file, cmd;
|
|
519
|
+
switch (type) {
|
|
520
|
+
case "image":
|
|
521
|
+
case "record":
|
|
522
|
+
file = path.join(this.dir, "..", type, "*");
|
|
523
|
+
cmd = os.platform().includes("win") ? "del /q " : "rm -f ";
|
|
524
|
+
exec(cmd + '"' + file + '"', (err, stdout, stderr) => {
|
|
525
|
+
if (err)
|
|
526
|
+
return this.logger.error(err);
|
|
527
|
+
if (stderr)
|
|
528
|
+
return this.logger.error(stderr);
|
|
529
|
+
this.logger.info(type + " cache clear");
|
|
530
|
+
});
|
|
531
|
+
break;
|
|
532
|
+
case "":
|
|
533
|
+
this.cleanCache("image");
|
|
534
|
+
this.cleanCache("record");
|
|
535
|
+
break;
|
|
536
|
+
default:
|
|
537
|
+
return buildApiRet(100, null, { code: -1, message: "unknown type (image, record, or undefined)" });
|
|
538
|
+
}
|
|
539
|
+
return buildApiRet(1);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
canSendImage() {
|
|
543
|
+
return buildApiRet(0, { yes: true });
|
|
544
|
+
}
|
|
545
|
+
canSendRecord() {
|
|
546
|
+
return buildApiRet(0, { yes: true });
|
|
547
|
+
}
|
|
548
|
+
getVersionInfo() {
|
|
549
|
+
return buildApiRet(0, version);
|
|
550
|
+
}
|
|
551
|
+
getStatus() {
|
|
552
|
+
return buildApiRet(0, {
|
|
553
|
+
online: this.isOnline(),
|
|
554
|
+
status: this.online_status,
|
|
555
|
+
remote_ip: this._socket.remoteAddress,
|
|
556
|
+
remote_port: this._socket.remotePort,
|
|
557
|
+
msg_cnt_per_min: this._calcMsgCnt(),
|
|
558
|
+
statistics: this.stat,
|
|
559
|
+
config: this.config
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
getLoginInfo() {
|
|
563
|
+
return buildApiRet(0, {
|
|
564
|
+
user_id: this.uin,
|
|
565
|
+
nickname: this.nickname,
|
|
566
|
+
age: this.age, sex: this.sex
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
acquireGfs(group_id) {
|
|
570
|
+
return new Gfs(this, group_id);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function createDataDir(dir, uin) {
|
|
575
|
+
if (!fs.existsSync(dir))
|
|
576
|
+
fs.mkdirSync(dir, { mode: 0o755, recursive: true });
|
|
577
|
+
const img_path = path.join(dir, "image");
|
|
578
|
+
const ptt_path = path.join(dir, "record");
|
|
579
|
+
const uin_path = path.join(dir, String(uin));
|
|
580
|
+
if (!fs.existsSync(img_path))
|
|
581
|
+
fs.mkdirSync(img_path);
|
|
582
|
+
if (!fs.existsSync(ptt_path))
|
|
583
|
+
fs.mkdirSync(ptt_path);
|
|
584
|
+
if (!fs.existsSync(uin_path))
|
|
585
|
+
fs.mkdirSync(uin_path, { mode: 0o755 });
|
|
586
|
+
return uin_path;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
module.exports = {
|
|
590
|
+
Client,
|
|
591
|
+
STATUS_ONLINE, STATUS_OFFLINE, STATUS_PENDING
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
require("./client-ext");
|