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 #90 from takayama-lily/dev
Browse files Browse the repository at this point in the history
-
  • Loading branch information
takayama-lily authored Nov 14, 2020
2 parents b2fabd3 + 280a3bf commit a91183a
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 47 deletions.
4 changes: 3 additions & 1 deletion client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ConfBot {
platform?: number, //1手机 2平板(默认) 3手表(不支持部分群事件)
kickoff?: boolean, //被挤下线是否在3秒后反挤对方,默认false
ignore_self?: boolean,//群聊是否无视自己的发言,默认true
resend?: boolean, //被风控时是否尝试用另一种方式强行发送,默认true
data_dir?: string, //数据存储文件夹,需要可写权限,默认主目录下的data文件夹
}

Expand Down Expand Up @@ -226,7 +227,8 @@ export class Client extends events.EventEmitter {
setBirthday(birthday: string | number): Promise<RetCommon>; //20110202的形式
setDescription(description?: string): Promise<RetCommon>;
setSignature(signature?: string): Promise<RetCommon>;
setPortrait(file?: Buffer | string): Promise<RetCommon>; //图片CQ码中file相同格式
setPortrait(file: Buffer | string): Promise<RetCommon>; //图片CQ码中file相同格式
setGroupPortrait(group_id: Uin, file: Buffer | string): Promise<RetCommon>;

getCookies(domain?: string): Promise<RetCommon>;
getCsrfToken(): Promise<RetCommon>;
Expand Down
38 changes: 27 additions & 11 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,32 @@ class AndroidClient extends Client {
const2 = crypto.randomBytes(4).readUInt32BE();
const3 = crypto.randomBytes(1)[0];

stat = {
start_time: timestamp(),
lost_times: 0,
recv_pkt_cnt: 0,
sent_pkt_cnt: 0,
recv_msg_cnt: 0,
sent_msg_cnt: 0,
};

constructor(uin, config) {
super();
this.uin = uin;

config = {
platform: 2, //1手机 2平板 3手表(不支持部分群事件)
log_level: "info", //trace,debug,info,warn,error,fatal,off
kickoff: false, //被挤下线是否在3秒后反挤
ignore_self: true, //是否无视自己的消息(群聊、私聊)
data_dir: path.join(process.mainModule.path, "data"),
platform: 2,
log_level: "info",
kickoff: false,
ignore_self:true,
resend: true,
data_dir: path.join(process.mainModule.path, "data"),
...config
};
this.config = config;
this.dir = createCacheDir(config.data_dir, uin);
this.dir = createDataDir(config.data_dir, uin);
this.logger = log4js.getLogger(`[BOT:${uin}]`);
this.logger.level = config.log_level;
this.ignore_self = !!config.ignore_self;
this.kickoff_reconn = !!config.kickoff;

const filepath = path.join(this.dir, `device-${uin}.json`);
if (!fs.existsSync(filepath))
Expand All @@ -117,6 +125,7 @@ class AndroidClient extends Client {
});
this.on("close", (e_flag)=>{
this.read();
++this.stat.lost_times;
if (this.remoteAddress)
this.logger.info(`${this.remoteAddress}:${this.remotePort} closed`);
this.stopHeartbeat();
Expand All @@ -143,6 +152,7 @@ class AndroidClient extends Client {
this.reconn_flag = true;
this.recv_timestamp = Date.now();
const packet = this.read(len - 4);
++this.stat.recv_pkt_cnt;
try {
core.parseIncomingPacket.call(this, packet);
} catch (e) {
Expand Down Expand Up @@ -196,6 +206,7 @@ class AndroidClient extends Client {
}

async send(packet, timeout = 3000) {
++this.stat.sent_pkt_cnt;
const seq_id = this.seq_id;
return new Promise((resolve, reject)=>{
this.write(packet, ()=>{
Expand All @@ -213,6 +224,7 @@ class AndroidClient extends Client {
});
}
writeUNI(cmd, body, seq) {
++this.stat.sent_pkt_cnt;
this.write(wt.build0x0BPacket.apply(this, arguments));
}
async sendUNI(cmd, body, seq) {
Expand Down Expand Up @@ -266,7 +278,7 @@ class AndroidClient extends Client {
let sub_type;
if (data.info.includes("另一")) {
sub_type = "kickoff";
if (this.kickoff_reconn) {
if (this.config.kickoff) {
this.logger.info("3秒后重新连接..");
setTimeout(this.login.bind(this), 3000);
} else {
Expand Down Expand Up @@ -569,6 +581,9 @@ class AndroidClient extends Client {
async setPortrait(file) {
return await this.useProtocol(indi.setPortrait, arguments);
}
async setGroupPortrait(group_id, file) {
return await this.useProtocol(indi.setGroupPortrait, arguments);
}

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

Expand Down Expand Up @@ -631,7 +646,8 @@ class AndroidClient extends Client {
return buildApiRet(0, {
online: this.isOnline(),
status: this.online_status,
msg_cnt_per_min: this.calcMsgCnt()
msg_cnt_per_min: this.calcMsgCnt(),
statistics: this.stat,
})
}
getLoginInfo() {
Expand All @@ -654,7 +670,7 @@ process.OICQ = {

console.log("OICQ程序启动。当前内核版本:v" + version.version);

function createCacheDir(dir, uin) {
function createDataDir(dir, uin) {
if (!fs.existsSync(dir))
fs.mkdirSync(dir, {mode: 0o755, recursive: true});
const img_path = path.join(dir, "image");
Expand Down
4 changes: 3 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ const client = oicq.createClient(uin, config);
log_level: "info", //日志级别,有trace,debug,info,warn,error,fatal,off
kickoff: false, //被挤下线是否在3秒后反挤
ignore_self: true, //是否无视自己的消息(群聊、私聊)
data_dir: undefined, //数据存储文件夹,需要可写权限,默认主目录下的data文件夹
resend: true, //被风控时是否尝试用另一种方式强行发送
data_dir: //数据存储文件夹,需要可写权限,默认主目录下的data文件夹
}
```

Expand Down Expand Up @@ -186,6 +187,7 @@ message可以使用 `Array` 格式或 `String` 格式,支持CQ码
+ async `client.setDescription([description])` 设置个人说明
+ async `client.setSignature([signature])` 设置个性签名
+ async `client.setPortrait(file)` 设置个人头像(file为Buffer或图片CQ码中相同格式的字符串)
+ async `client.setGroupPortrait(group_id, file)` 设置群头像

----

Expand Down
5 changes: 3 additions & 2 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ function onPushNotify(blob) {
case 166:
case 167:
case 208:
case 529:
return getMsg.call(this);
case 84:
case 87:
Expand Down Expand Up @@ -108,9 +109,9 @@ async function getMsg(sync_flag = 0) {
if (!this.sync_finished)
continue;
let uin = head[1];
if (uin === this.uin && (this.ignore_self || uin !== head[2]))
if (uin === this.uin && (this.config.ignore_self || uin !== head[2]))
continue;
if (![33, 141, 166, 167, 208].includes(type))
if (![33, 141, 166, 167, 208, 529].includes(type))
continue;
if (this.msgExists(uin, type, head[5], head[6]))
continue;
Expand Down
46 changes: 41 additions & 5 deletions lib/individual.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
"use strict";
const http = require("http");
const {uinAutoCheck, md5} = require("./common");
const {downloadWebImage, highwayUpload, readFile} = require("./service");
const pb = require("./pb");
const jce = require("./jce");

/**
* 设置头像
* @this {import("./ref").Client}
* @param {String|Buffer} file
* @returns {Buffer}
*/
async function setPortrait(file) {
async function getImgBuf(file) {
let buf;
if (file instanceof Buffer) {
buf = file;
Expand All @@ -32,6 +32,16 @@ async function setPortrait(file) {
}
}
}
return buf;
}

/**
* 设置头像
* @this {import("./ref").Client}
* @param {String|Buffer} file
*/
async function setPortrait(file) {
const buf = await getImgBuf(file);
const body = pb.encode({
1281: {
1: this.uin,
Expand All @@ -44,9 +54,35 @@ async function setPortrait(file) {
});
const blob = await this.sendUNI("HttpConn.0x6ff_501", body);
const rsp = pb.decode(blob)[1281];
highwayUpload.call(this, rsp[3][2][0][2], rsp[3][2][0][3], {
await highwayUpload.call(this, rsp[3][2][0][2], rsp[3][2][0][3], {
buf, md5: md5(buf), key: rsp[1].raw
}, 5);
return {result: 0};
}

/**
* 群头像
* @this {import("./ref").Client}
* @param {String|Buffer} file
*/
async function setGroupPortrait(group_id, file) {
var [group_id] = uinAutoCheck(group_id);
const buf = await getImgBuf(file);
await this.getCookies();
const url = `http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=${this.sig.skey}&range=0&uin=${this.uin}&seq=${this.seq_id}&groupuin=${group_id}&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=${buf.length}`;
try {
await new Promise((resolve, reject)=>{
http.request(url, {method:"POST"}, (res)=>{
if (res.statusCode === 200)
resolve();
else
reject();
}).on("error", (e)=>reject(e.message)).end(buf);
});
return {result: 0};
} catch (e) {
return {result: 102, emsg: e};
}
}

/**
Expand Down Expand Up @@ -254,5 +290,5 @@ async function setStatus(status) {
}

module.exports = {
setStatus, setProfile, setSign, sendLike, addFriend, delFriend, setPortrait,
setStatus, setProfile, setSign, sendLike, addFriend, delFriend, setPortrait, setGroupPortrait
};
22 changes: 13 additions & 9 deletions lib/message/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const zlib = require("zlib");
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
const {randomBytes} = require("crypto");
const spawn = require("child_process");
const music = require("./music");
const face = require("./face");
Expand Down Expand Up @@ -84,7 +84,7 @@ class Builder {
} catch (e) {}
}
}
if (dummy || (!common.checkUin(q) && qq !== "all")) {
if (dummy == "1" || (!common.checkUin(q) && qq !== "all")) {
if (!display.startsWith("@"))
display = "@" + display;
return this.buildTextElem(display);
Expand Down Expand Up @@ -175,8 +175,7 @@ class Builder {
async buildImageElem(cq) {
let {file, url, cache, type, timeout, proxy} = cq;
if (!file) return;
file = file.trim();
let fid, buf, md5 = crypto.randomBytes(16), size = 65536;
let fid, buf, md5 = randomBytes(16), size = 65536;
const nested = {}; //pb nested obj
if (type === "flash") {
var elem = this.type ? {1:nested} : {2:nested};
Expand All @@ -194,13 +193,18 @@ class Builder {
const img = {
buf, md5, size, nested
};

// bytes
if (file instanceof Buffer || file instanceof Uint8Array) {
buf = file, md5 = common.md5(file), size = file.length;
}

// 网络图片
if (file.startsWith("http://") || file.startsWith("https://")) {
else if (file.startsWith("http://") || file.startsWith("https://")) {
const filename = common.md5(Buffer.from(file, "utf-8")).toString('hex');
const filepath = path.join(this.c.dir, "..", "image", filename);
try {
if (cache === "0")
if (cache == "0")
throw new Error("no cache");
const content = await fs.promises.readFile(filepath, "utf8");
md5 = Buffer.from(content.slice(0, 32), "hex"), size = parseInt(content.slice(32));
Expand All @@ -227,7 +231,7 @@ class Builder {

// base64图片
else if (file.startsWith("base64://")) {
file = file.replace("base64://", "");
file = file.trim().replace("base64://", "");
buf = Buffer.from(file, "base64");
md5 = common.md5(buf), size = buf.length;
}
Expand All @@ -237,7 +241,7 @@ class Builder {
//本地图片
if (md5.length !== 16) {
try {
file = file.replace(/^file:\/{2,3}/, "");
file = file.trim().replace(/^file:\/{2,3}/, "");
buf = await readFile(file);
md5 = common.md5(buf), size = buf.length;
} catch (e) {
Expand Down Expand Up @@ -297,7 +301,7 @@ class Builder {
url = file;
const cache_filename = common.md5(Buffer.from(file, "utf-8")).toString('hex');
const cache_filepath = path.join(this.c.dir, "..", "record", cache_filename);
if (cache !== "0") {
if (cache != "0") {
try {
buf = await fs.promises.readFile(cache_filepath);
} catch (e) {}
Expand Down
Loading

0 comments on commit a91183a

Please sign in to comment.