Skip to content
This repository was archived by the owner on Nov 2, 2023. It is now read-only.

Commit 1227806

Browse files
Merge pull request #34 from takayama-lily/dev
-
2 parents 0f1da36 + 6a6c404 commit 1227806

File tree

10 files changed

+237
-55
lines changed

10 files changed

+237
-55
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323

2424
```js
2525
const oicq = require("oicq");
26-
const uin = 123456789, config = {};
26+
const uin = 123456789;
2727
const password_md5 = "202cb962ac59075b964b07152d234b70";
28-
const bot = oicq.createClient(uin, config);
28+
const bot = oicq.createClient(uin);
2929
bot.login(password_md5);
3030
```
3131

client.js

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class Client extends net.Socket {
7474
* @event system.offline.network 拔线
7575
* @event system.offline.frozen 账号冻结
7676
* @event system.offline.kickoff 被挤下线
77+
* @event system.offline.device 由于开启设备锁,需要重新验证
7778
* @event system.offline.unknown 未知领域
7879
*
7980
* 内部事件(一般无需监听)
@@ -206,6 +207,8 @@ class AndroidClient extends Client {
206207
curr_msg_id;
207208
curr_msg_rand;
208209

210+
dir;
211+
209212
/**
210213
* @constructor
211214
* @param {Number} uin
@@ -214,13 +217,13 @@ class AndroidClient extends Client {
214217
constructor(uin, config = {}) {
215218
super();
216219
this.uin = uin;
220+
this.dir = createCacheDir(uin);
217221

218222
config = {
219223
platform: 2, //1手机 2平板 3手表(不支持部分群事件)
220224
log_level: "info", //trace,debug,info,warn,error,fatal,off
221225
kickoff: false, //被挤下线是否在3秒后反挤对方
222226
ignore_self: true, //群聊是否无视自己的发言
223-
device_path: path.join(process.mainModule.path, "data"), //设备文件保存路径,默认为启动文件同目录下的data文件夹
224227
...config
225228
};
226229
this.config = config;
@@ -232,7 +235,7 @@ class AndroidClient extends Client {
232235
this.ignore_self = config.ignore_self;
233236
this.kickoff_reconn = config.kickoff;
234237

235-
const filepath = path.join(config.device_path, `device-${uin}.json`);
238+
const filepath = path.join(this.dir, `device-${uin}.json`);
236239
if (!fs.existsSync(filepath))
237240
this.logger.info("创建了新的设备文件:" + filepath);
238241
this.device_info = device(filepath);
@@ -358,13 +361,20 @@ class AndroidClient extends Client {
358361
});
359362
}
360363

364+
writeSyncCookieCache() {
365+
const filepath = path.join(this.dir, "sync-cookie");
366+
if (this.sync_cookie)
367+
fs.writeFile(filepath, this.sync_cookie, ()=>{});
368+
}
369+
361370
/**
362371
* @private
363372
*/
364373
startHeartbeat() {
365374
if (this.heartbeat)
366375
return;
367376
this.heartbeat = setInterval(async()=>{
377+
this.writeSyncCookieCache();
368378
if (Date.now() - this.send_timestamp > 300000)
369379
this.write(outgoing.buildGetMessageRequestPacket(0, this));
370380
try {
@@ -502,6 +512,9 @@ class AndroidClient extends Client {
502512
} else if (data.info.includes("冻结")) {
503513
sub_type = "frozen";
504514
this.terminate();
515+
} else if (data.info.includes("设备锁")) {
516+
sub_type = "device";
517+
this.terminate();
505518
} else {
506519
sub_type = "unknown";
507520
this.terminate();
@@ -936,6 +949,45 @@ class AndroidClient extends Client {
936949
return buildApiRet(100);
937950
}
938951

952+
async addFriend(group_id, user_id, comment = "") {
953+
group_id = parseInt(group_id), user_id = parseInt(user_id);
954+
if (!checkUin(group_id) || !checkUin(user_id))
955+
return buildApiRet(100);
956+
try {
957+
const type = await this.send(outgoing.buildAddSettingRequestPacket(user_id, this));
958+
switch (type) {
959+
case 0:
960+
case 1:
961+
// case 3:
962+
case 4:
963+
var res = await this.send(outgoing.buildAddFriendRequestPacket(type, group_id, user_id, String(comment), this));
964+
return buildApiRet(res ? 0 : 102);
965+
default:
966+
return buildApiRet(102);
967+
}
968+
} catch (e) {
969+
return buildApiRet(103);
970+
}
971+
}
972+
973+
async deleteFriend(user_id, block = true) {
974+
user_id = parseInt(user_id);
975+
if (!checkUin(user_id))
976+
return buildApiRet(100);
977+
this.write(outgoing.buildDelFriendRequestPacket(user_id, block, this));
978+
return buildApiRet(1);
979+
}
980+
981+
async inviteFriend(group_id, user_id) {
982+
group_id = parseInt(group_id), user_id = parseInt(user_id);
983+
if (!checkUin(group_id) || !checkUin(user_id))
984+
return buildApiRet(100);
985+
this.write(outgoing.buildInviteRequestPacket(group_id, user_id, this));
986+
return buildApiRet(1);
987+
}
988+
989+
///////////////////////////////////////////////////
990+
939991
canSendImage() {
940992
return buildApiRet(0, {yes: true});
941993
}
@@ -976,31 +1028,28 @@ process.OICQ = {
9761028
logger, config
9771029
};
9781030

979-
function createRootDir() {
980-
try {
981-
if (!fs.existsSync(config.cache_root))
982-
fs.mkdirSync(config.cache_root);
983-
const img_path = path.join(config.cache_root, "image");
984-
const ptt_path = path.join(config.cache_root, "record");
985-
if (!fs.existsSync(img_path))
986-
fs.mkdirSync(img_path);
987-
if (!fs.existsSync(ptt_path))
988-
fs.mkdirSync(ptt_path);
989-
} catch (e) {
990-
logger.error("创建数据文件夹失败,请确认权限。" + config.cache_root);
991-
}
1031+
function createCacheDir(uin) {
1032+
if (!fs.existsSync(config.cache_root))
1033+
fs.mkdirSync(config.cache_root, {mode: 0o755, recursive: true});
1034+
const img_path = path.join(config.cache_root, "image");
1035+
const ptt_path = path.join(config.cache_root, "record");
1036+
const uin_path = path.join(config.cache_root, uin.toString());
1037+
if (!fs.existsSync(img_path))
1038+
fs.mkdirSync(img_path);
1039+
if (!fs.existsSync(ptt_path))
1040+
fs.mkdirSync(ptt_path);
1041+
if (!fs.existsSync(uin_path))
1042+
fs.mkdirSync(uin_path, {mode: 0o755});
1043+
return uin_path;
9921044
}
9931045

994-
createRootDir();
995-
9961046
/**
9971047
* 全局设置
9981048
*/
9991049
function setGlobalConfig(config = {}) {
10001050
Object.assign(process.OICQ.config, config);
10011051
if (config.debug)
10021052
logger.level = "debug";
1003-
createRootDir();
10041053
}
10051054

10061055
/**

docs/api.md

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ const config = {
3737
log_level: "info", //日志级别,有trace,debug,info,warn,error,fatal,off
3838
kickoff: false, //被挤下线是否在3秒后反挤对方
3939
ignore_self: true, //群聊是否无视自己的发言
40-
device_path: //设备文件保存路径,默认为启动文件同目录下的data文件夹
4140
};
4241
```
4342

@@ -56,7 +55,7 @@ const config = {
5655
oicq.setGlobalConfig({
5756
web_image_timeout: 0, //下载网络图片的超时时间(0表示系统自己判断)
5857
web_record_timeout: 0, //下载网络语音的超时时间
59-
cache_root: "", //缓存文件夹根目录,需要可写权限
58+
cache_root: "", //缓存文件夹根目录,需要可写权限,默认主目录下的data文件夹
6059
debug: false,
6160
});
6261
```
@@ -101,6 +100,7 @@ client.on("system.login", (data)=>{
101100
+ `system.offline.network` 网络断开
102101
+ `system.offline.frozen` 被冻结
103102
+ `system.offline.kickoff` 另一处登陆
103+
+ `system.offline.device` 由于开启设备锁,需要重新验证
104104
+ `system.offline.unknown` 未知
105105

106106
----
@@ -189,7 +189,7 @@ client.on("system.login", (data)=>{
189189

190190
----
191191

192-
### 获取列表和info
192+
### 获取好友、群、群员列表和info
193193

194194
+ async `client.getFriendList([no_cache])`
195195
+ async `client.getGroupList([no_cache])`
@@ -201,7 +201,7 @@ client.on("system.login", (data)=>{
201201

202202
----
203203

204-
### 消息类
204+
### 发私聊消息、群消息
205205

206206
message可以使用 `Array` 格式或 `String` 格式,支持CQ码
207207

@@ -212,15 +212,15 @@ message可以使用 `Array` 格式或 `String` 格式,支持CQ码
212212

213213
----
214214

215-
### 处理申请
215+
### 处理申请和邀请
216216

217217
+ async `client.setFriendAddRequest(flag[, approve, remark, block])`
218218
+ async `client.setGroupAddRequest(flag[, approve, reason, block])`
219219
+ block字段表示是否拉黑,默认false
220220

221221
----
222222

223-
### 群操作
223+
### 群操作(踢人、禁言、退群、设置等)
224224

225225
+ async `client.setGroupKick(group_id, user_id[, reject_add_request])`
226226
+ async `client.setGroupBan(group_id, user_id[, duration])`
@@ -235,15 +235,22 @@ message可以使用 `Array` 格式或 `String` 格式,支持CQ码
235235

236236
----
237237

238+
## 改状态、加好友删好友、邀请好友入群
239+
240+
+ async `client.changeOnlineStatus(status)`
241+
+ `status` 允许的值:11我在线上 31离开 41隐身 50忙碌 60Q我吧 70请勿打扰
242+
+ async `client.addFriend(group_id, user_id[, comment])`
243+
+ async `client.deleteFriend(user_id[, block])` block(屏蔽)默认是true
244+
+ async `client.inviteFriend(group_id, user_id)`
245+
246+
----
247+
248+
## 其他
249+
238250
+ `client.canSendImage()`
239251
+ `client.canSendRecord()`
240252
+ `client.getStatus()`
241253
+ `client.getVersionInfo()`
242254
+ `client.getLoginInfo()`
243255

244256
----
245-
246-
## 其他
247-
248-
+ async `client.changeOnlineStatus(status)`
249-
+ `status` 允许的值:11我在线上 31离开 41隐身 50忙碌 60Q我吧 70请勿打扰

docs/project.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
|-|-|-|-|-|-|-|
1010
|好友|||||||
1111
|群聊|||||||
12-
|临时会话|||||||
12+
|临时会话|||||||
1313

1414
----
1515

@@ -18,6 +18,8 @@
1818
|好友列表||◯(好友增减)|
1919
|处理申请|||
2020
|撤回消息|||
21+
|加群员好友|||
22+
|删除好友|||
2123

2224
----
2325

@@ -39,6 +41,7 @@
3941
|退群|||
4042
|同意邀请|||
4143
|处理申请|||
44+
|邀请好友入群|||
4245

4346
----
4447

lib/incoming.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"use strict";
22
const zlib = require("zlib");
3+
const fs = require("fs");
4+
const path = require("path");
35
const Readable = require("stream").Readable;
46
const tea = require('crypto-tea');
57
const ecdh = require("./ecdh");
@@ -182,7 +184,9 @@ function decodeLoginResponse(blob, c) {
182184
stream.read(2);
183185
c.captcha_sign = stream.read(signLen);
184186
const image = stream.read();
185-
c.logger.info("收到图片验证码。");
187+
const filepath = path.join(c.dir, `captcha.jpg`);
188+
fs.writeFileSync(filepath, image);
189+
c.logger.info(`收到图片验证码,已保存到文件(${filepath}),请查看并输入: `);
186190
return event.emit(c, "system.login.captcha", {image});
187191
}
188192
c.logger.error("收到未知格式的验证码,暂不支持。");
@@ -350,15 +354,16 @@ function decodeMessageSvcResponse(blob, c) {
350354
}
351355

352356
c.write(outgoing.buildDeleteMessageRequestPacket(rubbish, c));
357+
if (common.timestamp() - c.friend_list_uptime > 900)
358+
c.getFriendList(true);
353359
if (o.syncFlag !== 2) {
354360
c.write(outgoing.buildGetMessageRequestPacket(o.syncFlag, c));
355361
} else if (!c.sync_finished) {
356362
c.sync_finished = true;
363+
c.writeSyncCookieCache();
357364
c.logger.info("初始化完毕,开始处理消息。")
358365
event.emit(c, "system.online");
359366
}
360-
if (common.timestamp() - c.friend_list_uptime > 900)
361-
c.getFriendList(true);
362367
}
363368

364369
async function decodePushNotifyEvent(blob, c) {
@@ -918,6 +923,26 @@ function decodeGroupFileUrlResponse(blob, c) {
918923
return pb.decode("D6D6RspBody", pb.decode("OIDBSSOPkg", blob).bodybuffer);
919924
}
920925

926+
function decodeAddSettingResponse(blob, c) {
927+
const nested = jce.decodeWrapper(blob);
928+
const parent = jce.decode(nested);
929+
if (parent[4]) return false;
930+
return parent[2];
931+
}
932+
function decodeAddFriendResponse(blob, c) {
933+
const nested = jce.decodeWrapper(blob);
934+
const parent = jce.decode(nested);
935+
return parent[6] === 0;
936+
}
937+
function decodeDelFriendResponse(blob, c) {
938+
// const nested = jce.decodeWrapper(blob);
939+
// const parent = jce.decode(nested);
940+
// common.log(parent)
941+
}
942+
function decodeInviteResponse(blob, c) {
943+
// common.log(pb.decode("OIDBSSOPkg", blob).bodybuffer)
944+
}
945+
921946
//----------------------------------------------------------------------------------------------------
922947

923948
const CMD = outgoing.CMD;
@@ -935,6 +960,11 @@ const decoders = new Map([
935960
[CMD.MEMBER_LIST, decodeGroupMemberListResponse],
936961
[CMD.GROUP_INFO, decodeGroupInfoResponse],
937962

963+
[CMD.ADD_SETTING, decodeAddSettingResponse],
964+
[CMD.ADD_FRIEND, decodeAddFriendResponse],
965+
[CMD.DEL_FRIEND, decodeDelFriendResponse],
966+
[CMD.GROUP_INVITE, decodeInviteResponse],
967+
938968
[CMD.FRIEND_REQ, decodeNewFriendResponse],
939969
[CMD.FRIEND_REQ_ACT, decodeNewFriendActionResponse],
940970
[CMD.GROUP_REQ, decodeNewGroupResponse],

0 commit comments

Comments
 (0)