mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-16 04:45:46 +00:00
chore: NTAPI
This commit is contained in:
parent
588ea7978e
commit
cd45f7051c
53
src/core/apis/collection.ts
Normal file
53
src/core/apis/collection.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { napCatCore } from "..";
|
||||
|
||||
export class NTQQCollectionApi {
|
||||
static async createCollection(authorUin: string, authorUid: string, authorName: string, brief: string, rawData: string) {
|
||||
let param = {
|
||||
commInfo: {
|
||||
bid: 1,
|
||||
category: 2,
|
||||
author: {
|
||||
type: 1,
|
||||
numId: authorUin,
|
||||
strId: authorName,
|
||||
groupId: '0',
|
||||
groupName: '',
|
||||
uid: authorUid
|
||||
},
|
||||
customGroupId: '0',
|
||||
createTime: Date.now().toString(),
|
||||
sequence: Date.now().toString()
|
||||
},
|
||||
richMediaSummary: {
|
||||
originalUri: '',
|
||||
publisher: '',
|
||||
richMediaVersion: 0,
|
||||
subTitle: '',
|
||||
title: '',
|
||||
brief: brief,
|
||||
picList: [],
|
||||
contentType: 1
|
||||
},
|
||||
richMediaContent: {
|
||||
rawData: rawData,
|
||||
bizDataList: [],
|
||||
picList: [],
|
||||
fileList: []
|
||||
},
|
||||
need_share_url: false
|
||||
};
|
||||
return napCatCore.session.getCollectionService().createNewCollectionItem(param);
|
||||
}
|
||||
static async getAllCollection(category: number = 0, count: number = 50) {
|
||||
let param = {
|
||||
category: category,
|
||||
groupId: -1,
|
||||
forceSync: true,
|
||||
forceFromDb: false,
|
||||
timeStamp: "0",
|
||||
count: count,
|
||||
searchDown: true
|
||||
};
|
||||
return napCatCore.session.getCollectionService().getCollectionItemList(param);
|
||||
}
|
||||
}
|
341
src/core/apis/file.ts
Normal file
341
src/core/apis/file.ts
Normal file
@ -0,0 +1,341 @@
|
||||
import {
|
||||
CacheFileListItem,
|
||||
CacheFileType,
|
||||
ChatCacheListItemBasic,
|
||||
ChatType,
|
||||
ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, Peer, PicElement, RawMessage
|
||||
} from '@/core/entities';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import fsPromises from 'fs/promises';
|
||||
import { log, logDebug, logError } from '@/common/utils/log';
|
||||
import { GeneralCallResult, napCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
|
||||
import { calculateFileMD5 } from '@/common/utils/file';
|
||||
import * as fileType from 'file-type';
|
||||
import imageSize from 'image-size';
|
||||
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
||||
import { sessionConfig } from '@/core/sessionConfig';
|
||||
import { rkeyManager } from '../utils/rkey';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
|
||||
import { selfInfo } from '../data';
|
||||
|
||||
|
||||
export class NTQQFileApi {
|
||||
static async getFileType(filePath: string) {
|
||||
return fileType.fileTypeFromFile(filePath);
|
||||
}
|
||||
|
||||
static async copyFile(filePath: string, destPath: string) {
|
||||
await napCatCore.util.copyFile(filePath, destPath);
|
||||
}
|
||||
|
||||
static async getFileSize(filePath: string): Promise<number> {
|
||||
return await napCatCore.util.getFileSize(filePath);
|
||||
}
|
||||
static async getVideoUrl(peer: Peer, msgId: string, elementId: string) {
|
||||
return (await napCatCore.session.getRichMediaService().getVideoPlayUrlV2(peer, msgId, elementId, 0, { downSourceType: 1, triggerType: 1 })).urlResult.domainUrl;
|
||||
}
|
||||
// 上传文件到QQ的文件夹
|
||||
static async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
|
||||
// napCatCore.wrapper.util.
|
||||
const fileMd5 = await calculateFileMD5(filePath);
|
||||
let ext: string = (await NTQQFileApi.getFileType(filePath))?.ext as string || '';
|
||||
if (ext) {
|
||||
ext = '.' + ext;
|
||||
}
|
||||
let fileName = `${path.basename(filePath)}`;
|
||||
if (fileName.indexOf('.') === -1) {
|
||||
fileName += ext;
|
||||
}
|
||||
const mediaPath = napCatCore.session.getMsgService().getRichMediaFilePathForGuild({
|
||||
md5HexStr: fileMd5,
|
||||
fileName: fileName,
|
||||
elementType: elementType,
|
||||
elementSubType,
|
||||
thumbSize: 0,
|
||||
needCreate: true,
|
||||
downloadType: 1,
|
||||
file_uuid: ''
|
||||
});
|
||||
await NTQQFileApi.copyFile(filePath, mediaPath!);
|
||||
const fileSize = await NTQQFileApi.getFileSize(filePath);
|
||||
return {
|
||||
md5: fileMd5,
|
||||
fileName,
|
||||
path: mediaPath,
|
||||
fileSize,
|
||||
ext
|
||||
};
|
||||
}
|
||||
static async downloadMediaByUuid() {
|
||||
//napCatCore.session.getRichMediaService().downloadFileForFileUuid();
|
||||
}
|
||||
static async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) {
|
||||
//logDebug('receive downloadMedia task', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
|
||||
// 用于下载收到的消息中的图片等
|
||||
if (sourcePath && fs.existsSync(sourcePath)) {
|
||||
if (force) {
|
||||
try {
|
||||
await fsPromises.unlink(sourcePath);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
} else {
|
||||
return sourcePath;
|
||||
}
|
||||
}
|
||||
let data = await NTEventDispatch.CallNormalEvent<
|
||||
(
|
||||
params: {
|
||||
fileModelId: string,
|
||||
downloadSourceType: number,
|
||||
triggerType: number,
|
||||
msgId: string,
|
||||
chatType: ChatType,
|
||||
peerUid: string,
|
||||
elementId: string,
|
||||
thumbSize: number,
|
||||
downloadType: number,
|
||||
filePath: string
|
||||
}) => Promise<unknown>,
|
||||
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/downloadRichMedia',
|
||||
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||
1,
|
||||
timeout,
|
||||
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||
if (arg.msgId === msgId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
{
|
||||
fileModelId: '0',
|
||||
downloadSourceType: 0,
|
||||
triggerType: 1,
|
||||
msgId: msgId,
|
||||
chatType: chatType,
|
||||
peerUid: peerUid,
|
||||
elementId: elementId,
|
||||
thumbSize: 0,
|
||||
downloadType: 1,
|
||||
filePath: thumbPath
|
||||
}
|
||||
);
|
||||
let filePath = data[1].filePath;
|
||||
if (filePath.startsWith('\\')) {
|
||||
// log('filePath start with \\');
|
||||
const downloadPath = sessionConfig.defaultFileDownloadPath;
|
||||
//logDebug('downloadPath', downloadPath);
|
||||
filePath = path.join(downloadPath, filePath);
|
||||
// 下载路径是下载文件夹的相对路径
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
static async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
|
||||
return new Promise((resolve, reject) => {
|
||||
imageSize(filePath, (err, dimensions) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(dimensions);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
static async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
|
||||
let GroupData;
|
||||
let BuddyData;
|
||||
if (peer.chatType === ChatType.group) {
|
||||
GroupData =
|
||||
[{
|
||||
groupCode: peer.peerUid,
|
||||
isConf: false,
|
||||
hasModifyConfGroupFace: true,
|
||||
hasModifyConfGroupName: true,
|
||||
groupName: "NapCat.Cached",
|
||||
remark: "NapCat.Cached"
|
||||
}];
|
||||
} else if (peer.chatType === ChatType.friend) {
|
||||
BuddyData = [{
|
||||
category_name: 'NapCat.Cached',
|
||||
peerUid: peer.peerUid,
|
||||
peerUin: peer.peerUid,
|
||||
remark: 'NapCat.Cached'
|
||||
}];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return napCatCore.session.getSearchService().addSearchHistory({
|
||||
type: 4,
|
||||
contactList: [],
|
||||
id: -1,
|
||||
groupInfos: [],
|
||||
msgs: [],
|
||||
fileInfos: [
|
||||
{
|
||||
chatType: peer.chatType,
|
||||
buddyChatInfo: BuddyData || [],
|
||||
discussChatInfo: [],
|
||||
groupChatInfo: GroupData || [],
|
||||
dataLineChatInfo: [],
|
||||
tmpChatInfo: [],
|
||||
msgId: msgId,
|
||||
msgSeq: msgSeq,
|
||||
msgTime: Math.floor(Date.now() / 1000).toString(),
|
||||
senderUid: senderUid,
|
||||
senderNick: 'NapCat.Cached',
|
||||
senderRemark: 'NapCat.Cached',
|
||||
senderCard: 'NapCat.Cached',
|
||||
elemId: elemId,
|
||||
elemType: elemType,
|
||||
fileSize: fileSize,
|
||||
filePath: '',
|
||||
fileName: fileName,
|
||||
hits: [{
|
||||
start: 12,
|
||||
end: 14
|
||||
}]
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
static async searchfile(keys: string[]) {
|
||||
type EventType = NodeIKernelSearchService['searchFileWithKeywords'];
|
||||
interface OnListener {
|
||||
searchId: string,
|
||||
hasMore: boolean,
|
||||
resultItems: {
|
||||
chatType: ChatType,
|
||||
buddyChatInfo: any[],
|
||||
discussChatInfo: any[],
|
||||
groupChatInfo:
|
||||
{
|
||||
groupCode: string,
|
||||
isConf: boolean,
|
||||
hasModifyConfGroupFace: boolean,
|
||||
hasModifyConfGroupName: boolean,
|
||||
groupName: string,
|
||||
remark: string
|
||||
}[]
|
||||
,
|
||||
dataLineChatInfo: any[],
|
||||
tmpChatInfo: any[],
|
||||
msgId: string,
|
||||
msgSeq: string,
|
||||
msgTime: string,
|
||||
senderUid: string,
|
||||
senderNick: string,
|
||||
senderRemark: string,
|
||||
senderCard: string,
|
||||
elemId: string,
|
||||
elemType: number,
|
||||
fileSize: string,
|
||||
filePath: string,
|
||||
fileName: string,
|
||||
hits:
|
||||
{
|
||||
start: number,
|
||||
end: number
|
||||
}[]
|
||||
}[]
|
||||
};
|
||||
const Event = await NTEventDispatch.CreatEventFunction<EventType>('NodeIKernelSearchService/searchFileWithKeywords');
|
||||
let id = '';
|
||||
const Listener = NTEventDispatch.RegisterListen<(params: OnListener) => void>('NodeIKernelSearchListener/onSearchFileKeywordsResult', 1, 20000, (params) => {
|
||||
if (id !== '' && params.searchId == id) {
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
});
|
||||
id = await Event!(keys, 12);
|
||||
let [ret] = (await Listener);
|
||||
return ret;
|
||||
}
|
||||
static async getImageUrl(element: PicElement) {
|
||||
if (!element) {
|
||||
return '';
|
||||
}
|
||||
const url: string = element.originImageUrl!; // 没有域名
|
||||
const md5HexStr = element.md5HexStr;
|
||||
const fileMd5 = element.md5HexStr;
|
||||
const fileUuid = element.fileUuid;
|
||||
|
||||
if (url) {
|
||||
let UrlParse = new URL(IMAGE_HTTP_HOST + url);//临时解析拼接
|
||||
let imageAppid = UrlParse.searchParams.get('appid');
|
||||
let isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid);
|
||||
if (isNewPic) {
|
||||
let UrlRkey = UrlParse.searchParams.get('rkey');
|
||||
if (UrlRkey) {
|
||||
return IMAGE_HTTP_HOST_NT + url;
|
||||
}
|
||||
const rkeyData = await rkeyManager.getRkey();
|
||||
UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
|
||||
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`;
|
||||
} else {
|
||||
// 老的图片url,不需要rkey
|
||||
return IMAGE_HTTP_HOST + url;
|
||||
}
|
||||
} else if (fileMd5 || md5HexStr) {
|
||||
// 没有url,需要自己拼接
|
||||
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`;
|
||||
}
|
||||
logDebug('图片url获取失败', element);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export class NTQQFileCacheApi {
|
||||
static async setCacheSilentScan(isSilent: boolean = true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
static getCacheSessionPathList() {
|
||||
return '';
|
||||
}
|
||||
|
||||
static clearCache(cacheKeys: Array<string> = ['tmp', 'hotUpdate']) {
|
||||
// 参数未验证
|
||||
return napCatCore.session.getStorageCleanService().clearCacheDataByKeys(cacheKeys);
|
||||
}
|
||||
|
||||
static addCacheScannedPaths(pathMap: object = {}) {
|
||||
return napCatCore.session.getStorageCleanService().addCacheScanedPaths(pathMap);
|
||||
}
|
||||
|
||||
static scanCache(): Promise<GeneralCallResult & {
|
||||
size: string[]
|
||||
}> {
|
||||
// 需要注册Listener onFinishScan
|
||||
return napCatCore.session.getStorageCleanService().scanCache();
|
||||
}
|
||||
|
||||
static getHotUpdateCachePath() {
|
||||
// 未实现
|
||||
return '';
|
||||
}
|
||||
|
||||
static getDesktopTmpPath() {
|
||||
// 未实现
|
||||
return '';
|
||||
}
|
||||
|
||||
static getChatCacheList(type: ChatType, pageSize: number = 1000, pageIndex: number = 0) {
|
||||
return napCatCore.session.getStorageCleanService().getChatCacheInfo(type, pageSize, 1, pageIndex);
|
||||
}
|
||||
|
||||
static getFileCacheInfo(fileType: CacheFileType, pageSize: number = 1000, lastRecord?: CacheFileListItem) {
|
||||
const _lastRecord = lastRecord ? lastRecord : { fileType: fileType };
|
||||
//需要五个参数
|
||||
//return napCatCore.session.getStorageCleanService().getFileCacheInfo();
|
||||
}
|
||||
|
||||
static async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) {
|
||||
return napCatCore.session.getStorageCleanService().clearChatCacheInfo(chats, fileKeys);
|
||||
}
|
||||
}
|
97
src/core/apis/friend.ts
Normal file
97
src/core/apis/friend.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { FriendRequest, FriendV2, SimpleInfo, User } from '@/core/entities';
|
||||
import { BuddyListReqType, napCatCore, NodeIKernelBuddyListener, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { LimitedHashTable } from '@/common/utils/MessageUnique';
|
||||
import { CacheClassFuncAsyncExtend } from '@/common/utils/helper';
|
||||
export class NTQQFriendApi {
|
||||
static async getBuddyV2(refresh = false): Promise<FriendV2[]> {
|
||||
let uids: string[] = [];
|
||||
const buddyService = napCatCore.session.getBuddyService();
|
||||
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||
);
|
||||
return Array.from(data.values());
|
||||
}
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, 'getBuddyIdMap', () => true)
|
||||
static async getBuddyIdMapCache(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||
return await NTQQFriendApi.getBuddyIdMap(refresh);
|
||||
}
|
||||
static async getBuddyIdMap(refresh = false): Promise<LimitedHashTable<string, string>> {
|
||||
let uids: string[] = [];
|
||||
let retMap: LimitedHashTable<string, string> = new LimitedHashTable<string, string>(5000);
|
||||
const buddyService = napCatCore.session.getBuddyService();
|
||||
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
|
||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||
);
|
||||
data.forEach((value, key) => {
|
||||
retMap.set(value.uin!, value.uid!);
|
||||
});
|
||||
//console.log('getBuddyIdMap', retMap.getValue);
|
||||
return retMap;
|
||||
}
|
||||
static async getBuddyV2ExWithCate(refresh = false) {
|
||||
let uids: string[] = [];
|
||||
let categoryMap: Map<string, any> = new Map();
|
||||
const buddyService = napCatCore.session.getBuddyService();
|
||||
const buddyListV2 = refresh ? (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data : (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data;
|
||||
uids.push(
|
||||
...buddyListV2.flatMap(item => {
|
||||
item.buddyUids.forEach(uid => {
|
||||
categoryMap.set(uid, { categoryId: item.categoryId, categroyName: item.categroyName });
|
||||
});
|
||||
return item.buddyUids
|
||||
}));
|
||||
const data = await NTEventDispatch.CallNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
|
||||
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids
|
||||
);
|
||||
return Array.from(data).map(([key, value]) => {
|
||||
const category = categoryMap.get(key);
|
||||
return category ? { ...value, categoryId: category.categoryId, categroyName: category.categroyName } : value;
|
||||
});
|
||||
}
|
||||
static async isBuddy(uid: string) {
|
||||
return napCatCore.session.getBuddyService().isBuddy(uid);
|
||||
}
|
||||
/**
|
||||
* @deprecated
|
||||
* @param forced
|
||||
* @returns
|
||||
*/
|
||||
static async getFriends(forced = false): Promise<User[]> {
|
||||
let [_retData, _BuddyArg] = await NTEventDispatch.CallNormalEvent
|
||||
<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
|
||||
(
|
||||
'NodeIKernelBuddyService/getBuddyList',
|
||||
'NodeIKernelBuddyListener/onBuddyListChange',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
forced
|
||||
);
|
||||
const friends: User[] = [];
|
||||
for (const categoryItem of _BuddyArg) {
|
||||
for (const friend of categoryItem.buddyList) {
|
||||
friends.push(friend);
|
||||
}
|
||||
}
|
||||
return friends;
|
||||
}
|
||||
|
||||
static async handleFriendRequest(flag: string, accept: boolean) {
|
||||
let data = flag.split('|');
|
||||
if (data.length < 2) {
|
||||
return;
|
||||
}
|
||||
let friendUid = data[0];
|
||||
let reqTime = data[1];
|
||||
napCatCore.session.getBuddyService()?.approvalFriendRequest({
|
||||
friendUid: friendUid,
|
||||
reqTime: reqTime,
|
||||
accept
|
||||
});
|
||||
}
|
||||
}
|
327
src/core/apis/group.ts
Normal file
327
src/core/apis/group.ts
Normal file
@ -0,0 +1,327 @@
|
||||
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType, GroupNotifyTypes, ChatType, Peer, GroupListUpdateType } from '../entities';
|
||||
import { GeneralCallResult, NTQQUserApi, NodeIKernelGroupListener, NodeIKernelGroupService, napCatCore } from '@/core';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { log } from '@/common/utils/log';
|
||||
import { groupMembers } from '../data';
|
||||
import { CacheClassFuncAsyncExtend, runAllWithTimeout } from '@/common/utils/helper';
|
||||
export class NTQQGroupApi {
|
||||
static async setGroupAvatar(gc: string, filePath: string) {
|
||||
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||
}
|
||||
static async getGroups(forced = false) {
|
||||
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
|
||||
let [_retData, _updateType, groupList] = await NTEventDispatch.CallNormalEvent
|
||||
<(force: boolean) => Promise<any>, ListenerType>
|
||||
(
|
||||
'NodeIKernelGroupService/getGroupList',
|
||||
'NodeIKernelGroupListener/onGroupListUpdate',
|
||||
1,
|
||||
5000,
|
||||
(updateType) => true,
|
||||
forced
|
||||
);
|
||||
return groupList;
|
||||
}
|
||||
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, "LastestSendTime", () => true)
|
||||
static async getGroupMemberLastestSendTimeCache(GroupCode: string) {
|
||||
return NTQQGroupApi.getGroupMemberLastestSendTime(GroupCode);
|
||||
}
|
||||
/**
|
||||
* 通过QQ自带数据库获取群成员最后发言时间(仅返回有效数据 且消耗延迟大 需要进行缓存)
|
||||
* @param GroupCode 群号
|
||||
* @returns Map<string, string> key: uin value: sendTime
|
||||
* @example
|
||||
* let ret = await NTQQGroupApi.getGroupMemberLastestSendTime('123456');
|
||||
* for (let [uin, sendTime] of ret) {
|
||||
* console.log(uin, sendTime);
|
||||
* }
|
||||
*/
|
||||
static async getGroupMemberLastestSendTime(GroupCode: string) {
|
||||
async function getdata(uid: string) {
|
||||
let NTRet = await NTQQGroupApi.getLastestMsgByUids(GroupCode, [uid]);
|
||||
if (NTRet.result != 0 && NTRet.msgList.length < 1) {
|
||||
return undefined;
|
||||
}
|
||||
return { sendUin: NTRet.msgList[0].senderUin, sendTime: NTRet.msgList[0].msgTime }
|
||||
}
|
||||
let currentGroupMembers = groupMembers.get(GroupCode);
|
||||
let PromiseData: Promise<({
|
||||
sendUin: string;
|
||||
sendTime: string;
|
||||
} | undefined)>[] = [];
|
||||
let ret: Map<string, string> = new Map();
|
||||
if (!currentGroupMembers) {
|
||||
return ret;
|
||||
}
|
||||
for (let member of currentGroupMembers.values()) {
|
||||
PromiseData.push(getdata(member.uid).catch(() => undefined));
|
||||
}
|
||||
let allRet = await runAllWithTimeout(PromiseData, 2500);
|
||||
for (let PromiseDo of allRet) {
|
||||
if (PromiseDo) {
|
||||
ret.set(PromiseDo.sendUin, PromiseDo.sendTime);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static async getLastestMsgByUids(GroupCode: string, uids: string[]) {
|
||||
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||
chatInfo: {
|
||||
peerUid: GroupCode,
|
||||
chatType: ChatType.group,
|
||||
},
|
||||
filterMsgType: [],
|
||||
filterSendersUid: uids,
|
||||
filterMsgToTime: '0',
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: false,
|
||||
isIncludeCurrent: true,
|
||||
pageLimit: 1,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
static async getGroupMemberAll(GroupCode: string, forced = false) {
|
||||
return napCatCore.session.getGroupService().getAllMemberList(GroupCode, forced);
|
||||
}
|
||||
static async getLastestMsg(GroupCode: string, uins: string[]) {
|
||||
let uids: Array<string> = [];
|
||||
for (let uin of uins) {
|
||||
let uid = await NTQQUserApi.getUidByUin(uin)
|
||||
if (uid) {
|
||||
uids.push(uid);
|
||||
}
|
||||
}
|
||||
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||
chatInfo: {
|
||||
peerUid: GroupCode,
|
||||
chatType: ChatType.group,
|
||||
},
|
||||
filterMsgType: [],
|
||||
filterSendersUid: uids,
|
||||
filterMsgToTime: '0',
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: false,
|
||||
isIncludeCurrent: true,
|
||||
pageLimit: 1,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
static async getGroupRecommendContactArkJson(GroupCode: string) {
|
||||
return napCatCore.session.getGroupService().getGroupRecommendContactArkJson(GroupCode);
|
||||
}
|
||||
static async CreatGroupFileFolder(groupCode: string, folderName: string) {
|
||||
return napCatCore.session.getRichMediaService().createGroupFolder(groupCode, folderName);
|
||||
}
|
||||
static async DelGroupFile(groupCode: string, files: string[]) {
|
||||
return napCatCore.session.getRichMediaService().deleteGroupFile(groupCode, [102], files);
|
||||
}
|
||||
static async DelGroupFileFolder(groupCode: string, folderId: string) {
|
||||
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
||||
}
|
||||
static async addGroupEssence(GroupCode: string, msgId: string) {
|
||||
// 代码没测过
|
||||
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||
let param = {
|
||||
groupCode: GroupCode,
|
||||
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||
};
|
||||
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||
return napCatCore.session.getGroupService().addGroupEssence(param);
|
||||
}
|
||||
static async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||
// 代码没测过
|
||||
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||
let param = {
|
||||
groupCode: GroupCode,
|
||||
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||
};
|
||||
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||
return napCatCore.session.getGroupService().removeGroupEssence(param);
|
||||
}
|
||||
static async getSingleScreenNotifies(num: number) {
|
||||
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
|
||||
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
||||
(
|
||||
'NodeIKernelGroupService/getSingleScreenNotifies',
|
||||
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
||||
1,
|
||||
5000,
|
||||
() => true,
|
||||
false,
|
||||
'',
|
||||
num
|
||||
);
|
||||
return notifies;
|
||||
}
|
||||
static async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
|
||||
type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
|
||||
type EventType = NodeIKernelGroupService['getMemberInfo'];
|
||||
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
|
||||
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
|
||||
const [ret, _groupCode, _changeType, _members] = await NTEventDispatch.CallNormalEvent
|
||||
<EventType, ListenerType>
|
||||
(
|
||||
'NodeIKernelGroupService/getMemberInfo',
|
||||
'NodeIKernelGroupListener/onMemberInfoChange',
|
||||
1,
|
||||
5000,
|
||||
(groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
|
||||
return groupCode == GroupCode && members.has(uid);
|
||||
},
|
||||
GroupCode, [uid], forced
|
||||
);
|
||||
return _members.get(uid);
|
||||
}
|
||||
static async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||
const groupService = napCatCore.session.getGroupService();
|
||||
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
||||
const result = await groupService.getNextMemberList(sceneId!, undefined, num);
|
||||
if (result.errCode !== 0) {
|
||||
throw ('获取群成员列表出错,' + result.errMsg);
|
||||
}
|
||||
|
||||
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
|
||||
return result.result.infos;
|
||||
/*
|
||||
console.log(sceneId);
|
||||
const result = await napCatCore.getGroupService().getNextMemberList(sceneId, num);
|
||||
console.log(result);
|
||||
|
||||
return result;
|
||||
*/
|
||||
}
|
||||
|
||||
static async getGroupNotifies() {
|
||||
// 获取管理员变更
|
||||
// 加群通知,退出通知,需要管理员权限
|
||||
|
||||
}
|
||||
static async GetGroupFileCount(Gids: Array<string>) {
|
||||
return napCatCore.session.getRichMediaService().batchGetGroupFileCount(Gids);
|
||||
}
|
||||
static async getGroupIgnoreNotifies() {
|
||||
}
|
||||
static async getArkJsonGroupShare(GroupCode: string) {
|
||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
||||
<(GroupId: string) => Promise<GeneralCallResult & { arkJson: string }>>(
|
||||
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
|
||||
5000,
|
||||
GroupCode
|
||||
);
|
||||
return ret.arkJson;
|
||||
}
|
||||
//需要异常处理
|
||||
static async uploadGroupBulletinPic(GroupCode: string, imageurl: string) {
|
||||
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
||||
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
||||
}
|
||||
static async handleGroupRequest(flag: string, operateType: GroupRequestOperateTypes, reason?: string) {
|
||||
let flagitem = flag.split('|');
|
||||
let groupCode = flagitem[0];
|
||||
let seq = flagitem[1];
|
||||
let type = parseInt(flagitem[2]);
|
||||
|
||||
return napCatCore.session.getGroupService().operateSysNotify(
|
||||
false,
|
||||
{
|
||||
'operateType': operateType, // 2 拒绝
|
||||
'targetMsg': {
|
||||
'seq': seq, // 通知序列号
|
||||
'type': type,
|
||||
'groupCode': groupCode,
|
||||
'postscript': reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static async quitGroup(groupQQ: string) {
|
||||
return napCatCore.session.getGroupService().quitGroup(groupQQ);
|
||||
}
|
||||
|
||||
static async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
|
||||
return napCatCore.session.getGroupService().kickMember(groupQQ, kickUids, refuseForever, kickReason);
|
||||
}
|
||||
|
||||
static async banMember(groupQQ: string, memList: Array<{ uid: string, timeStamp: number }>) {
|
||||
// timeStamp为秒数, 0为解除禁言
|
||||
return napCatCore.session.getGroupService().setMemberShutUp(groupQQ, memList);
|
||||
}
|
||||
|
||||
static async banGroup(groupQQ: string, shutUp: boolean) {
|
||||
return napCatCore.session.getGroupService().setGroupShutUp(groupQQ, shutUp);
|
||||
}
|
||||
|
||||
static async setMemberCard(groupQQ: string, memberUid: string, cardName: string) {
|
||||
return napCatCore.session.getGroupService().modifyMemberCardName(groupQQ, memberUid, cardName);
|
||||
}
|
||||
|
||||
static async setMemberRole(groupQQ: string, memberUid: string, role: GroupMemberRole) {
|
||||
return napCatCore.session.getGroupService().modifyMemberRole(groupQQ, memberUid, role);
|
||||
}
|
||||
|
||||
static async setGroupName(groupQQ: string, groupName: string) {
|
||||
return napCatCore.session.getGroupService().modifyGroupName(groupQQ, groupName, false);
|
||||
}
|
||||
|
||||
// 头衔不可用
|
||||
static async setGroupTitle(groupQQ: string, uid: string, title: string) {
|
||||
|
||||
}
|
||||
|
||||
static async publishGroupBulletin(groupQQ: string, content: string, picInfo: { id: string, width: number, height: number } | undefined = undefined, pinned: number = 0, confirmRequired: number = 0,) {
|
||||
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com');
|
||||
//text是content内容url编码
|
||||
let data = {
|
||||
text: encodeURI(content),
|
||||
picInfo: picInfo,
|
||||
oldFeedsId: '',
|
||||
pinned: pinned,
|
||||
confirmRequired: confirmRequired
|
||||
};
|
||||
return napCatCore.session.getGroupService().publishGroupBulletin(groupQQ, _Pskey!, data);
|
||||
}
|
||||
static async getGroupRemainAtTimes(GroupCode: string) {
|
||||
napCatCore.session.getGroupService().getGroupRemainAtTimes(GroupCode);
|
||||
}
|
||||
static async getMemberExtInfo(groupCode: string, uin: string) {
|
||||
// 仅NTQQ 9.9.11 24568测试 容易炸开谨慎使用
|
||||
return napCatCore.session.getGroupService().getMemberExtInfo(
|
||||
{
|
||||
groupCode: groupCode,
|
||||
sourceType: MemberExtSourceType.TITLETYPE,
|
||||
beginUin: '0',
|
||||
dataTime: '0',
|
||||
uinList: [uin],
|
||||
uinNum: '',
|
||||
seq: '',
|
||||
groupType: '',
|
||||
richCardNameVer: '',
|
||||
memberExtFilter: {
|
||||
memberLevelInfoUin: 1,
|
||||
memberLevelInfoPoint: 1,
|
||||
memberLevelInfoActiveDay: 1,
|
||||
memberLevelInfoLevel: 1,
|
||||
memberLevelInfoName: 1,
|
||||
levelName: 1,
|
||||
dataTime: 1,
|
||||
userShowFlag: 1,
|
||||
sysShowFlag: 1,
|
||||
timeToUpdate: 1,
|
||||
nickName: 1,
|
||||
specialTitle: 1,
|
||||
levelNameNew: 1,
|
||||
userShowFlagNew: 1,
|
||||
msgNeedField: 1,
|
||||
cmdUinFlagExt3Grocery: 1,
|
||||
memberIcon: 1,
|
||||
memberInfoSeq: 1
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
8
src/core/apis/index.ts
Normal file
8
src/core/apis/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export * from './file';
|
||||
export * from './friend';
|
||||
export * from './group';
|
||||
export * from './msg';
|
||||
export * from './user';
|
||||
export * from './webapi';
|
||||
export * from './sign';
|
||||
export * from './system';
|
317
src/core/apis/msg.ts
Normal file
317
src/core/apis/msg.ts
Normal file
@ -0,0 +1,317 @@
|
||||
import { GetFileListParam, Peer, RawMessage, SendMessageElement, SendMsgElementConstructor } from '@/core/entities';
|
||||
import { friends, groups, selfInfo } from '@/core/data';
|
||||
import { log, logWarn } from '@/common/utils/log';
|
||||
import { sleep } from '@/common/utils/helper';
|
||||
import { napCatCore, NTQQUserApi } from '@/core';
|
||||
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
||||
import { GeneralCallResult } from '@/core/services/common';
|
||||
import { MessageUnique } from '../../../common/utils/MessageUnique';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
async function LoadMessageIdList(Peer: Peer, msgId: string) {
|
||||
let msgList = await NTQQMsgApi.getMsgHistory(Peer, msgId, 50);
|
||||
for (let j = 0; j < msgList.msgList.length; j++) {
|
||||
let shortId = MessageUnique.createMsg(Peer, msgList.msgList[j].msgId);
|
||||
}
|
||||
}
|
||||
async function loadMessageUnique() {
|
||||
if (groups.size > 100) {
|
||||
logWarn('[性能检测] 群数量大于100,可能会导致性能问题');
|
||||
}
|
||||
let predict = (groups.size + friends.size / 2) / 5;
|
||||
predict = predict < 20 ? 20 : predict;
|
||||
predict = predict > 50 ? 50 : predict;
|
||||
//let waitpromise: Array<Promise<{ msgList: RawMessage[]; }>> = [];
|
||||
predict = Math.floor(predict * 50);
|
||||
MessageUnique.resize(predict);
|
||||
let RecentContact = await NTQQUserApi.getRecentContactListSnapShot(predict);
|
||||
let LoadMessageIdDo: Array<Promise<void>> = new Array<Promise<void>>();
|
||||
if (RecentContact?.info?.changedList && RecentContact?.info?.changedList?.length > 0) {
|
||||
for (let i = 0; i < RecentContact.info.changedList.length; i++) {
|
||||
let Peer: Peer = { chatType: RecentContact.info.changedList[i].chatType, peerUid: RecentContact.info.changedList[i].peerUid, guildId: '' };
|
||||
LoadMessageIdDo.push(LoadMessageIdList(Peer, RecentContact.info.changedList[i].msgId));
|
||||
}
|
||||
}
|
||||
await Promise.all(LoadMessageIdDo).then(() => {
|
||||
log(`[消息序列] 加载 ${predict} 条历史消息记录完成`);
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
napCatCore.onLoginSuccess(async () => {
|
||||
await sleep(100);
|
||||
// NTQQMsgApi.CheckSendMode().then().catch();
|
||||
loadMessageUnique().then().catch();
|
||||
//下面的代码还没摸清 不要使用
|
||||
//let data = await napCatCore.session.getMsgService().sendSsoCmdReqByContend("LightAppSvc.mini_app_growguard.ReportExecute","1124343");
|
||||
//console.log(data);
|
||||
});
|
||||
}, 100);
|
||||
//歇菜LocalMsg压根不写Db
|
||||
// setTimeout(async () => {
|
||||
// let ele: MessageElement = { extBufForUI: '0x', ...SendMsgElementConstructor.text('测试消息') };
|
||||
// let MsgId = await NTQQMsgApi.getMsgUniqueEx();
|
||||
// let peer = { chatType: 2, peerUid: '', guildId: '' };
|
||||
// console.log(await napCatCore.session.getTestPerformanceService().insertMsg(
|
||||
// {
|
||||
// peer: peer,
|
||||
// msgTime: Math.floor(Date.now() / 1000).toString(),
|
||||
// msgId: MsgId,
|
||||
// msgSeq: '56564',
|
||||
// batchNums: 1,
|
||||
// timesPerBatch: 1,
|
||||
// numPerTime: 1
|
||||
// }, [ele]
|
||||
// ));
|
||||
// console.log(await NTQQMsgApi.multiForwardMsg(peer, peer, [MsgId]));
|
||||
// }, 25000)
|
||||
|
||||
export class NTQQMsgApi {
|
||||
static async FetchLongMsg(peer: Peer, msgId: string) {
|
||||
return napCatCore.session.getMsgService().fetchLongMsg(peer, msgId);
|
||||
}
|
||||
static async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
|
||||
//console.log(peer, msgSeq, emojiId, emojiType, count);
|
||||
//注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged M likiowa
|
||||
return napCatCore.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, "", false, 20)
|
||||
}
|
||||
// static napCatCore: NapCatCore | null = null;
|
||||
// enum BaseEmojiType {
|
||||
// NORMAL_EMOJI,
|
||||
// SUPER_EMOJI,
|
||||
// RANDOM_SUPER_EMOJI,
|
||||
// CHAIN_SUPER_EMOJI,
|
||||
// EMOJI_EMOJI
|
||||
// }
|
||||
static async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
||||
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
|
||||
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
||||
// 其实以官方文档为准是最好的,https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
|
||||
emojiId = emojiId.toString();
|
||||
return napCatCore.session.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set);
|
||||
}
|
||||
static async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
|
||||
msgList: RawMessage[]
|
||||
} | undefined> {
|
||||
return napCatCore.session.getMsgService().getMultiMsg(peer, rootMsgId, parentMsgId);
|
||||
}
|
||||
static async ForwardMsg(peer: Peer, msgIds: string[]) {
|
||||
return napCatCore.session.getMsgService().forwardMsg(msgIds, peer, [peer], new Map());
|
||||
}
|
||||
static async getLastestMsgByUids(peer: Peer, count: number = 20, isReverseOrder: boolean = false) {
|
||||
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||
chatInfo: peer,
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
filterMsgToTime: '0',
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: isReverseOrder,//此参数有点离谱 注意不是本次查询的排序 而是全部消历史信息的排序 默认false 从新消息拉取到旧消息
|
||||
isIncludeCurrent: true,
|
||||
pageLimit: count,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
static async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) {
|
||||
if (!peer) throw new Error('peer is not allowed');
|
||||
if (!msgIds) throw new Error('msgIds is not allowed');
|
||||
//Mlikiowa: 参数不合规会导致NC异常崩溃 原因是TX未对进入参数判断 对应Android标记@NotNull AndroidJADX分析可得
|
||||
return await napCatCore.session.getMsgService().getMsgsByMsgId(peer, msgIds);
|
||||
}
|
||||
static async getSingleMsg(peer: Peer, seq: string) {
|
||||
return await napCatCore.session.getMsgService().getSingleMsg(peer, seq);
|
||||
}
|
||||
static async fetchFavEmojiList(num: number) {
|
||||
return napCatCore.session.getMsgService().fetchFavEmojiList("", num, true, true)
|
||||
}
|
||||
static async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
|
||||
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
|
||||
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
|
||||
filterMsgType: [],
|
||||
filterSendersUid: [],
|
||||
filterMsgToTime: '0',
|
||||
filterMsgFromTime: '0',
|
||||
isReverseOrder: false,
|
||||
isIncludeCurrent: true,
|
||||
pageLimit: 1,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||
return await napCatCore.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
|
||||
}
|
||||
static async setMsgRead(peer: Peer) {
|
||||
return napCatCore.session.getMsgService().setMsgRead(peer);
|
||||
}
|
||||
static async getGroupFileList(GroupCode: string, params: GetFileListParam) {
|
||||
let data = await NTEventDispatch.CallNormalEvent<
|
||||
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
|
||||
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
|
||||
>(
|
||||
'NodeIKernelRichMediaService/getGroupFileList',
|
||||
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||
1,
|
||||
5000,
|
||||
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
||||
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
|
||||
return true;
|
||||
},
|
||||
GroupCode,
|
||||
params
|
||||
);
|
||||
return data[1].item;
|
||||
}
|
||||
static async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) {
|
||||
// 消息时间从旧到新
|
||||
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, isReverseOrder);
|
||||
}
|
||||
static async recallMsg(peer: Peer, msgIds: string[]) {
|
||||
await napCatCore.session.getMsgService().recallMsg({
|
||||
chatType: peer.chatType,
|
||||
peerUid: peer.peerUid
|
||||
}, msgIds);
|
||||
}
|
||||
static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||
function generateMsgId() {
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
const random = Math.floor(Math.random() * Math.pow(2, 32));
|
||||
const buffer = Buffer.alloc(8);
|
||||
buffer.writeUInt32BE(timestamp, 0);
|
||||
buffer.writeUInt32BE(random, 4);
|
||||
const msgId = BigInt("0x" + buffer.toString('hex')).toString();
|
||||
return msgId;
|
||||
}
|
||||
// 此处有采用Hack方法 利用数据返回正确得到对应消息
|
||||
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
|
||||
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
|
||||
let msgId: string;
|
||||
try {
|
||||
msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
|
||||
} catch (error) {
|
||||
//if (!napCatCore.session.getMsgService()['generateMsgUniqueId'])
|
||||
//兜底识别策略V2
|
||||
msgId = generateMsgId().toString();
|
||||
}
|
||||
let data = await NTEventDispatch.CallNormalEvent<
|
||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (let msgRecord of msgRecords) {
|
||||
if (msgRecord.msgId === msgId && msgRecord.sendStatus === 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
msgId,
|
||||
peer,
|
||||
msgElements,
|
||||
new Map()
|
||||
);
|
||||
let retMsg = data[1].find(msgRecord => {
|
||||
if (msgRecord.msgId === msgId) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return retMsg;
|
||||
}
|
||||
static sendMsgEx(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||
//return NTQQMsgApi.sendMsgV1(peer, msgElements, waitComplete, timeout);
|
||||
}
|
||||
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||
//唉? !我有个想法
|
||||
let msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime());
|
||||
peer.guildId = msgId;
|
||||
let data = await NTEventDispatch.CallNormalEvent<
|
||||
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/sendMsg',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
timeout,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (let msgRecord of msgRecords) {
|
||||
if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
"0",
|
||||
peer,
|
||||
msgElements,
|
||||
new Map()
|
||||
);
|
||||
let retMsg = data[1].find(msgRecord => {
|
||||
if (msgRecord.guildId === msgId) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return retMsg;
|
||||
}
|
||||
static async getMsgUnique(chatType: number, time: string) {
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return napCatCore.session.getMsgService().generateMsgUniqueId(chatType, time);
|
||||
}
|
||||
return napCatCore.session.getMsgService().getMsgUniqueId(time);
|
||||
}
|
||||
static async getServerTime() {
|
||||
return napCatCore.session.getMSFService().getServerTime();
|
||||
}
|
||||
static async getServerTimeV2() {
|
||||
return NTEventDispatch.CallNoListenerEvent<() => string>('NodeIKernelMsgService/getServerTime', 5000);
|
||||
}
|
||||
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||
return napCatCore.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
|
||||
}
|
||||
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
||||
const msgInfos = msgIds.map(id => {
|
||||
return { msgId: id, senderShowName: selfInfo.nick };
|
||||
});
|
||||
let data = await NTEventDispatch.CallNormalEvent<
|
||||
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>,) => Promise<unknown>,
|
||||
(msgList: RawMessage[]) => void
|
||||
>(
|
||||
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||
1,
|
||||
5000,
|
||||
(msgRecords: RawMessage[]) => {
|
||||
for (let msgRecord of msgRecords) {
|
||||
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == selfInfo.uid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
msgInfos,
|
||||
srcPeer,
|
||||
destPeer,
|
||||
[],
|
||||
new Map()
|
||||
);
|
||||
for (let msg of data[1]) {
|
||||
const arkElement = msg.elements.find(ele => ele.arkElement);
|
||||
if (!arkElement) {
|
||||
continue;
|
||||
}
|
||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
||||
if (forwardData.app != 'com.tencent.multimsg') {
|
||||
continue;
|
||||
}
|
||||
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
throw new Error('转发消息超时');
|
||||
}
|
||||
static async markallMsgAsRead() {
|
||||
return napCatCore.session.getMsgService().setAllC2CAndGroupMsgRead();
|
||||
}
|
||||
}
|
308
src/core/apis/sign.ts
Normal file
308
src/core/apis/sign.ts
Normal file
@ -0,0 +1,308 @@
|
||||
import { logDebug } from '@/common/utils/log';
|
||||
import { NTQQUserApi } from './user';
|
||||
import { selfInfo } from '../data';
|
||||
import { RequestUtil } from '@/common/utils/request';
|
||||
import { WebApi } from './webapi';
|
||||
import { checkFileReceived, checkFileReceived2, uri2local } from '@/common/utils/file';
|
||||
import fs from 'node:fs'
|
||||
import { sleep } from '@/common/utils/helper';
|
||||
export interface IdMusicSignPostData {
|
||||
type: 'qq' | '163',
|
||||
id: string | number,
|
||||
}
|
||||
|
||||
export interface CustomMusicSignPostData {
|
||||
type: 'custom',
|
||||
url: string,
|
||||
audio: string,
|
||||
title: string,
|
||||
image?: string,
|
||||
singer?: string
|
||||
}
|
||||
// let t = await napCatCore.session.getGroupService().shareDigest({
|
||||
// appId: "100497308",
|
||||
// appType: 1,
|
||||
// msgStyle: 0,
|
||||
// recvUin: "726067488",
|
||||
// sendType: 1,
|
||||
// clientInfo: {
|
||||
// platform: 1
|
||||
// },
|
||||
// richMsg: {
|
||||
// usingArk: true,
|
||||
// title: "Bot测试title",
|
||||
// summary: "Bot测试summary",
|
||||
// url: "https://www.bilibili.com",
|
||||
// pictureUrl: "https://y.qq.com/music/photo_new/T002R300x300M0000035DC6W4ZpSqf_1.jpg?max_age=2592000",
|
||||
// brief: "Bot测试brief",
|
||||
// }
|
||||
// });
|
||||
// {
|
||||
// errCode: 0,
|
||||
// errMsg: '',
|
||||
// rsp: {
|
||||
// sendType: 1,
|
||||
// recvUin: '726067488',
|
||||
// recvOpenId: '',
|
||||
// errCode: 901501,
|
||||
// errMsg: 'imagent service_error:150_OIDB_NO_PRIV',
|
||||
// extInfo: {
|
||||
// wording: '消息下发失败(错误码:901501)',
|
||||
// jumpResult: 0,
|
||||
// jumpUrl: '',
|
||||
// level: 0,
|
||||
// subLevel: 0,
|
||||
// developMsg: 'imagent error'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// export class MusicSign {
|
||||
// private readonly url: string;
|
||||
|
||||
// constructor(url: string) {
|
||||
// this.url = url;
|
||||
// }
|
||||
|
||||
// sign(postData: CustomMusicSignPostData | IdMusicSignPostData): Promise<any> {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// fetch(this.url, {
|
||||
// method: 'POST', // 指定请求方法为 POST
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json' // 设置请求头,指明发送的数据类型为 JSON
|
||||
// },
|
||||
// body: JSON.stringify(postData) // 将 JavaScript 对象转换为 JSON 字符串作为请求体
|
||||
// })
|
||||
// .then(response => {
|
||||
// if (!response.ok) {
|
||||
// reject(response.statusText); // 请求失败,返回错误信息
|
||||
// }
|
||||
// return response.json(); // 解析 JSON 格式的响应体
|
||||
// })
|
||||
// .then(data => {
|
||||
// logDebug('音乐消息生成成功', data);
|
||||
// resolve(data);
|
||||
// })
|
||||
// .catch(error => {
|
||||
// reject(error);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
export interface MiniAppLuaJsonType {
|
||||
prompt: string,
|
||||
title: string,
|
||||
preview: string,
|
||||
jumpUrl: string,
|
||||
tag: string,
|
||||
tagIcon: string,
|
||||
source: string,
|
||||
sourcelogo: string,
|
||||
}
|
||||
export async function SignMiniApp(CardData: MiniAppLuaJsonType) {
|
||||
// {
|
||||
// "app": "com.tencent.miniapp.lua",
|
||||
// "bizsrc": "tianxuan.imgJumpArk",
|
||||
// "view": "miniapp",
|
||||
// "prompt": "hi! 这里有我的日常故事,只想讲给你听",
|
||||
// "config": {
|
||||
// "type": "normal",
|
||||
// "forward": 1,
|
||||
// "autosize": 0
|
||||
// },
|
||||
// "meta": {
|
||||
// "miniapp": {
|
||||
// "title": "hi! 这里有我的日常故事,只想讲给你听",
|
||||
// "preview": "https:\/\/tianquan.gtimg.cn\/qqAIAgent\/item\/7\/square.png",
|
||||
// "jumpUrl": "https:\/\/club.vip.qq.com\/transfer?open_kuikly_info=%7B%22version%22%3A%20%221%22%2C%22src_type%22%3A%20%22web%22%2C%22kr_turbo_display%22%3A%20%221%22%2C%22page_name%22%3A%20%22vas_ai_persona_moments%22%2C%22bundle_name%22%3A%20%22vas_ai_persona_moments%22%7D&page_name=vas_ai_persona_moments&enteranceId=share&robot_uin=3889008584",
|
||||
// "tag": "QQ智能体",
|
||||
// "tagIcon": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png",
|
||||
// "source": "QQ智能体",
|
||||
// "sourcelogo": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// token : function(url,skey){
|
||||
// var str = skey || cookie('skey') || cookie('rv2') || '',
|
||||
// hash = 5381;
|
||||
// if(url){
|
||||
// var hostname = uri(url).hostname;
|
||||
// if(hostname.indexOf('qun.qq.com') > -1 || (hostname.indexOf('qzone.qq.com') > -1 && hostname.indexOf('qun.qzone.qq.com') === -1)){
|
||||
// str = cookie('p_skey') || str;
|
||||
// }
|
||||
// }
|
||||
// for(var i = 0, len = str.length; i < len; ++i){
|
||||
// hash += (hash << 5) + str.charAt(i).charCodeAt();
|
||||
// }
|
||||
// return hash & 0x7fffffff;
|
||||
// },
|
||||
//
|
||||
|
||||
// function signToken(skey: string) {
|
||||
// let hash = 5381;
|
||||
// for (let i = 0, len = skey.length; i < len; ++i) {
|
||||
// hash += (hash << 5) + skey.charCodeAt(i);
|
||||
// }
|
||||
// return hash & 0x7fffffff;
|
||||
// }
|
||||
let signCard = {
|
||||
"app": "com.tencent.miniapp.lua",
|
||||
"bizsrc": "tianxuan.imgJumpArk",
|
||||
"view": "miniapp",
|
||||
"prompt": CardData.prompt,
|
||||
"config": {
|
||||
"type": "normal",
|
||||
"forward": 1,
|
||||
"autosize": 0
|
||||
},
|
||||
"meta": {
|
||||
"miniapp": {
|
||||
"title": CardData.title,
|
||||
"preview": (CardData.preview as string).replace(/\\/g, "\\/\\/"),
|
||||
"jumpUrl": (CardData.jumpUrl as string).replace(/\\/g, "\\/\\/"),
|
||||
"tag": CardData.tag,
|
||||
"tagIcon": (CardData.tagIcon as string).replace(/\\/g, "\\/\\/"),
|
||||
"source": CardData.source,
|
||||
"sourcelogo": (CardData.sourcelogo as string).replace(/\\/g, "\\/\\/")
|
||||
}
|
||||
}
|
||||
};
|
||||
// let signCard = {
|
||||
// "app": "com.tencent.eventshare.lua",
|
||||
// "prompt": "Bot Test",
|
||||
// "bizsrc": "tianxuan.business",
|
||||
// "meta": {
|
||||
// "eventshare": {
|
||||
// "button1URL": "https://www.bilibili.com",
|
||||
// "button1disable": false,
|
||||
// "button1title": "点我前往",
|
||||
// "button2URL": "",
|
||||
// "button2disable": false,
|
||||
// "button2title": "",
|
||||
// "buttonNum": 1,
|
||||
// "jumpURL": "https://www.bilibili.com",
|
||||
// "preview": "https://tianquan.gtimg.cn/shoal/card/9930bc4e-4a92-4da3-814f-8094a2421d9c.png",
|
||||
// "tag": "QQ集卡",
|
||||
// "tagIcon": "https://tianquan.gtimg.cn/shoal/card/c034854b-102d-40be-a545-5ca90a7c49c9.png",
|
||||
// "title": "Bot Test"
|
||||
// }
|
||||
// },
|
||||
// "config": {
|
||||
// "autosize": 0,
|
||||
// "collect": 0,
|
||||
// "ctime": 1716568575,
|
||||
// "forward": 1,
|
||||
// "height": 336,
|
||||
// "reply": 0,
|
||||
// "round": 1,
|
||||
// "type": "normal",
|
||||
// "width": 263
|
||||
// },
|
||||
// "view": "eventshare",
|
||||
// "ver": "0.0.0.1"
|
||||
// };
|
||||
let data = (await NTQQUserApi.getQzoneCookies());
|
||||
const Bkn = WebApi.genBkn(data.p_skey);
|
||||
|
||||
const CookieValue = 'p_skey=' + data.p_skey + '; skey=' + data.skey + '; p_uin=o' + selfInfo.uin + '; uin=o' + selfInfo.uin;
|
||||
|
||||
let signurl = "https://h5.qzone.qq.com/v2/vip/tx/trpc/ark-share/GenNewSignedArk?g_tk=" + Bkn + "&ark=" + encodeURIComponent(JSON.stringify(signCard));
|
||||
let signed_ark = "";
|
||||
try {
|
||||
let retData = await RequestUtil.HttpGetJson<{ code: number, data: { signed_ark: string } }>(signurl, 'GET', undefined, { Cookie: CookieValue });
|
||||
//logDebug('MiniApp JSON 消息生成成功', retData);
|
||||
signed_ark = retData.data.signed_ark;
|
||||
} catch (error) {
|
||||
logDebug('MiniApp JSON 消息生成失败', error);
|
||||
}
|
||||
return signed_ark;
|
||||
}
|
||||
export async function SignMusicInternal(songname: string, singer: string, cover: string, songmid: string, songmusic: string) {
|
||||
//curl -X POST 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003' -H 'Content-Type: application/json' -H 'Cookie: uin=o10086' -d '{"app":"com.tencent.qqreader.share","config":{"ctime":1718634110,"forward":1,"token":"9a63343c32d5a16bcde653eb97faa25d","type":"normal"},"extra":{"app_type":1,"appid":100497308,"msg_seq":14386738075403815000.0,"uin":1733139081},"meta":{"music":{"action":"","android_pkg_name":"","app_type":1,"appid":100497308,"ctime":1718634110,"desc":"周杰伦","jumpUrl":"https://i.y.qq.com/v8/playsong.html?songmid=0039MnYb0qxYhV&type=0","musicUrl":"http://ws.stream.qqmusic.qq.com/http://isure6.stream.qqmusic.qq.com/M800002202B43Cq4V4.mp3?fromtag=810033622&guid=br_xzg&trace=23fe7bcbe2336bbf&uin=553&vkey=CF0F5CE8B0FA16F3001F8A88D877A217EB5E4F00BDCEF1021EB6C48969CA33C6303987AEECE9CC840122DD2F917A59D6130D8A8CA4577C87","preview":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","cover":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","sourceMsgId":"0","source_icon":"https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0","source_url":"","tag":"QQ音乐","title":"晴天","uin":10086}},"prompt":"[分享]晴天","ver":"0.0.0.1","view":"music"}'
|
||||
let signurl = 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003';
|
||||
//let = "https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg";
|
||||
let signCard = {
|
||||
app: "com.tencent.qqreader.share",
|
||||
config: {
|
||||
ctime: 1718634110,
|
||||
forward: 1,
|
||||
token: "9a63343c32d5a16bcde653eb97faa25d",
|
||||
type: "normal"
|
||||
},
|
||||
extra: {
|
||||
app_type: 1,
|
||||
appid: 100497308,
|
||||
msg_seq: 14386738075403815000.0,
|
||||
uin: 1733139081
|
||||
},
|
||||
meta: {
|
||||
music:
|
||||
{
|
||||
action: "",
|
||||
android_pkg_name: "",
|
||||
app_type: 1,
|
||||
appid: 100497308,
|
||||
ctime: 1718634110,
|
||||
desc: singer,
|
||||
jumpUrl: "https://i.y.qq.com/v8/playsong.html?songmid=" + songmid + "&type=0",
|
||||
musicUrl: songmusic,
|
||||
preview: cover,
|
||||
cover: cover,
|
||||
sourceMsgId: "0",
|
||||
source_icon: "https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0",
|
||||
source_url: "",
|
||||
tag: "QQ音乐",
|
||||
title: songname,
|
||||
uin: 10086
|
||||
}
|
||||
},
|
||||
prompt: "[分享]" + songname,
|
||||
ver: "0.0.0.1",
|
||||
view: "music"
|
||||
}
|
||||
//console.log(JSON.stringify(signCard, null, 2));
|
||||
let data = await RequestUtil.HttpGetJson<{ code: number, data: { arkResult: string } }>
|
||||
(signurl, 'POST', signCard, { 'Cookie': 'uin=o10086', 'Content-Type': 'application/json' });
|
||||
return data;
|
||||
}
|
||||
//注意处理错误
|
||||
export async function CreateMusicThridWay0(id: string = '', mid: string = '') {
|
||||
if (mid == '') {
|
||||
let MusicInfo = await RequestUtil.HttpGetJson
|
||||
<{ songinfo?: { data?: { track_info: { mid: string } } } }>
|
||||
(
|
||||
'https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8¬ice=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"}}',
|
||||
'GET',
|
||||
undefined
|
||||
);
|
||||
mid = MusicInfo.songinfo?.data?.track_info.mid!;
|
||||
}
|
||||
//第三方接口 存在速率限制 现在勉强用
|
||||
let MusicReal = await RequestUtil.HttpGetJson
|
||||
<{ code: number, data?: { name: string, singer: string, url: string, cover: string } }>
|
||||
('https://api.leafone.cn/api/qqmusic?id=' + mid + '&type=8', 'GET', undefined);
|
||||
//console.log(MusicReal);
|
||||
return { ...MusicReal.data, mid: mid };
|
||||
}
|
||||
export async function CreateMusicThridWay1(id: string = '', mid: string = '') {
|
||||
|
||||
}
|
||||
//转换外域名为 https://qq.ugcimg.cn/v1/cpqcbu4b8870i61bde6k7cbmjgejq8mr3in82qir4qi7ielffv5slv8ck8g42novtmev26i233ujtuab6tvu2l2sjgtupfr389191v00s1j5oh5325j5eqi40774jv1i/khovifoh7jrqd6eahoiv7koh8o
|
||||
//https://cgi.connect.qq.com/qqconnectopen/openapi/change_image_url?url=https://th.bing.com/th?id=OSK.b8ed36f1fb1889de6dc84fd81c187773&w=46&h=46&c=11&rs=1&qlt=80&o=6&dpr=2&pid=SANGAM
|
||||
|
||||
//外域名不行得走qgroup中转
|
||||
//https://proxy.gtimg.cn/tx_tls_gate=y.qq.com/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg
|
||||
|
||||
//可外域名
|
||||
//https://pic.ugcimg.cn/500955bdd6657ecc8e82e02d2df06800/jpg1
|
||||
|
||||
//QQ音乐gtimg接口
|
||||
//https://y.gtimg.cn/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg?max_age=2592000
|
||||
|
||||
//还有一处公告上传可以上传高质量图片 持久为qq域名
|
||||
export async function SignMusicWrapper(id: string = '') {
|
||||
let MusicInfo = await CreateMusicThridWay0(id)!;
|
||||
let MusicCard = await SignMusicInternal(MusicInfo.name!, MusicInfo.singer!, MusicInfo.cover!, MusicInfo.mid!, "https://ws.stream.qqmusic.qq.com/" + MusicInfo.url!);
|
||||
return MusicCard;
|
||||
}
|
35
src/core/apis/system.ts
Normal file
35
src/core/apis/system.ts
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { GeneralCallResult, NTQQFileApi, NTQQUserApi, napCatCore } from '@/core';
|
||||
export class NTQQSystemApi {
|
||||
static async hasOtherRunningQQProcess() {
|
||||
return napCatCore.util.hasOtherRunningQQProcess();
|
||||
}
|
||||
static async ORCImage(filePath: string) {
|
||||
return napCatCore.session.getNodeMiscService().wantWinScreenOCR(filePath);
|
||||
}
|
||||
static async translateEnWordToZn(words: string[]) {
|
||||
return napCatCore.session.getRichMediaService().translateEnWordToZn(words);
|
||||
}
|
||||
//调用会超时 没灯用 (好像是通知listener的) onLineDev
|
||||
static async getOnlineDev() {
|
||||
return napCatCore.session.getMsgService().getOnLineDev();
|
||||
}
|
||||
//1-2-162b9b42-65b9-4405-a8ed-2e256ec8aa50
|
||||
static async getArkJsonCollection(cid: string) {
|
||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
||||
<(cid: string) => Promise<GeneralCallResult & { arkJson: string }>>(
|
||||
'NodeIKernelCollectionService/collectionArkShare',
|
||||
5000,
|
||||
'1717662698058'
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
static async BootMiniApp(appfile: string, params: string) {
|
||||
await napCatCore.session.getNodeMiscService().setMiniAppVersion('2.16.4');
|
||||
let c = await napCatCore.session.getNodeMiscService().getMiniAppPath();
|
||||
|
||||
return napCatCore.session.getNodeMiscService().startNewMiniApp(appfile, params);
|
||||
}
|
||||
}
|
357
src/core/apis/user.ts
Normal file
357
src/core/apis/user.ts
Normal file
@ -0,0 +1,357 @@
|
||||
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '@/core/entities';
|
||||
import { friends, groupMembers, selfInfo } from '@/core/data';
|
||||
import { CacheClassFuncAsync, CacheClassFuncAsyncExtend } from '@/common/utils/helper';
|
||||
import { napCatCore, NTQQFriendApi } from '@/core';
|
||||
import { NodeIKernelProfileListener, ProfileListener } from '@/core/listeners';
|
||||
import { RequestUtil } from '@/common/utils/request';
|
||||
import { logWarn } from '@/common/utils/log';
|
||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
|
||||
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||
|
||||
export class NTQQUserApi {
|
||||
async getProfileLike(uid: string) {
|
||||
return napCatCore.session.getProfileLikeService().getBuddyProfileLike({
|
||||
friendUids: [
|
||||
uid
|
||||
],
|
||||
basic: 1,
|
||||
vote: 1,
|
||||
favorite: 0,
|
||||
userProfile: 1,
|
||||
type: 2,
|
||||
start: 0,
|
||||
limit: 20
|
||||
});
|
||||
}
|
||||
async setLongNick(longNick: string) {
|
||||
return napCatCore.session.getProfileService().setLongNick(longNick);
|
||||
}
|
||||
async setSelfOnlineStatus(status: number, extStatus: number, batteryStatus: number) {
|
||||
return napCatCore.session.getMsgService().setStatus({ status: status, extStatus: extStatus, batteryStatus: batteryStatus });
|
||||
}
|
||||
async getBuddyRecommendContactArkJson(uin: string, sencenID = '') {
|
||||
return napCatCore.session.getBuddyService().getBuddyRecommendContactArkJson(uin, sencenID);
|
||||
}
|
||||
async like(uid: string, count = 1): Promise<{ result: number, errMsg: string, succCounts: number }> {
|
||||
return napCatCore.session.getProfileLikeService().setBuddyProfileLike({
|
||||
friendUid: uid,
|
||||
sourceId: 71,
|
||||
doLikeCount: count,
|
||||
doLikeTollCount: 0
|
||||
});
|
||||
}
|
||||
|
||||
async setQQAvatar(filePath: string) {
|
||||
type setQQAvatarRet = { result: number, errMsg: string };
|
||||
const ret = await napCatCore.session.getProfileService().setHeader(filePath) as setQQAvatarRet;
|
||||
return { result: ret?.result, errMsg: ret?.errMsg };
|
||||
}
|
||||
async setGroupAvatar(gc: string, filePath: string) {
|
||||
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||
}
|
||||
|
||||
async fetchUserDetailInfos(uids: string[]) {
|
||||
//26702 以上使用新接口 .Dev Mlikiowa
|
||||
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||
let retData: User[] = [];
|
||||
let [_retData, _retListener] = await NTEventDispatch.CallNormalEvent
|
||||
<EventService, EventListener>
|
||||
(
|
||||
'NodeIKernelProfileService/fetchUserDetailInfo',
|
||||
'NodeIKernelProfileListener/onUserDetailInfoChanged',
|
||||
uids.length,
|
||||
5000,
|
||||
(profile) => {
|
||||
if (uids.includes(profile.uid)) {
|
||||
let RetUser: User = {
|
||||
...profile.simpleInfo.coreInfo,
|
||||
...profile.simpleInfo.status,
|
||||
...profile.simpleInfo.vasInfo,
|
||||
...profile.commonExt,
|
||||
...profile.simpleInfo.baseInfo,
|
||||
qqLevel: profile.commonExt.qqLevel,
|
||||
pendantId: ""
|
||||
};
|
||||
retData.push(RetUser);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
"BuddyProfileStore",
|
||||
uids,
|
||||
UserDetailSource.KSERVER,
|
||||
[
|
||||
ProfileBizType.KALL
|
||||
]
|
||||
);
|
||||
|
||||
return retData;
|
||||
}
|
||||
async fetchUserDetailInfo(uid: string) {
|
||||
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
|
||||
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
|
||||
let [_retData, profile] = await NTEventDispatch.CallNormalEvent
|
||||
<EventService, EventListener>
|
||||
(
|
||||
'NodeIKernelProfileService/fetchUserDetailInfo',
|
||||
'NodeIKernelProfileListener/onUserDetailInfoChanged',
|
||||
1,
|
||||
5000,
|
||||
(profile) => {
|
||||
if (profile.uid === uid) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
"BuddyProfileStore",
|
||||
[
|
||||
uid
|
||||
],
|
||||
UserDetailSource.KSERVER,
|
||||
[
|
||||
ProfileBizType.KALL
|
||||
]
|
||||
);
|
||||
let RetUser: User = {
|
||||
...profile.simpleInfo.coreInfo,
|
||||
...profile.simpleInfo.status,
|
||||
...profile.simpleInfo.vasInfo,
|
||||
...profile.commonExt,
|
||||
...profile.simpleInfo.baseInfo,
|
||||
qqLevel: profile.commonExt.qqLevel,
|
||||
pendantId: ""
|
||||
};
|
||||
return RetUser;
|
||||
}
|
||||
async getUserDetailInfo(uid: string) {
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return this.fetchUserDetailInfo(uid);
|
||||
}
|
||||
return this.getUserDetailInfoOld(uid);
|
||||
}
|
||||
async getUserDetailInfoOld(uid: string) {
|
||||
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo'];
|
||||
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged'];
|
||||
let [_retData, profile] = await NTEventDispatch.CallNormalEvent
|
||||
<EventService, EventListener>
|
||||
(
|
||||
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
|
||||
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
|
||||
2,
|
||||
5000,
|
||||
(profile: User) => {
|
||||
if (profile.uid === uid) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
uid,
|
||||
[0]
|
||||
);
|
||||
return profile;
|
||||
}
|
||||
async modifySelfProfile(param: ModifyProfileParams) {
|
||||
return napCatCore.session.getProfileService().modifyDesktopMiniProfile(param);
|
||||
}
|
||||
//需要异常处理
|
||||
@CacheClassFuncAsync(1800 * 1000)
|
||||
async getCookies(domain: string) {
|
||||
const ClientKeyData = await NTQQUserApi.forceFetchClientKey();
|
||||
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + selfInfo.uin + '%2Finfocenter&keyindex=19%27'
|
||||
let cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
|
||||
return cookies;
|
||||
}
|
||||
@CacheClassFuncAsync(1800 * 1000)
|
||||
async getPSkey(domainList: string[]) {
|
||||
return await napCatCore.session.getTipOffService().getPskey(domainList, true);
|
||||
}
|
||||
async getRobotUinRange(): Promise<Array<any>> {
|
||||
const robotUinRanges = await napCatCore.session.getRobotService().getRobotUinRange({
|
||||
justFetchMsgConfig: '1',
|
||||
type: 1,
|
||||
version: 0,
|
||||
aioKeywordVersion: 0
|
||||
});
|
||||
// console.log(robotUinRanges?.response?.robotUinRanges);
|
||||
return robotUinRanges?.response?.robotUinRanges;
|
||||
}
|
||||
//需要异常处理
|
||||
@CacheClassFuncAsync(1800 * 1000)
|
||||
async getQzoneCookies() {
|
||||
const ClientKeyData = await NTQQUserApi.forceFetchClientKey();
|
||||
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2Fuser.qzone.qq.com%2F' + selfInfo.uin + '%2Finfocenter&keyindex=19%27'
|
||||
let cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
|
||||
return cookies;
|
||||
}
|
||||
//需要异常处理
|
||||
@CacheClassFuncAsync(1800 * 1000)
|
||||
async getSkey(): Promise<string | undefined> {
|
||||
const ClientKeyData = await NTQQUserApi.forceFetchClientKey();
|
||||
if (ClientKeyData.result !== 0) {
|
||||
throw new Error('getClientKey Error');
|
||||
}
|
||||
const clientKey = ClientKeyData.clientKey;
|
||||
const keyIndex = ClientKeyData.keyIndex;
|
||||
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + selfInfo.uin + '&clientkey=' + clientKey + '&u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=19%27';
|
||||
let cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
|
||||
const skey = cookies['skey'];
|
||||
if (!skey) {
|
||||
throw new Error('getSkey Skey is Empty');
|
||||
}
|
||||
return skey;
|
||||
}
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, 'Uin2Uid', (Uin: string, Uid: string | undefined) => {
|
||||
if (Uid && Uid.indexOf('u_') != -1) {
|
||||
return true
|
||||
}
|
||||
logWarn("uin转换到uid时异常", Uin, Uid);
|
||||
return false;
|
||||
})
|
||||
async getUidByUin(Uin: string) {
|
||||
//此代码仅临时使用,后期会被废弃
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return await NTQQUserApi.getUidByUinV2(Uin);
|
||||
}
|
||||
return await NTQQUserApi.getUidByUinV1(Uin);
|
||||
}
|
||||
@CacheClassFuncAsyncExtend(3600 * 1000, 'Uid2Uin', (Uid: string | undefined, Uin: number | undefined) => {
|
||||
if (Uin && Uin != 0 && !isNaN(Uin)) {
|
||||
return true
|
||||
}
|
||||
logWarn("uid转换到uin时异常", Uid, Uin);
|
||||
return false;
|
||||
})
|
||||
async getUinByUid(Uid: string) {
|
||||
//此代码仅临时使用,后期会被废弃
|
||||
if (requireMinNTQQBuild('26702')) {
|
||||
return await NTQQUserApi.getUinByUidV2(Uid);
|
||||
}
|
||||
return await NTQQUserApi.getUinByUidV1(Uid);
|
||||
}
|
||||
|
||||
//后期改成流水线处理
|
||||
async getUidByUinV2(Uin: string) {
|
||||
let uid = (await napCatCore.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
|
||||
if (uid) return uid;
|
||||
uid = (await napCatCore.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
|
||||
if (uid) return uid;
|
||||
uid = (await napCatCore.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||
if (uid) return uid;
|
||||
console.log((await NTQQFriendApi.getBuddyIdMapCache(true)));
|
||||
uid = (await NTQQFriendApi.getBuddyIdMapCache(true)).getValue(Uin);//从Buddy缓存获取Uid
|
||||
if (uid) return uid;
|
||||
uid = (await NTQQFriendApi.getBuddyIdMap(true)).getValue(Uin);
|
||||
if (uid) return uid;
|
||||
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUinV2(Uin)).detail.uid;//从QQ Native 特殊转换
|
||||
if (unveifyUid.indexOf("*") == -1) uid = unveifyUid;
|
||||
//if (uid) return uid;
|
||||
return uid;
|
||||
}
|
||||
//后期改成流水线处理
|
||||
async getUinByUidV2(Uid: string) {
|
||||
let uin = (await napCatCore.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await napCatCore.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await napCatCore.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await NTQQFriendApi.getBuddyIdMapCache(true)).getKey(Uid);//从Buddy缓存获取Uin
|
||||
if (uin) return uin;
|
||||
uin = (await NTQQFriendApi.getBuddyIdMap(true)).getKey(Uid);
|
||||
if (uin) return uin;
|
||||
uin = (await NTQQUserApi.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||
return uin;
|
||||
}
|
||||
|
||||
async getUidByUinV1(Uin: string) {
|
||||
// 通用转换开始尝试
|
||||
let uid = (await napCatCore.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
|
||||
// Uid 好友转
|
||||
if (!uid) {
|
||||
Array.from(friends.values()).forEach((t) => {
|
||||
if (t.uin == Uin) {
|
||||
uid = t.uid;
|
||||
}
|
||||
});
|
||||
}
|
||||
//Uid 群友列表转
|
||||
if (!uid) {
|
||||
for (let groupMembersList of groupMembers.values()) {
|
||||
for (let GroupMember of groupMembersList.values()) {
|
||||
if (GroupMember.uin == Uin) {
|
||||
uid = GroupMember.uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!uid) {
|
||||
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
||||
if (unveifyUid.indexOf("*") == -1) {
|
||||
uid = unveifyUid;
|
||||
}
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
async getUinByUidV1(Uid: string) {
|
||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
||||
<(Uin: string[]) => Promise<{ uinInfo: Map<string, string> }>>(
|
||||
'NodeIKernelUixConvertService/getUin',
|
||||
5000,
|
||||
[Uid]
|
||||
);
|
||||
let uin = ret.uinInfo.get(Uid);
|
||||
if (!uin) {
|
||||
//从Buddy缓存获取Uin
|
||||
Array.from(friends.values()).forEach((t) => {
|
||||
if (t.uid == Uid) {
|
||||
uin = t.uin;
|
||||
}
|
||||
})
|
||||
}
|
||||
if (!uin) {
|
||||
uin = (await NTQQUserApi.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||
}
|
||||
|
||||
// if (!uin) {
|
||||
// uin = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 缓存转换
|
||||
// }
|
||||
// if (!uin) {
|
||||
// uin = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 非缓存转换
|
||||
// }
|
||||
return uin;
|
||||
}
|
||||
async getRecentContactListSnapShot(count: number) {
|
||||
return await napCatCore.session.getRecentContactService().getRecentContactListSnapShot(count);
|
||||
}
|
||||
async getRecentContactListSyncLimit(count: number) {
|
||||
return await napCatCore.session.getRecentContactService().getRecentContactListSyncLimit(count);
|
||||
}
|
||||
async getRecentContactListSync() {
|
||||
return await napCatCore.session.getRecentContactService().getRecentContactListSync();
|
||||
}
|
||||
async getRecentContactList() {
|
||||
return await napCatCore.session.getRecentContactService().getRecentContactList();
|
||||
}
|
||||
async getUserDetailInfoByUinV2(Uin: string) {
|
||||
return await NTEventDispatch.CallNoListenerEvent
|
||||
<(Uin: string) => Promise<UserDetailInfoByUinV2>>(
|
||||
'NodeIKernelProfileService/getUserDetailInfoByUin',
|
||||
5000,
|
||||
Uin
|
||||
);
|
||||
}
|
||||
async getUserDetailInfoByUin(Uin: string) {
|
||||
return NTEventDispatch.CallNoListenerEvent
|
||||
<(Uin: string) => Promise<UserDetailInfoByUin>>(
|
||||
'NodeIKernelProfileService/getUserDetailInfoByUin',
|
||||
5000,
|
||||
Uin
|
||||
);
|
||||
}
|
||||
@CacheClassFuncAsync(3600 * 1000, 'ClientKey')
|
||||
async forceFetchClientKey() {
|
||||
return await napCatCore.session.getTicketService().forceFetchClientKey('');
|
||||
}
|
||||
}
|
367
src/core/apis/webapi.ts
Normal file
367
src/core/apis/webapi.ts
Normal file
@ -0,0 +1,367 @@
|
||||
import { selfInfo } from '@/core/data';
|
||||
import { log, logDebug } from '@/common/utils/log';
|
||||
import { NTQQUserApi } from './user';
|
||||
import { RequestUtil } from '@/common/utils/request';
|
||||
import { CacheClassFuncAsync } from '@/common/utils/helper';
|
||||
export enum WebHonorType {
|
||||
ALL = 'all',
|
||||
TALKACTIVE = 'talkative',
|
||||
PERFROMER = 'performer',
|
||||
LEGEND = 'legend',
|
||||
STORONGE_NEWBI = 'strong_newbie',
|
||||
EMOTION = 'emotion'
|
||||
}
|
||||
export interface WebApiGroupMember {
|
||||
uin: number
|
||||
role: number
|
||||
g: number
|
||||
join_time: number
|
||||
last_speak_time: number
|
||||
lv: {
|
||||
point: number
|
||||
level: number
|
||||
}
|
||||
card: string
|
||||
tags: string
|
||||
flag: number
|
||||
nick: string
|
||||
qage: number
|
||||
rm: number
|
||||
}
|
||||
interface WebApiGroupMemberRet {
|
||||
ec: number
|
||||
errcode: number
|
||||
em: string
|
||||
cache: number
|
||||
adm_num: number
|
||||
levelname: any
|
||||
mems: WebApiGroupMember[]
|
||||
count: number
|
||||
svr_time: number
|
||||
max_count: number
|
||||
search_count: number
|
||||
extmode: number
|
||||
}
|
||||
export interface WebApiGroupNoticeFeed {
|
||||
u: number//发送者
|
||||
fid: string//fid
|
||||
pubt: number//时间
|
||||
msg: {
|
||||
text: string
|
||||
text_face: string
|
||||
title: string,
|
||||
pics?: {
|
||||
id: string,
|
||||
w: string,
|
||||
h: string
|
||||
}[]
|
||||
}
|
||||
type: number
|
||||
fn: number
|
||||
cn: number
|
||||
vn: number
|
||||
settings: {
|
||||
is_show_edit_card: number
|
||||
remind_ts: number
|
||||
tip_window_type: number
|
||||
confirm_required: number
|
||||
}
|
||||
read_num: number
|
||||
is_read: number
|
||||
is_all_confirm: number
|
||||
}
|
||||
export interface WebApiGroupNoticeRet {
|
||||
ec: number
|
||||
em: string
|
||||
ltsm: number
|
||||
srv_code: number
|
||||
read_only: number
|
||||
role: number
|
||||
feeds: WebApiGroupNoticeFeed[]
|
||||
group: {
|
||||
group_id: number
|
||||
class_ext: number
|
||||
}
|
||||
sta: number,
|
||||
gln: number
|
||||
tst: number,
|
||||
ui: any
|
||||
server_time: number
|
||||
svrt: number
|
||||
ad: number
|
||||
}
|
||||
interface GroupEssenceMsg {
|
||||
group_code: string
|
||||
msg_seq: number
|
||||
msg_random: number
|
||||
sender_uin: string
|
||||
sender_nick: string
|
||||
sender_time: number
|
||||
add_digest_uin: string
|
||||
add_digest_nick: string
|
||||
add_digest_time: number
|
||||
msg_content: any[]
|
||||
can_be_removed: true
|
||||
}
|
||||
export interface GroupEssenceMsgRet {
|
||||
retcode: number
|
||||
retmsg: string
|
||||
data: {
|
||||
msg_list: GroupEssenceMsg[]
|
||||
is_end: boolean
|
||||
group_role: number
|
||||
config_page_url: string
|
||||
}
|
||||
}
|
||||
export class WebApi {
|
||||
async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||
let ret: any = undefined;
|
||||
const data = 'group_code=' + groupCode + '&msg_seq=' + msgSeq + '&msg_random=' + msgRandom + '&target_group_code=' + targetGroupCode;
|
||||
const url = 'https://qun.qq.com/cgi-bin/group_digest/share_digest?bkn=' + Bkn + "&" + data;
|
||||
//console.log(url);
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': CookieValue });
|
||||
return ret;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members')
|
||||
async getGroupEssenceMsg(GroupCode: string, page_start: string) {
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||
const url = 'https://qun.qq.com/cgi-bin/group_digest/digest_list?bkn=' + Bkn + '&group_code=' + GroupCode + '&page_start=' + page_start + '&page_limit=20';
|
||||
let ret;
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<GroupEssenceMsgRet>(url, 'GET', '', { 'Cookie': CookieValue });
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
//console.log(url, CookieValue);
|
||||
if (ret.retcode !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@CacheClassFuncAsync(3600 * 1000, 'webapi_get_group_members')
|
||||
async getGroupMembers(GroupCode: string, cached: boolean = true): Promise<WebApiGroupMember[]> {
|
||||
//logDebug('webapi 获取群成员', GroupCode);
|
||||
let MemberData: Array<WebApiGroupMember> = new Array<WebApiGroupMember>();
|
||||
try {
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||
const retList: Promise<WebApiGroupMemberRet>[] = [];
|
||||
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>('https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?st=0&end=40&sort=1&gc=' + GroupCode + '&bkn=' + Bkn, 'POST', '', { 'Cookie': CookieValue });
|
||||
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
|
||||
return [];
|
||||
} else {
|
||||
for (const key in fastRet.mems) {
|
||||
MemberData.push(fastRet.mems[key]);
|
||||
}
|
||||
}
|
||||
//初始化获取PageNum
|
||||
const PageNum = Math.ceil(fastRet.count / 40);
|
||||
//遍历批量请求
|
||||
for (let i = 2; i <= PageNum; i++) {
|
||||
const ret: Promise<WebApiGroupMemberRet> = RequestUtil.HttpGetJson<WebApiGroupMemberRet>('https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?st=' + (i - 1) * 40 + '&end=' + i * 40 + '&sort=1&gc=' + GroupCode + '&bkn=' + Bkn, 'POST', '', { 'Cookie': CookieValue });
|
||||
retList.push(ret);
|
||||
}
|
||||
//批量等待
|
||||
for (let i = 1; i <= PageNum; i++) {
|
||||
const ret = await (retList[i]);
|
||||
if (!ret?.count || ret?.errcode !== 0 || !ret?.mems) {
|
||||
continue;
|
||||
}
|
||||
for (const key in ret.mems) {
|
||||
MemberData.push(ret.mems[key]);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return MemberData;
|
||||
}
|
||||
return MemberData;
|
||||
}
|
||||
// public async addGroupDigest(groupCode: string, msgSeq: string) {
|
||||
// const url = `https://qun.qq.com/cgi-bin/group_digest/cancel_digest?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&msg_seq=${msgSeq}&msg_random=444021292`;
|
||||
// const res = await this.request(url);
|
||||
// return await res.json();
|
||||
// }
|
||||
|
||||
// public async getGroupDigest(groupCode: string) {
|
||||
// const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?random=665&X-CROSS-ORIGIN=fetch&group_code=${groupCode}&page_start=0&page_limit=20`;
|
||||
// const res = await this.request(url);
|
||||
// return await res.json();
|
||||
// }
|
||||
async setGroupNotice(GroupCode: string, Content: string = '') {
|
||||
//https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=${bkn}
|
||||
//qid=${群号}&bkn=${bkn}&text=${内容}&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}
|
||||
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||
let ret: any = undefined;
|
||||
const data = 'qid=' + GroupCode + '&bkn=' + Bkn + '&text=' + Content + '&pinned=0&type=1&settings={"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}';
|
||||
const url = 'https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn=' + Bkn;
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<any>(url, 'GET', '', { 'Cookie': CookieValue });
|
||||
return ret;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
async getGrouptNotice(GroupCode: string): Promise<undefined | WebApiGroupNoticeRet> {
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||
let ret: WebApiGroupNoticeRet | undefined = undefined;
|
||||
//console.log(CookieValue);
|
||||
const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' + Bkn + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20';
|
||||
try {
|
||||
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': CookieValue });
|
||||
if (ret?.ec !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
return ret;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
genBkn(sKey: string) {
|
||||
sKey = sKey || '';
|
||||
let hash = 5381;
|
||||
|
||||
for (let i = 0; i < sKey.length; i++) {
|
||||
const code = sKey.charCodeAt(i);
|
||||
hash = hash + (hash << 5) + code;
|
||||
}
|
||||
|
||||
return (hash & 0x7FFFFFFF).toString();
|
||||
}
|
||||
|
||||
@CacheClassFuncAsync(3600 * 1000, 'GroupHonorInfo')
|
||||
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
|
||||
const CookiesObject = await NTQQUserApi.getCookies('qun.qq.com');
|
||||
const CookieValue = Object.entries(CookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||
const Bkn = WebApi.genBkn(CookiesObject.skey);
|
||||
async function getDataInternal(Internal_groupCode: string, Internal_type: number) {
|
||||
let url = 'https://qun.qq.com/interactive/honorlist?gc=' + Internal_groupCode + '&type=' + Internal_type.toString();
|
||||
let res = '';
|
||||
let resJson;
|
||||
try {
|
||||
res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': CookieValue });
|
||||
const match = res.match(/window\.__INITIAL_STATE__=(.*?);/);
|
||||
if (match) {
|
||||
resJson = JSON.parse(match[1].trim());
|
||||
}
|
||||
if (Internal_type === 1) {
|
||||
return resJson?.talkativeList;
|
||||
} else {
|
||||
return resJson?.actorList;
|
||||
}
|
||||
} catch (e) {
|
||||
logDebug('获取当前群荣耀失败', url, e);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let HonorInfo: any = { group_id: groupCode };
|
||||
|
||||
if (getType === WebHonorType.TALKACTIVE || getType === WebHonorType.ALL) {
|
||||
try {
|
||||
let RetInternal = await getDataInternal(groupCode, 1);
|
||||
if (!RetInternal) {
|
||||
throw new Error('获取龙王信息失败');
|
||||
}
|
||||
HonorInfo.current_talkative = {
|
||||
user_id: RetInternal[0]?.uin,
|
||||
avatar: RetInternal[0]?.avatar,
|
||||
nickname: RetInternal[0]?.name,
|
||||
day_count: 0,
|
||||
description: RetInternal[0]?.desc
|
||||
}
|
||||
HonorInfo.talkative_list = [];
|
||||
for (const talkative_ele of RetInternal) {
|
||||
HonorInfo.talkative_list.push({
|
||||
user_id: talkative_ele?.uin,
|
||||
avatar: talkative_ele?.avatar,
|
||||
description: talkative_ele?.desc,
|
||||
day_count: 0,
|
||||
nickname: talkative_ele?.name
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logDebug(e);
|
||||
}
|
||||
}
|
||||
if (getType === WebHonorType.PERFROMER || getType === WebHonorType.ALL) {
|
||||
try {
|
||||
let RetInternal = await getDataInternal(groupCode, 2);
|
||||
if (!RetInternal) {
|
||||
throw new Error('获取群聊之火失败');
|
||||
}
|
||||
HonorInfo.performer_list = [];
|
||||
for (const performer_ele of RetInternal) {
|
||||
HonorInfo.performer_list.push({
|
||||
user_id: performer_ele?.uin,
|
||||
nickname: performer_ele?.name,
|
||||
avatar: performer_ele?.avatar,
|
||||
description: performer_ele?.desc
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logDebug(e);
|
||||
}
|
||||
}
|
||||
if (getType === WebHonorType.PERFROMER || getType === WebHonorType.ALL) {
|
||||
try {
|
||||
let RetInternal = await getDataInternal(groupCode, 3);
|
||||
if (!RetInternal) {
|
||||
throw new Error('获取群聊炽焰失败');
|
||||
}
|
||||
HonorInfo.legend_list = [];
|
||||
for (const legend_ele of RetInternal) {
|
||||
HonorInfo.legend_list.push({
|
||||
user_id: legend_ele?.uin,
|
||||
nickname: legend_ele?.name,
|
||||
avatar: legend_ele?.avatar,
|
||||
desc: legend_ele?.description
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logDebug('获取群聊炽焰失败', e);
|
||||
}
|
||||
}
|
||||
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
|
||||
try {
|
||||
let RetInternal = await getDataInternal(groupCode, 6);
|
||||
if (!RetInternal) {
|
||||
throw new Error('获取快乐源泉失败');
|
||||
}
|
||||
HonorInfo.emotion_list = [];
|
||||
for (const emotion_ele of RetInternal) {
|
||||
HonorInfo.emotion_list.push({
|
||||
user_id: emotion_ele?.uin,
|
||||
nickname: emotion_ele?.name,
|
||||
avatar: emotion_ele?.avatar,
|
||||
desc: emotion_ele?.description
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logDebug('获取快乐源泉失败', e);
|
||||
}
|
||||
}
|
||||
//冒尖小春笋好像已经被tx扬了
|
||||
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
|
||||
HonorInfo.strong_newbie_list = [];
|
||||
}
|
||||
return HonorInfo;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user