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

Commit

Permalink
Merge pull request #34 from takayama-lily/dev
Browse files Browse the repository at this point in the history
-
  • Loading branch information
takayama-lily authored Sep 14, 2020
2 parents 0f1da36 + 6a6c404 commit 1227806
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 55 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@

```js
const oicq = require("oicq");
const uin = 123456789, config = {};
const uin = 123456789;
const password_md5 = "202cb962ac59075b964b07152d234b70";
const bot = oicq.createClient(uin, config);
const bot = oicq.createClient(uin);
bot.login(password_md5);
```

Expand Down
85 changes: 67 additions & 18 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Client extends net.Socket {
* @event system.offline.network 拔线
* @event system.offline.frozen 账号冻结
* @event system.offline.kickoff 被挤下线
* @event system.offline.device 由于开启设备锁,需要重新验证
* @event system.offline.unknown 未知领域
*
* 内部事件(一般无需监听)
Expand Down Expand Up @@ -206,6 +207,8 @@ class AndroidClient extends Client {
curr_msg_id;
curr_msg_rand;

dir;

/**
* @constructor
* @param {Number} uin
Expand All @@ -214,13 +217,13 @@ class AndroidClient extends Client {
constructor(uin, config = {}) {
super();
this.uin = uin;
this.dir = createCacheDir(uin);

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

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

writeSyncCookieCache() {
const filepath = path.join(this.dir, "sync-cookie");
if (this.sync_cookie)
fs.writeFile(filepath, this.sync_cookie, ()=>{});
}

/**
* @private
*/
startHeartbeat() {
if (this.heartbeat)
return;
this.heartbeat = setInterval(async()=>{
this.writeSyncCookieCache();
if (Date.now() - this.send_timestamp > 300000)
this.write(outgoing.buildGetMessageRequestPacket(0, this));
try {
Expand Down Expand Up @@ -502,6 +512,9 @@ class AndroidClient extends Client {
} else if (data.info.includes("冻结")) {
sub_type = "frozen";
this.terminate();
} else if (data.info.includes("设备锁")) {
sub_type = "device";
this.terminate();
} else {
sub_type = "unknown";
this.terminate();
Expand Down Expand Up @@ -936,6 +949,45 @@ class AndroidClient extends Client {
return buildApiRet(100);
}

async addFriend(group_id, user_id, comment = "") {
group_id = parseInt(group_id), user_id = parseInt(user_id);
if (!checkUin(group_id) || !checkUin(user_id))
return buildApiRet(100);
try {
const type = await this.send(outgoing.buildAddSettingRequestPacket(user_id, this));
switch (type) {
case 0:
case 1:
// case 3:
case 4:
var res = await this.send(outgoing.buildAddFriendRequestPacket(type, group_id, user_id, String(comment), this));
return buildApiRet(res ? 0 : 102);
default:
return buildApiRet(102);
}
} catch (e) {
return buildApiRet(103);
}
}

async deleteFriend(user_id, block = true) {
user_id = parseInt(user_id);
if (!checkUin(user_id))
return buildApiRet(100);
this.write(outgoing.buildDelFriendRequestPacket(user_id, block, this));
return buildApiRet(1);
}

async inviteFriend(group_id, user_id) {
group_id = parseInt(group_id), user_id = parseInt(user_id);
if (!checkUin(group_id) || !checkUin(user_id))
return buildApiRet(100);
this.write(outgoing.buildInviteRequestPacket(group_id, user_id, this));
return buildApiRet(1);
}

///////////////////////////////////////////////////

canSendImage() {
return buildApiRet(0, {yes: true});
}
Expand Down Expand Up @@ -976,31 +1028,28 @@ process.OICQ = {
logger, config
};

function createRootDir() {
try {
if (!fs.existsSync(config.cache_root))
fs.mkdirSync(config.cache_root);
const img_path = path.join(config.cache_root, "image");
const ptt_path = path.join(config.cache_root, "record");
if (!fs.existsSync(img_path))
fs.mkdirSync(img_path);
if (!fs.existsSync(ptt_path))
fs.mkdirSync(ptt_path);
} catch (e) {
logger.error("创建数据文件夹失败,请确认权限。" + config.cache_root);
}
function createCacheDir(uin) {
if (!fs.existsSync(config.cache_root))
fs.mkdirSync(config.cache_root, {mode: 0o755, recursive: true});
const img_path = path.join(config.cache_root, "image");
const ptt_path = path.join(config.cache_root, "record");
const uin_path = path.join(config.cache_root, uin.toString());
if (!fs.existsSync(img_path))
fs.mkdirSync(img_path);
if (!fs.existsSync(ptt_path))
fs.mkdirSync(ptt_path);
if (!fs.existsSync(uin_path))
fs.mkdirSync(uin_path, {mode: 0o755});
return uin_path;
}

createRootDir();

/**
* 全局设置
*/
function setGlobalConfig(config = {}) {
Object.assign(process.OICQ.config, config);
if (config.debug)
logger.level = "debug";
createRootDir();
}

/**
Expand Down
29 changes: 18 additions & 11 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const config = {
log_level: "info", //日志级别,有trace,debug,info,warn,error,fatal,off
kickoff: false, //被挤下线是否在3秒后反挤对方
ignore_self: true, //群聊是否无视自己的发言
device_path: //设备文件保存路径,默认为启动文件同目录下的data文件夹
};
```

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

----
Expand Down Expand Up @@ -189,7 +189,7 @@ client.on("system.login", (data)=>{

----

### 获取列表和info
### 获取好友、群、群员列表和info

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

----

### 消息类
### 发私聊消息、群消息

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

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

----

### 处理申请
### 处理申请和邀请

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

----

### 群操作
### 群操作(踢人、禁言、退群、设置等)

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

----

## 改状态、加好友删好友、邀请好友入群

+ async `client.changeOnlineStatus(status)`
+ `status` 允许的值:11我在线上 31离开 41隐身 50忙碌 60Q我吧 70请勿打扰
+ async `client.addFriend(group_id, user_id[, comment])`
+ async `client.deleteFriend(user_id[, block])` block(屏蔽)默认是true
+ async `client.inviteFriend(group_id, user_id)`

----

## 其他

+ `client.canSendImage()`
+ `client.canSendRecord()`
+ `client.getStatus()`
+ `client.getVersionInfo()`
+ `client.getLoginInfo()`

----

## 其他

+ async `client.changeOnlineStatus(status)`
+ `status` 允许的值:11我在线上 31离开 41隐身 50忙碌 60Q我吧 70请勿打扰
5 changes: 4 additions & 1 deletion docs/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
|-|-|-|-|-|-|-|
|好友|||||||
|群聊|||||||
|临时会话|||||||
|临时会话|||||||

----

Expand All @@ -18,6 +18,8 @@
|好友列表||◯(好友增减)|
|处理申请|||
|撤回消息|||
|加群员好友|||
|删除好友|||

----

Expand All @@ -39,6 +41,7 @@
|退群|||
|同意邀请|||
|处理申请|||
|邀请好友入群|||

----

Expand Down
36 changes: 33 additions & 3 deletions lib/incoming.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use strict";
const zlib = require("zlib");
const fs = require("fs");
const path = require("path");
const Readable = require("stream").Readable;
const tea = require('crypto-tea');
const ecdh = require("./ecdh");
Expand Down Expand Up @@ -182,7 +184,9 @@ function decodeLoginResponse(blob, c) {
stream.read(2);
c.captcha_sign = stream.read(signLen);
const image = stream.read();
c.logger.info("收到图片验证码。");
const filepath = path.join(c.dir, `captcha.jpg`);
fs.writeFileSync(filepath, image);
c.logger.info(`收到图片验证码,已保存到文件(${filepath}),请查看并输入: `);
return event.emit(c, "system.login.captcha", {image});
}
c.logger.error("收到未知格式的验证码,暂不支持。");
Expand Down Expand Up @@ -350,15 +354,16 @@ function decodeMessageSvcResponse(blob, c) {
}

c.write(outgoing.buildDeleteMessageRequestPacket(rubbish, c));
if (common.timestamp() - c.friend_list_uptime > 900)
c.getFriendList(true);
if (o.syncFlag !== 2) {
c.write(outgoing.buildGetMessageRequestPacket(o.syncFlag, c));
} else if (!c.sync_finished) {
c.sync_finished = true;
c.writeSyncCookieCache();
c.logger.info("初始化完毕,开始处理消息。")
event.emit(c, "system.online");
}
if (common.timestamp() - c.friend_list_uptime > 900)
c.getFriendList(true);
}

async function decodePushNotifyEvent(blob, c) {
Expand Down Expand Up @@ -918,6 +923,26 @@ function decodeGroupFileUrlResponse(blob, c) {
return pb.decode("D6D6RspBody", pb.decode("OIDBSSOPkg", blob).bodybuffer);
}

function decodeAddSettingResponse(blob, c) {
const nested = jce.decodeWrapper(blob);
const parent = jce.decode(nested);
if (parent[4]) return false;
return parent[2];
}
function decodeAddFriendResponse(blob, c) {
const nested = jce.decodeWrapper(blob);
const parent = jce.decode(nested);
return parent[6] === 0;
}
function decodeDelFriendResponse(blob, c) {
// const nested = jce.decodeWrapper(blob);
// const parent = jce.decode(nested);
// common.log(parent)
}
function decodeInviteResponse(blob, c) {
// common.log(pb.decode("OIDBSSOPkg", blob).bodybuffer)
}

//----------------------------------------------------------------------------------------------------

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

[CMD.ADD_SETTING, decodeAddSettingResponse],
[CMD.ADD_FRIEND, decodeAddFriendResponse],
[CMD.DEL_FRIEND, decodeDelFriendResponse],
[CMD.GROUP_INVITE, decodeInviteResponse],

[CMD.FRIEND_REQ, decodeNewFriendResponse],
[CMD.FRIEND_REQ_ACT, decodeNewFriendActionResponse],
[CMD.GROUP_REQ, decodeNewGroupResponse],
Expand Down
Loading

0 comments on commit 1227806

Please sign in to comment.