🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

oicq

Package Overview
Dependencies
Maintainers
1
Versions
176
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

oicq - npm Package Compare versions

Comparing version
2.1.7
to
2.1.8
+580
lib/client.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createClient = exports.Client = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const log4js = __importStar(require("log4js"));
const core_1 = require("./core");
const pkg = require("../package.json");
const common_1 = require("./common");
const internal_1 = require("./internal");
const friend_1 = require("./friend");
const group_1 = require("./group");
const member_1 = require("./member");
const message_1 = require("./message");
/** 一个客户端 */
class Client extends core_1.BaseClient {
//@ts-ignore ts2376??
constructor(uin, conf) {
const config = {
log_level: "info",
platform: core_1.Platform.Android,
auto_server: true,
ignore_self: true,
resend: true,
cache_group_member: true,
reconn_interval: 5,
data_dir: path.join(require?.main?.path || process.cwd(), "data"),
...conf,
};
const dir = createDataDir(config.data_dir, uin);
const file = path.join(dir, `device-${uin}.json`);
try {
var device = require(file);
var _ = false;
}
catch {
var device = (0, core_1.generateShortDevice)(uin);
var _ = true;
fs.writeFile(file, JSON.stringify(device, null, 2), common_1.NOOP);
}
super(uin, config.platform, device);
/** 得到一个群对象, 每个群都是单例 */
this.pickGroup = group_1.Group.as.bind(this);
/** 得到一个好友对象, 每个好友都是单例 */
this.pickFriend = friend_1.Friend.as.bind(this);
/** 得到一个群员对象, 开启群员列表缓存时,每个群员都是单例 */
this.pickMember = member_1.Member.as.bind(this);
/** 创建一个用户对象 */
this.pickUser = friend_1.User.as.bind(this);
/** 创建一个讨论组对象 */
this.pickDiscuss = group_1.Discuss.as.bind(this);
this._cache = new Map();
/** 好友列表(务必以`ReadonlyMap`方式访问) */
this.fl = new Map();
/** 陌生人列表(务必以`ReadonlyMap`方式访问) */
this.sl = new Map();
/** 群列表(务必以`ReadonlyMap`方式访问) */
this.gl = new Map();
/** 群员列表缓存(务必以`ReadonlyMap`方式访问) */
this.gml = new Map();
/** 黑名单列表(务必以`ReadonlySet`方式访问) */
this.blacklist = new Set();
/** 好友分组 */
this.classes = new Map();
/** 勿手动修改这些属性 */
this.status = 0;
this.nickname = "";
this.sex = "unknown";
this.age = 0;
this.bid = "";
/** 漫游表情缓存 */
this.stamp = new Set();
this.cookies = new Proxy(this.pskey, {
get: (obj, domain) => {
const cookie = `uin=o${this.uin}; skey=${this.sig.skey};`;
if (!obj[domain])
return cookie;
return `${cookie} p_uin=o${this.uin}; p_skey=${obj[domain]};`;
},
set: () => {
return false;
}
});
this.logger = log4js.getLogger(`[${this.apk.display}:${uin}]`);
this.logger.level = config.log_level;
if (_)
this.logger.mark("创建了新的设备文件:" + file);
this.logger.mark("----------");
this.logger.mark(`Package Version: oicq@${pkg.version} (Released on ${pkg.upday})`);
this.logger.mark("View Changelogs:https://github.com/takayama-lily/oicq/releases");
this.logger.mark("----------");
this.dir = dir;
this.config = config;
internal_1.bindInternalListeners.call(this);
this.on("internal.verbose", (verbose, level) => {
const list = ["fatal", "mark", "error", "warn", "info", "trace"];
this.logger[list[level]](verbose);
});
(0, common_1.lock)(this, "dir");
(0, common_1.lock)(this, "config");
(0, common_1.lock)(this, "_cache");
(0, common_1.lock)(this, "internal");
(0, common_1.lock)(this, "pickUser");
(0, common_1.lock)(this, "pickFriend");
(0, common_1.lock)(this, "pickGroup");
(0, common_1.lock)(this, "pickDiscuss");
(0, common_1.lock)(this, "pickMember");
(0, common_1.lock)(this, "cookies");
(0, common_1.lock)(this, "fl");
(0, common_1.lock)(this, "gl");
(0, common_1.lock)(this, "sl");
(0, common_1.lock)(this, "gml");
(0, common_1.lock)(this, "blacklist");
(0, common_1.hide)(this, "_sync_cookie");
let n = 0;
this.heartbeat = () => {
this._calcMsgCntPerMin();
n++;
if (n > 10) {
n = 0;
this.setOnlineStatus();
}
};
if (!this.config.auto_server)
this.setRemoteServer("msfwifi.3g.qq.com", 8080);
}
get [Symbol.toStringTag]() {
return "OicqClient";
}
/** csrf token */
get bkn() {
let bkn = 5381;
for (let v of this.sig.skey)
bkn = bkn + (bkn << 5) + v;
bkn &= 2147483647;
return bkn;
}
/** 数据统计 */
get stat() {
this.statistics.msg_cnt_per_min = this._calcMsgCntPerMin();
return this.statistics;
}
/** 修改日志级别 */
set log_level(level) {
this.logger.level = level;
this.config.log_level = level;
}
/**
* 会优先尝试使用token登录 (token在上次登录成功后存放在`this.dir`下)
*
* 无token或token失效时:
* * 传了`password`则尝试密码登录
* * 不传`password`则尝试扫码登录
*
* 掉线重连时也是自动调用此函数,走相同逻辑
* 你也可以在配置中修改`reconn_interval`,关闭掉线重连并自行处理
*
* @param password 可以为密码原文,或密码的md5值
*/
async login(password) {
if (password && password.length > 0) {
let md5pass;
if (typeof password === "string")
md5pass = Buffer.from(password, "hex");
else
md5pass = password;
if (md5pass.length !== 16)
md5pass = (0, common_1.md5)(String(password));
this.password_md5 = md5pass;
}
try {
const token = await fs.promises.readFile(path.join(this.dir, "token"));
return this.tokenLogin(token);
}
catch {
if (this.password_md5)
return this.passwordLogin(this.password_md5);
else
return this.sig.qrsig.length ? this.qrcodeLogin() : this.fetchQrcode();
}
}
/** 设置在线状态 */
setOnlineStatus(status = this.status || common_1.OnlineStatus.Online) {
return internal_1.setStatus.call(this, status);
}
/** 设置昵称 */
async setNickname(nickname) {
return this._setProfile(0x14E22, Buffer.from(String(nickname)));
}
/** 设置性别(1男2女) */
async setGender(gender) {
return this._setProfile(0x14E29, Buffer.from([gender]));
}
/** 设置生日(20201202) */
async setBirthday(birthday) {
const birth = String(birthday).replace(/[^\d]/g, "");
const buf = Buffer.allocUnsafe(4);
buf.writeUInt16BE(Number(birth.substr(0, 4)));
buf[2] = Number(birth.substr(4, 2));
buf[3] = Number(birth.substr(6, 2));
return this._setProfile(0x16593, buf);
}
/** 设置个人说明 */
async setDescription(description = "") {
return this._setProfile(0x14E33, Buffer.from(String(description)));
}
/** 设置个性签名 */
async setSignature(signature = "") {
return internal_1.setSign.call(this, signature);
}
/** 设置头像 */
async setAvatar(file) {
return internal_1.setAvatar.call(this, new message_1.Image({ type: "image", file }));
}
/** 获取漫游表情 */
getRoamingStamp(no_cache = false) {
return internal_1.getStamp.call(this, no_cache);
}
/** 删除表情(支持批量) */
deleteStamp(id) {
return internal_1.delStamp.call(this, id);
}
/** 获取系统消息 */
getSystemMsg() {
return internal_1.getSysMsg.call(this);
}
/** 添加好友分组 */
addClass(name) {
return internal_1.addClass.call(this, name);
}
/** 删除好友分组 */
deleteClass(id) {
return internal_1.delClass.call(this, id);
}
/** 重命名好友分组 */
renameClass(id, name) {
return internal_1.renameClass.call(this, id, name);
}
/** 重载好友列表 */
reloadFriendList() {
return internal_1.loadFL.call(this);
}
/** 重载陌生人列表 */
reloadStrangerList() {
return internal_1.loadSL.call(this);
}
/** 重载群列表 */
reloadGroupList() {
return internal_1.loadGL.call(this);
}
/** 重载黑名单 */
reloadBlackList() {
return internal_1.loadBL.call(this);
}
/** 清空缓存文件 fs.rm need v14.14 */
cleanCache() {
const dir = path.join(this.dir, "../image");
fs.rm?.(dir, { recursive: true }, () => {
fs.mkdir(dir, common_1.NOOP);
});
}
/** 获取视频下载地址 */
getVideoUrl(fid, md5) {
return this.pickFriend(this.uin).getVideoUrl(fid, md5);
}
/** 获取转发消息 */
getForwardMsg(resid) {
return this.pickFriend(this.uin).getForwardMsg(resid);
}
/** 制作转发消息 */
makeForwardMsg(fake, dm = false) {
return (dm ? this.pickFriend : this.pickGroup)(this.uin).makeForwardMsg(fake);
}
/** @cqhttp (cqhttp遗留方法) use client.cookies[domain] */
getCookies(domain = "") {
return this.cookies[domain];
}
/** @cqhttp use client.bkn */
getCsrfToken() {
return this.bkn;
}
/** @cqhttp use client.fl */
getFriendList() {
return this.fl;
}
/** @cqhttp use client.gl */
getGroupList() {
return this.gl;
}
/** @cqhttp use client.sl */
getStrangerList() {
return this.sl;
}
/** @cqhttp use user.getSimpleInfo() */
async getStrangerInfo(user_id) {
return this.pickUser(user_id).getSimpleInfo();
}
/** @cqhttp use group.info or group.renew() */
async getGroupInfo(group_id, no_cache = false) {
const group = this.pickGroup(group_id);
if (no_cache)
return group.renew();
return group.info || group.renew();
}
/** @cqhttp use group.getMemberList() */
async getGroupMemberList(group_id, no_cache = false) {
return this.pickGroup(group_id).getMemberMap(no_cache);
}
/** @cqhttp use member.info or member.renew() */
async getGroupMemberInfo(group_id, user_id, no_cache = false) {
if (no_cache || !this.gml.get(group_id)?.has(user_id))
return this.pickMember(group_id, user_id).renew();
return this.gml.get(group_id)?.get(user_id);
}
/** @cqhttp use friend.sendMsg() */
async sendPrivateMsg(user_id, message, source) {
return this.pickFriend(user_id).sendMsg(message);
}
/** @cqhttp use group.sendMsg() */
async sendGroupMsg(group_id, message, source) {
return this.pickGroup(group_id).sendMsg(message);
}
/** @cqhttp use discuss.sendMsg() */
async sendDiscussMsg(discuss_id, message, source) {
return this.pickDiscuss(discuss_id).sendMsg(message);
}
/** @cqhttp use member.sendMsg() */
async sendTempMsg(group_id, user_id, message) {
return this.pickMember(group_id, user_id).sendMsg(message);
}
/** @cqhttp use user.recallMsg() or group.recallMsg() */
async deleteMsg(message_id) {
if (message_id.length > 24) {
const { group_id, seq, rand, pktnum } = (0, message_1.parseGroupMessageId)(message_id);
return this.pickGroup(group_id).recallMsg(seq, rand, pktnum);
}
else {
const { user_id, seq, rand, time } = (0, message_1.parseDmMessageId)(message_id);
return this.pickUser(user_id).recallMsg(seq, rand, time);
}
}
/** @cqhttp use user.markRead() or group.markRead() */
async reportReaded(message_id) {
if (message_id.length > 24) {
const { group_id, seq } = (0, message_1.parseGroupMessageId)(message_id);
return this.pickGroup(group_id).markRead(seq);
}
else {
const { user_id, time } = (0, message_1.parseDmMessageId)(message_id);
return this.pickUser(user_id).markRead(time);
}
}
/** @cqhttp use user.getChatHistory() or group.getChatHistory() */
async getMsg(message_id) {
return (await this.getChatHistory(message_id, 1)).pop();
}
/** @cqhttp use user.getChatHistory() or group.getChatHistory() */
async getChatHistory(message_id, count = 20) {
if (message_id.length > 24) {
const { group_id, seq } = (0, message_1.parseGroupMessageId)(message_id);
return this.pickGroup(group_id).getChatHistory(seq, count);
}
else {
const { user_id, time } = (0, message_1.parseDmMessageId)(message_id);
return this.pickUser(user_id).getChatHistory(time, count);
}
}
/** @cqhttp use group.muteAnony() */
async setGroupAnonymousBan(group_id, flag, duration = 1800) {
return this.pickGroup(group_id).muteAnony(flag, duration);
}
/** @cqhttp use group.allowAnony() */
async setGroupAnonymous(group_id, enable = true) {
return this.pickGroup(group_id).allowAnony(enable);
}
/** @cqhttp use group.muteAll() */
async setGroupWholeBan(group_id, enable = true) {
return this.pickGroup(group_id).muteAll(enable);
}
/** @cqhttp use group.setName() */
async setGroupName(group_id, name) {
return this.pickGroup(group_id).setName(name);
}
/** @cqhttp use group.announce() */
async sendGroupNotice(group_id, content) {
return this.pickGroup(group_id).announce(content);
}
/** @cqhttp use group.setAdmin() or member.setAdmin() */
async setGroupAdmin(group_id, user_id, enable = true) {
return this.pickMember(group_id, user_id).setAdmin(enable);
}
/** @cqhttp use group.setSpecialTitle() or member.setSpecialTitle() */
async setGroupSpecialTitle(group_id, user_id, special_title, duration = -1) {
return this.pickMember(group_id, user_id).setTitle(special_title, duration);
}
/** @cqhttp use group.setCard() or member.setCard() */
async setGroupCard(group_id, user_id, card) {
return this.pickMember(group_id, user_id).setCard(card);
}
/** @cqhttp use group.kickMember() or member.kick() */
async setGroupKick(group_id, user_id, reject_add_request = false) {
return this.pickMember(group_id, user_id).kick(reject_add_request);
}
/** @cqhttp use group.muteMember() or member.mute() */
async setGroupBan(group_id, user_id, duration = 1800) {
return this.pickMember(group_id, user_id).mute(duration);
}
/** @cqhttp use group.quit() */
async setGroupLeave(group_id) {
return this.pickGroup(group_id).quit();
}
/** @cqhttp use group.pokeMember() or member.poke() */
async sendGroupPoke(group_id, user_id) {
return this.pickMember(group_id, user_id).poke();
}
/** @cqhttp use member.addFriend() */
async addFriend(group_id, user_id, comment = "") {
return this.pickMember(group_id, user_id).addFriend(comment);
}
/** @cqhttp use friend.delete() */
async deleteFriend(user_id, block = true) {
return this.pickFriend(user_id).delete(block);
}
/** @cqhttp use group.invite() */
async inviteFriend(group_id, user_id) {
return this.pickGroup(group_id).invite(user_id);
}
/** @cqhttp use friend.thumbUp() */
async sendLike(user_id, times = 1) {
return this.pickFriend(user_id).thumbUp(times);
}
/** @cqhttp user client.setAvatar() */
async setPortrait(file) {
return this.setAvatar(file);
}
/** @cqhttp use group.setAvatar() */
async setGroupPortrait(group_id, file) {
return this.pickGroup(group_id).setAvatar(file);
}
/** @cqhttp use group.fs */
acquireGfs(group_id) {
return this.pickGroup(group_id).fs;
}
/** @cqhttp use user.setFriendReq() or user.addFriendBack() */
async setFriendAddRequest(flag, approve = true, remark = "", block = false) {
const { user_id, seq, single } = (0, internal_1.parseFriendRequestFlag)(flag);
const user = this.pickUser(user_id);
return single ? user.addFriendBack(seq, remark) : user.setFriendReq(seq, approve, remark, block);
}
/** @cqhttp use user.setGroupInvite() or user.setGroupReq() */
async setGroupAddRequest(flag, approve = true, reason = "", block = false) {
const { group_id, user_id, seq, invite } = (0, internal_1.parseGroupRequestFlag)(flag);
const user = this.pickUser(user_id);
return invite ? user.setGroupInvite(group_id, seq, approve, block) : user.setGroupReq(group_id, seq, approve, reason, block);
}
/** dont use it if not clear the usage */
sendOidb(cmd, body, timeout = 5) {
const sp = cmd //OidbSvc.0x568_22
.replace("OidbSvc.", "")
.replace("oidb_", "")
.split("_");
const type1 = parseInt(sp[0], 16), type2 = parseInt(sp[1]);
body = core_1.pb.encode({
1: type1,
2: isNaN(type2) ? 1 : type2,
3: 0,
4: body,
6: "android " + this.apk.ver,
});
return this.sendUni(cmd, body, timeout);
}
/** emit an event */
em(name = "", data) {
data = Object.defineProperty(data || {}, "self_id", {
value: this.uin,
writable: true,
enumerable: true,
configurable: true,
});
while (true) {
this.emit(name, data);
let i = name.lastIndexOf(".");
if (i === -1)
break;
name = name.slice(0, i);
}
}
_msgExists(from, type, seq, time) {
if ((0, common_1.timestamp)() - time >= 60 || time < this.stat.start_time)
return true;
const id = [from, type, seq].join("-");
const set = this._cache.get(time);
if (!set) {
this._cache.set(time, new Set([id]));
return false;
}
else {
if (set.has(id))
return true;
else
set.add(id);
return false;
}
}
_calcMsgCntPerMin() {
let cnt = 0;
for (let [time, set] of this._cache) {
if ((0, common_1.timestamp)() - time >= 60)
this._cache.delete(time);
else
cnt += set.size;
}
return cnt;
}
async _setProfile(k, v) {
const buf = Buffer.allocUnsafe(11 + v.length);
buf.writeUInt32BE(this.uin);
buf.writeUInt8(0, 4);
buf.writeInt32BE(k, 5);
buf.writeUInt16BE(v.length, 9);
buf.fill(v, 11);
const payload = await this.sendOidb("OidbSvc.0x4ff_9", buf);
const obj = core_1.pb.decode(payload);
return obj[3] === 0 || obj[3] === 34;
}
/** @deprecated use client.submitSlider() */
sliderLogin(ticket) {
return this.submitSlider(ticket);
}
/** @deprecated use client.sendSmsCode() */
sendSMSCode() {
return this.sendSmsCode();
}
/** @deprecated use client.submitSmsCode() */
submitSMSCode(code) {
return this.submitSmsCode(code);
}
/** @deprecated use client.status */
get online_status() {
return this.status;
}
}
exports.Client = Client;
function createDataDir(dir, uin) {
if (!fs.existsSync(dir))
fs.mkdirSync(dir, { mode: 0o755, recursive: true });
const img_path = path.join(dir, "image");
const uin_path = path.join(dir, String(uin));
if (!fs.existsSync(img_path))
fs.mkdirSync(img_path);
if (!fs.existsSync(uin_path))
fs.mkdirSync(uin_path, { mode: 0o755 });
return uin_path;
}
/** 创建一个客户端 (=new Client) */
function createClient(uin, config) {
if (isNaN(Number(uin)))
throw new Error(uin + " is not an OICQ account");
return new Client(Number(uin), config);
}
exports.createClient = createClient;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OnlineStatus = exports.MAX_UPLOAD_SIZE = exports.TMP_DIR = exports.IS_WIN = exports.PB_CONTENT = exports.DownloadTransform = exports.log = exports.escapeXml = exports.parseFunString = exports.uin2code = exports.code2uin = exports.fileHash = exports.md5Stream = exports.uuid = void 0;
const fs = __importStar(require("fs"));
const crypto = __importStar(require("crypto"));
const stream = __importStar(require("stream"));
const util = __importStar(require("util"));
const os = __importStar(require("os"));
const core_1 = require("./core");
function uuid() {
let hex = crypto.randomBytes(16).toString("hex");
return hex.substr(0, 8) + "-" + hex.substr(8, 4) + "-" + hex.substr(12, 4) + "-" + hex.substr(16, 4) + "-" + hex.substr(20);
}
exports.uuid = uuid;
/** 计算流的md5 */
function md5Stream(readable) {
return new Promise((resolve, reject) => {
readable.on("error", reject);
readable.pipe(crypto.createHash("md5")
.on("error", reject)
.on("data", resolve));
});
}
exports.md5Stream = md5Stream;
/** 计算文件的md5和sha */
function fileHash(filepath) {
const readable = fs.createReadStream(filepath);
const sha = new Promise((resolve, reject) => {
readable.on("error", reject);
readable.pipe(crypto.createHash("sha1")
.on("error", reject)
.on("data", resolve));
});
return Promise.all([md5Stream(readable), sha]);
}
exports.fileHash = fileHash;
/** 群号转uin */
function code2uin(code) {
let left = Math.floor(code / 1000000);
if (left >= 0 && left <= 10)
left += 202;
else if (left >= 11 && left <= 19)
left += 469;
else if (left >= 20 && left <= 66)
left += 2080;
else if (left >= 67 && left <= 156)
left += 1943;
else if (left >= 157 && left <= 209)
left += 1990;
else if (left >= 210 && left <= 309)
left += 3890;
else if (left >= 310 && left <= 334)
left += 3490;
else if (left >= 335 && left <= 386) //335 336不确定
left += 2265;
else if (left >= 387 && left <= 499)
left += 3490;
return left * 1000000 + code % 1000000;
}
exports.code2uin = code2uin;
/** uin转群号 */
function uin2code(uin) {
let left = Math.floor(uin / 1000000);
if (left >= 202 && left <= 212)
left -= 202;
else if (left >= 480 && left <= 488)
left -= 469;
else if (left >= 2100 && left <= 2146)
left -= 2080;
else if (left >= 2010 && left <= 2099)
left -= 1943;
else if (left >= 2147 && left <= 2199)
left -= 1990;
else if (left >= 2600 && left <= 2651)
left -= 2265;
else if (left >= 3800 && left <= 3989)
left -= 3490;
else if (left >= 4100 && left <= 4199)
left -= 3890;
return left * 1000000 + uin % 1000000;
}
exports.uin2code = uin2code;
/** 解析彩色群名片 */
function parseFunString(buf) {
if (buf[0] === 0xA) {
let res = "";
try {
let arr = core_1.pb.decode(buf)[1];
if (!Array.isArray(arr))
arr = [arr];
for (let v of arr) {
if (v[2])
res += String(v[2]);
}
}
catch { }
return res;
}
else {
return String(buf);
}
}
exports.parseFunString = parseFunString;
/** xml转义 */
function escapeXml(str) {
return str.replace(/[&"><]/g, function (s) {
if (s === "&")
return "&amp;";
if (s === "<")
return "&lt;";
if (s === ">")
return "&gt;";
if (s === "\"")
return "&quot;";
return "";
});
}
exports.escapeXml = escapeXml;
function log(any) {
if (any instanceof Buffer)
any = any.toString("hex").replace(/(.)(.)/g, '$1$2 ');
console.log(util.inspect(any, { depth: 20, showHidden: false, maxArrayLength: 1000, maxStringLength: 20000 }));
}
exports.log = log;
/** 用于下载限量 */
class DownloadTransform extends stream.Transform {
constructor() {
super(...arguments);
this._size = 0;
}
_transform(data, encoding, callback) {
this._size += data.length;
let error = null;
if (this._size <= exports.MAX_UPLOAD_SIZE)
this.push(data);
else
error = new Error("downloading over 30MB is refused");
callback(error);
}
}
exports.DownloadTransform = DownloadTransform;
exports.PB_CONTENT = core_1.pb.encode({ 1: 1, 2: 0, 3: 0 });
exports.IS_WIN = os.platform().includes("win");
/** 系统临时目录,用于临时存放下载的图片等内容 */
exports.TMP_DIR = os.tmpdir();
/** 最大上传和下载大小,以图片上传限制为准:30MB */
exports.MAX_UPLOAD_SIZE = 31457280;
/** 可设置的在线状态 */
var OnlineStatus;
(function (OnlineStatus) {
OnlineStatus[OnlineStatus["Online"] = 11] = "Online";
OnlineStatus[OnlineStatus["Absent"] = 31] = "Absent";
OnlineStatus[OnlineStatus["Invisible"] = 41] = "Invisible";
OnlineStatus[OnlineStatus["Busy"] = 50] = "Busy";
OnlineStatus[OnlineStatus["Qme"] = 60] = "Qme";
OnlineStatus[OnlineStatus["DontDisturb"] = 70] = "DontDisturb";
})(OnlineStatus = exports.OnlineStatus || (exports.OnlineStatus = {}));
__exportStar(require("./core/constants"), exports);
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FACE_OLD_BUF = exports.pokemap = exports.facemap = void 0;
exports.facemap = {
0: "惊讶",
1: "撇嘴",
2: "色",
3: "发呆",
4: "得意",
5: "流泪",
6: "害羞",
7: "闭嘴",
8: "睡",
9: "大哭",
10: "尴尬",
11: "发怒",
12: "调皮",
13: "呲牙",
14: "微笑",
15: "难过",
16: "酷",
18: "抓狂",
19: "吐",
20: "偷笑",
21: "可爱",
22: "白眼",
23: "傲慢",
24: "饥饿",
25: "困",
26: "惊恐",
27: "流汗",
28: "憨笑",
29: "悠闲",
30: "奋斗",
31: "咒骂",
32: "疑问",
33: "嘘",
34: "晕",
35: "折磨",
36: "衰",
37: "骷髅",
38: "敲打",
39: "再见",
41: "发抖",
42: "爱情",
43: "跳跳",
46: "猪头",
49: "拥抱",
53: "蛋糕",
54: "闪电",
55: "炸弹",
56: "刀",
57: "足球",
59: "便便",
60: "咖啡",
61: "饭",
63: "玫瑰",
64: "凋谢",
66: "爱心",
67: "心碎",
69: "礼物",
74: "太阳",
75: "月亮",
76: "赞",
77: "踩",
78: "握手",
79: "胜利",
85: "飞吻",
86: "怄火",
89: "西瓜",
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: "右太极",
136: "双喜",
137: "鞭炮",
138: "灯笼",
140: "K歌",
144: "喝彩",
145: "祈祷",
146: "爆筋",
147: "棒棒糖",
148: "喝奶",
151: "飞机",
158: "钞票",
168: "药",
169: "手枪",
171: "茶",
172: "眨眼睛",
173: "泪奔",
174: "无奈",
175: "卖萌",
176: "小纠结",
177: "喷血",
178: "斜眼笑",
180: "惊喜",
181: "骚扰",
182: "笑哭",
183: "我最美",
184: "河蟹",
185: "羊驼",
187: "幽灵",
188: "蛋",
190: "菊花",
192: "红包",
193: "大笑",
194: "不开心",
197: "冷漠",
198: "呃",
199: "好棒",
200: "拜托",
201: "点赞",
202: "无聊",
203: "托脸",
204: "吃",
205: "送花",
206: "害怕",
207: "花痴",
208: "小样儿",
210: "飙泪",
211: "我不看",
212: "托腮",
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: "口罩护体",
260: "/搬砖中",
261: "/忙到飞起",
262: "/脑阔疼",
263: "/沧桑",
264: "/捂脸",
265: "/辣眼睛",
266: "/哦哟",
267: "/头秃",
268: "/问号脸",
269: "/暗中观察",
270: "/emm",
271: "/吃瓜",
272: "/呵呵哒",
273: "/我酸了",
274: "/太南了",
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: "/打call",
312: "/变形",
313: "/嗑到了",
314: "/仔细分析",
315: "/加油",
316: "/我没事",
317: "/菜狗",
318: "/崇拜",
319: "/比心",
320: "/庆祝",
321: "/老色痞",
322: "/拒绝",
323: "/嫌弃",
324: "/吃糖",
};
exports.pokemap = {
0: "回戳",
1: "戳一戳",
2: "比心",
3: "点赞",
4: "心碎",
5: "666",
6: "放大招",
2000: "敲门",
2001: "抓一下",
2002: "碎屏",
2003: "勾引",
2004: "手雷",
2005: "结印",
2006: "召唤术",
2007: "玫瑰花",
2009: "让你皮",
2011: "宝贝球",
};
exports.FACE_OLD_BUF = Buffer.from([0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0xF5, 0xD0]);
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Image = exports.parseImageFileParam = exports.buildImageFileParam = void 0;
const stream_1 = require("stream");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const crypto_1 = require("crypto");
const probe_image_size_1 = __importDefault(require("probe-image-size"));
const axios_1 = __importDefault(require("axios"));
const common_1 = require("../common");
const TYPE = {
jpg: 1000,
png: 1001,
webp: 1002,
bmp: 1005,
gif: 2000,
face: 4,
};
const EXT = {
3: "png",
4: "face",
1000: "jpg",
1001: "png",
1002: "webp",
1003: "jpg",
1005: "bmp",
2000: "gif",
2001: "png",
};
/** 构造图片file */
function buildImageFileParam(md5, size, width, height, type) {
size = size || 0;
width = width || 0;
height = height || 0;
const ext = EXT[type] || "jpg";
return md5 + size + "-" + width + "-" + height + "." + ext;
}
exports.buildImageFileParam = buildImageFileParam;
/** 从图片的file中解析出图片属性参数 */
function parseImageFileParam(file) {
let md5, size, width, height, ext;
let sp = file.split("-");
md5 = sp[0].slice(0, 32);
size = Number(sp[0].slice(32)) | 0;
width = Number(sp[1]) | 0;
height = Number(sp[2]) | 0;
sp = file.split(".");
ext = sp[1] || "jpg";
return { md5, size, width, height, ext };
}
exports.parseImageFileParam = parseImageFileParam;
class Image {
/** @param dm 是否私聊图片 */
constructor(elem, dm = false, cachedir) {
this.dm = dm;
this.cachedir = cachedir;
/** 最终用于发送的对象 */
this.proto = {};
/** 图片属性 */
this.md5 = (0, crypto_1.randomBytes)(16);
this.size = 0xffff;
this.width = 320;
this.height = 240;
this.type = 1000;
let { file, cache, timeout, headers, asface, origin } = elem;
this.origin = origin;
this.asface = asface;
this.setProto();
if (file instanceof Buffer) {
this.task = this.fromProbeSync(file);
}
else if (file instanceof stream_1.Readable) {
this.task = this.fromReadable(file);
}
else if (typeof file !== "string") {
throw new Error("bad file param: " + file);
}
else if (file.startsWith("base64://")) {
this.task = this.fromProbeSync(Buffer.from(file.slice(9), "base64"));
}
else if (file.startsWith("http://") || file.startsWith("https://")) {
this.task = this.fromWeb(file, cache, headers, timeout);
}
else {
this.task = this.fromLocal(file);
}
}
/** 从服务端拿到fid后必须设置此值,否则图裂 */
set fid(val) {
this._fid = val;
if (this.dm) {
this.proto[3] = val;
this.proto[10] = val;
}
else {
this.proto[7] = val;
}
}
setProperties(dimensions) {
if (!dimensions)
throw new Error("bad image file");
this.width = dimensions.width;
this.height = dimensions.height;
this.type = TYPE[dimensions.type] || 1000;
}
parseFileParam(file) {
const { md5, size, width, height, ext } = parseImageFileParam(file);
const hash = Buffer.from(md5, "hex");
if (hash.length !== 16)
throw new Error("bad file param: " + file);
this.md5 = hash;
size > 0 && (this.size = size);
this.width = width;
this.height = height;
TYPE[ext] & (this.type = TYPE[ext]);
this.setProto();
}
async fromProbeSync(buf) {
const dimensions = probe_image_size_1.default.sync(buf);
this.setProperties(dimensions);
this.md5 = (0, common_1.md5)(buf);
this.size = buf.length;
this.readable = stream_1.Readable.from(buf, { objectMode: false });
this.setProto();
}
async fromReadable(readable, timeout) {
try {
readable = readable.pipe(new common_1.DownloadTransform);
timeout = timeout > 0 ? timeout : 60;
this.tmpfile = path_1.default.join(common_1.TMP_DIR, (0, common_1.uuid)());
var id = setTimeout(() => {
readable.destroy();
}, timeout * 1000);
const [dimensions, md5] = await Promise.all([
// @ts-ignore
(0, probe_image_size_1.default)(readable, true),
(0, common_1.md5Stream)(readable),
(0, common_1.pipeline)(readable, fs_1.default.createWriteStream(this.tmpfile)),
]);
this.setProperties(dimensions);
this.md5 = md5;
this.size = (await fs_1.default.promises.stat(this.tmpfile)).size;
this.readable = fs_1.default.createReadStream(this.tmpfile, { highWaterMark: 1024 * 256 });
this.setProto();
}
catch (e) {
this.deleteTmpFile();
throw e;
}
finally {
clearTimeout(id);
}
}
async fromWeb(url, cache, headers, timeout) {
if (this.cachedir) {
this.cachefile = path_1.default.join(this.cachedir, (0, common_1.md5)(url).toString("hex"));
if (cache) {
try {
this.parseFileParam(await fs_1.default.promises.readFile(this.cachefile, "utf8"));
return;
}
catch { }
}
}
const readable = (await axios_1.default.get(url, {
headers,
maxRedirects: 3,
responseType: "stream",
})).data;
await this.fromReadable(readable, timeout);
this.cachefile && fs_1.default.writeFile(this.cachefile, buildImageFileParam(this.md5.toString("hex"), this.size, this.width, this.height, this.type), common_1.NOOP);
}
async fromLocal(file) {
try {
//收到的图片
this.parseFileParam(file);
}
catch {
//本地图片
file.startsWith("file://") && (file = file.slice(7));
common_1.IS_WIN && file.startsWith("/") && (file = file.slice(1));
const stat = await fs_1.default.promises.stat(file);
if (stat.size <= 0 || stat.size > common_1.MAX_UPLOAD_SIZE)
throw new Error("bad file size: " + stat.size);
const readable = fs_1.default.createReadStream(file);
const [dimensions, md5] = await Promise.all([
// @ts-ignore
(0, probe_image_size_1.default)(readable, true),
(0, common_1.md5Stream)(readable)
]);
readable.destroy();
this.setProperties(dimensions);
this.md5 = md5;
this.size = stat.size;
this.readable = fs_1.default.createReadStream(file, { highWaterMark: 1024 * 256 });
this.setProto();
}
}
setProto() {
let proto;
if (this.dm) {
proto = {
1: this.md5.toString("hex"),
2: this.size,
3: this._fid,
5: this.type,
7: this.md5,
8: this.height,
9: this.width,
10: this._fid,
13: this.origin ? 1 : 0,
16: this.type === 4 ? 5 : 0,
24: 0,
25: 0,
29: {
1: this.asface ? 1 : 0
},
};
}
else {
proto = {
2: this.md5.toString("hex") + ".gif",
7: this._fid,
8: 0,
9: 0,
10: 66,
12: 1,
13: this.md5,
// 17: 3,
20: this.type,
22: this.width,
23: this.height,
24: 200,
25: this.size,
26: this.origin ? 1 : 0,
29: 0,
30: 0,
34: {
1: this.asface ? 1 : 0
},
};
}
Object.assign(this.proto, proto);
}
/** 服务端图片失效时建议调用此函数 */
deleteCacheFile() {
this.cachefile && fs_1.default.unlink(this.cachefile, common_1.NOOP);
}
/** 图片上传完成后建议调用此函数(文件存在系统临时目录中) */
deleteTmpFile() {
this.tmpfile && fs_1.default.unlink(this.tmpfile, common_1.NOOP);
this.readable?.destroy();
}
}
exports.Image = Image;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildMusic = void 0;
const axios_1 = __importDefault(require("axios"));
async function getQQSong(id) {
let rsp = await axios_1.default.get(`https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&data={"comm":{"ct":24,"cv":0},"songinfo":{"method":"get_song_detail_yqq","param":{"song_type":0,"song_mid":"","song_id":${id}},"module":"music.pf_song_detail_svr"}}`, { responseType: "json" });
rsp = rsp.data.songinfo.data.track_info;
let mid = rsp.mid, title = rsp.name, album = rsp.album.mid, singer = rsp.singer?.[0]?.name || "unknown";
rsp = await axios_1.default.get(`http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={"comm":{"ct":23,"cv":0},"url_mid":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"4311206557","songmid":["${mid}"],"songtype":[0],"uin":"0","loginflag":1,"platform":"23"}}}&_=1599039471576`, { responseType: "json" });
rsp = rsp.data.url_mid.data.midurlinfo[0];
return {
title: title,
singer: singer,
jumpUrl: `https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=${mid}&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare`,
musicUrl: rsp.purl,
preview: `http://y.gtimg.cn/music/photo_new/T002R180x180M000${album}.jpg`,
};
}
async function get163Song(id) {
let rsp = await axios_1.default.get(`http://music.163.com/api/song/detail/?id=${id}&ids=[${id}]`, { responseType: "json" });
rsp = rsp.data.songs[0];
return {
title: rsp.name,
singer: rsp.artists[0].name,
jumpUrl: "https://y.music.163.com/m/song/" + id,
musicUrl: "http://music.163.com/song/media/outer/url?id=" + id,
preview: rsp.album.picUrl,
};
}
async function getMiGuSong(id) {
let rsp = await axios_1.default.get(`https://c.musicapp.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?copyrightId=${id}&resourceType=2`, { responseType: "json" });
rsp = rsp.data.resource[0];
let preview = "";
try {
let a = await axios_1.default.get(`https://music.migu.cn/v3/api/music/audioPlayer/getSongPic?songId=${rsp.songId}`, { responseType: "json", headers: { referer: "https://music.migu.cn/v3/music/player/audio" } });
preview = a.data.smallPic || "";
}
catch { }
let url = await axios_1.default.get(`https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/shareInfo.do?contentId=${rsp.contentId}&contentName=${rsp.songName}&resourceType=2&targetUserName=${rsp.singer}`, { responseType: "json" });
let jumpUrl = url.data.url || "http://c.migu.cn/";
return {
title: rsp.songName,
singer: rsp.singer,
jumpUrl,
musicUrl: rsp.newRateFormats ? rsp.newRateFormats[0].url.replace(/ftp:\/\/[^/]+/, "https://freetyst.nf.migu.cn") : rsp.rateFormats[0].url.replace(/ftp:\/\/[^/]+/, "https://freetyst.nf.migu.cn"),
preview: preview || "",
};
}
async function getKuGouSong(id) {
let url = `https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=&hash=${id}&dfid=&mid=${id}&platid=4&_=${+new Date()}&album_id=`;
let rsp = await axios_1.default.get(url, { responseType: "json" });
rsp = rsp.data.data;
url += rsp.album_id;
rsp = await axios_1.default.get(url, { responseType: "json" });
rsp = rsp.data.data;
return {
title: rsp.audio_name,
singer: rsp.author_name,
jumpUrl: `https://www.kugou.com/song/#hash=${id}&album_id=${rsp.album_id}`,
musicUrl: rsp.play_url || "https://webfs.yun.kugou.com",
preview: rsp.img,
};
}
async function getKuwoSong(id) {
let rsp = await axios_1.default.get(`http://yinyue.kuwo.cn/api/www/music/musicInfo?mid=${id}&httpsStatus=1`, { responseType: "json", headers: { csrf: id, cookie: " kw_token=" + id } });
rsp = rsp.data.data;
let url = await axios_1.default.get(`http://yinyue.kuwo.cn/url?format=mp3&rid=${id}&response=url&type=convert_url3&from=web&t=${+new Date()}`, { responseType: "json" });
return {
title: rsp.name,
singer: rsp.artist,
jumpUrl: "http://yinyue.kuwo.cn/play_detail/" + id,
musicUrl: url.data.url || "https://win-web-ra01-sycdn.kuwo.cn",
preview: rsp.pic,
};
}
/** 构造b77音乐分享 */
async function buildMusic(target, platform, id, bu) {
var appid, appname, appsign, style = 4;
try {
if (platform == "qq") {
appid = 100497308, appname = "com.tencent.qqmusic", appsign = "cbd27cd7c861227d013a25b2d10f0799";
var { singer, title, jumpUrl, musicUrl, preview } = await getQQSong(id);
if (!musicUrl)
style = 0;
}
else if (platform == "163") {
appid = 100495085, appname = "com.netease.cloudmusic", appsign = "da6b069da1e2982db3e386233f68d76d";
var { singer, title, jumpUrl, musicUrl, preview } = await get163Song(id);
}
else if (platform == "migu") {
appid = 1101053067, appname = "cmccwm.mobilemusic", appsign = "6cdc72a439cef99a3418d2a78aa28c73";
var { singer, title, jumpUrl, musicUrl, preview } = await getMiGuSong(id);
}
else if (platform == "kugou") {
appid = 205141, appname = "com.kugou.android", appsign = "fe4a24d80fcf253a00676a808f62c2c6";
var { singer, title, jumpUrl, musicUrl, preview } = await getKuGouSong(id);
}
else if (platform == "kuwo") {
appid = 100243533, appname = "cn.kuwo.player", appsign = "bf9ff4ffb4c558a34ee3fd52c223ebf5";
var { singer, title, jumpUrl, musicUrl, preview } = await getKuwoSong(id);
}
else {
throw new Error("unknown music platform: " + platform);
}
}
catch (e) {
throw new Error("unknown music id: " + id + ", in platform: " + platform);
}
return {
1: appid,
2: 1,
3: style,
5: {
1: 1,
2: "0.0.0",
3: appname,
4: appsign
},
10: bu,
11: target,
12: {
10: title,
11: singer,
12: "[分享]" + title,
13: jumpUrl,
14: preview,
16: musicUrl,
}
};
}
exports.buildMusic = buildMusic;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGroupImageUrl = exports.Parser = exports.parse = void 0;
const zlib_1 = require("zlib");
const core_1 = require("../core");
const face_1 = require("./face");
const image_1 = require("./image");
/** 解析消息 */
function parse(rich, uin) {
return new Parser(rich, uin);
}
exports.parse = parse;
/** 消息解析器 */
class Parser {
constructor(rich, uin) {
this.uin = uin;
this.message = [];
this.brief = "";
this.content = "";
this.atme = false;
this.atall = false;
this.exclusive = false;
if (Array.isArray(rich)) {
this.parseElems(rich);
}
else {
if (rich[4])
this.parseExclusiveElem(0, rich[4]);
this.parseElems(Array.isArray(rich[2]) ? rich[2] : [rich[2]]);
}
}
/** 获取下一个节点的文本 */
getNextText() {
try {
const elem = this.it?.next().value[1][1];
return String(elem[1]);
}
catch {
return "[未知]";
}
}
/** 解析: xml, json, ptt, video, flash, file, shake, poke */
parseExclusiveElem(type, proto) {
let elem;
let brief;
switch (type) {
case 12: //xml
case 51: //json
const buf = proto[1].toBuffer();
elem = {
type: type === 12 ? "xml" : "json",
data: String(buf[0] > 0 ? (0, zlib_1.unzipSync)(buf.slice(1)) : buf.slice(1)),
id: proto[2]
};
brief = elem.type + "消息";
this.content = elem.data;
break;
case 3: //flash
elem = this.parseImgElem(proto, "flash");
brief = "闪照";
this.content = `{flash:${elem.file.slice(0, 32).toUpperCase()}}`;
break;
case 0: //ptt
elem = {
type: "record",
file: "protobuf://" + proto.toBase64(),
url: "",
md5: proto[4].toHex(),
size: proto[6] || 0,
seconds: proto[19] || 0,
};
if (proto[20]) {
const url = String(proto[20]);
elem.url = url.startsWith("http") ? url : "https://grouptalk.c2c.qq.com" + url;
}
brief = "语音";
this.content = `{ptt:${elem.url}}`;
break;
case 19: //video
elem = {
type: "video",
file: "protobuf://" + proto.toBase64(),
name: proto[3]?.toString() || "",
fid: String(proto[1]),
md5: proto[2].toBase64(),
size: proto[6] || 0,
seconds: proto[5] || 0,
};
brief = "视频";
this.content = `{video:${elem.fid}}`;
break;
case 5: //transElem
const trans = core_1.pb.decode(proto[2].toBuffer().slice(3))[7][2];
elem = {
type: "file",
name: String(trans[4]),
fid: String(trans[2]).replace("/", ""),
md5: String(trans[8]),
size: trans[3],
duration: trans[5],
};
brief = "群文件";
this.content = `{file:${elem.fid}}`;
break;
case 126: //poke
if (!proto[3])
return;
const pokeid = proto[3] === 126 ? proto[2][4] : proto[3];
elem = {
type: "poke",
id: pokeid,
text: face_1.pokemap[pokeid]
};
brief = face_1.pokemap[pokeid];
this.content = `{poke:${elem.id}}`;
break;
default:
return;
}
this.message = [elem];
this.brief = "[" + brief + "]";
this.exclusive = true;
}
/** 解析: text, at, face, bface, sface, image, mirai */
parsePartialElem(type, proto) {
let elem;
let brief = "";
let content = "";
switch (type) {
case 1: //text&at
brief = String(proto[1]);
const buf = proto[3]?.toBuffer();
if (buf && buf[1] === 1) {
elem = {
type: "at",
qq: 0,
text: brief
};
if (buf[6] === 1) {
elem.qq = "all";
this.atall = true;
}
else {
elem.qq = buf.readUInt32BE(7);
if (elem.qq === this.uin)
this.atme = true;
}
brief = brief || ("@" + elem.qq);
content = `{at:${elem.qq}}`;
}
else {
if (!brief)
return;
content = brief;
elem = {
type: "text",
text: brief
};
}
break;
case 2: //face
elem = {
type: "face",
id: proto[1],
text: face_1.facemap[proto[1]] || "表情",
};
brief = `[${elem.text}]`;
content = `{face:${elem.id}}`;
break;
case 33: //face(id>255)
elem = {
type: "face",
id: proto[1],
text: face_1.facemap[proto[1]],
};
if (!elem.text)
elem.text = proto[2] ? String(proto[2]) : ("/" + elem.id);
brief = `[${elem.text}]`;
content = `{face:${elem.id}}`;
break;
case 6: //bface
brief = this.getNextText();
if (brief.includes("骰子") || brief.includes("猜拳")) {
elem = {
type: brief.includes("骰子") ? "dice" : "rps",
id: proto[12].toBuffer()[16] - 0x30 + 1
};
content = `{${elem.type}:${elem.id}}`;
}
else {
elem = {
type: "bface",
file: proto[4].toHex() + proto[7].toHex() + proto[5],
text: brief.replace(/[[\]]/g, "")
};
content = `{bface:${elem.text}}`;
}
break;
case 4:
case 8:
elem = this.parseImgElem(proto, "image");
brief = elem.asface ? "[动画表情]" : "[图片]";
content = `{image:${elem.file.slice(0, 32).toUpperCase()}}`;
break;
case 34: //sface
brief = this.getNextText();
elem = {
type: "sface",
id: proto[1],
text: brief.replace(/[[\]]/g, ""),
};
content = `{sface:${elem.id}}`;
break;
case 31: //mirai
if (proto[3] === 103904510) {
brief = content = String(proto[2]);
elem = {
type: "mirai",
data: brief,
};
}
else {
return;
}
break;
default:
return;
}
// 删除回复中多余的AT元素
if (this.message.length === 2 && elem.type === "at" && this.message[0]?.type === "at" && this.message[1]?.type === "text") {
if (this.message[0].qq === elem.qq && this.message[1].text === " ") {
this.message.splice(0, 2);
this.brief = "";
}
}
this.brief += brief;
this.content += content;
if (!Array.isArray(this.message))
this.message = [];
const prev = this.message[this.message.length - 1];
if (elem.type === "text" && prev?.type === "text")
prev.text += elem.text;
else
this.message.push(elem);
}
parseElems(arr) {
this.it = arr.entries();
while (true) {
let wrapper = this.it.next().value?.[1];
if (!wrapper)
break;
const type = Number(Object.keys(Reflect.getPrototypeOf(wrapper))[0]);
const proto = wrapper[type];
if (type === 16) { //extraInfo
this.extra = proto;
}
else if (type === 21) { //anonGroupMsg
this.anon = proto;
}
else if (type === 45) { //sourceMsg
this.quotation = proto;
}
else if (!this.exclusive) {
switch (type) {
case 1: //text
case 2: //face
case 4: //notOnlineImage
case 6: //bface
case 8: //customFace
case 31: //mirai
case 34: //sface
this.parsePartialElem(type, proto);
break;
case 5: //transElem
case 12: //xml
case 19: //video
case 51: //json
this.parseExclusiveElem(type, proto);
break;
case 53: //commonElem
if (proto[1] === 3) { //flash
this.parseExclusiveElem(3, proto[2][1] ? proto[2][1] : proto[2][2]);
}
else if (proto[1] === 33) { //face(id>255)
this.parsePartialElem(33, proto[2]);
}
else if (proto[1] === 2) { //poke
this.parseExclusiveElem(126, proto);
}
break;
default:
break;
}
}
}
}
parseImgElem(proto, type) {
let elem;
if (proto[7]?.toHex) {
elem = {
type,
file: (0, image_1.buildImageFileParam)(proto[7].toHex(), proto[2], proto[9], proto[8], proto[5]),
url: "",
};
if (proto[15])
elem.url = `https://c2cpicdw.qpic.cn${proto[15]}`;
else if (proto[10])
elem.url = `https://c2cpicdw.qpic.cn/offpic_new/0/${proto[10]}/0`;
if (elem.type === "image")
elem.asface = proto[29]?.[1] === 1;
}
else { //群图
elem = {
type,
file: (0, image_1.buildImageFileParam)(proto[13].toHex(), proto[25], proto[22], proto[23], proto[20]),
url: proto[16] ? `https://gchat.qpic.cn${proto[16]}` : getGroupImageUrl(proto[13].toHex()),
};
if (elem.type === "image")
elem.asface = proto[34]?.[1] === 1;
}
return elem;
}
}
exports.Parser = Parser;
function getGroupImageUrl(md5) {
return `https://gchat.qpic.cn/gchatpic_new/0/0-0-${md5.toUpperCase()}/0`;
}
exports.getGroupImageUrl = getGroupImageUrl;
+1
-1
{
"name": "oicq",
"version": "2.1.7",
"version": "2.1.8",
"upday": "2021/12/08",

@@ -5,0 +5,0 @@ "description": "QQ protocol!",