mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
chore: OneBotApi
This commit is contained in:
@@ -10,6 +10,7 @@ import { SelfInfo, LineDevice, SelfStatusInfo } from "./entities";
|
|||||||
import { LegacyNTEventWrapper } from "@/common/framework/event-legacy";
|
import { LegacyNTEventWrapper } from "@/common/framework/event-legacy";
|
||||||
import { NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi, NTQQWebApi } from "./apis";
|
import { NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi, NTQQWebApi } from "./apis";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
|
import { NTQQCollectionApi } from "./apis/collection";
|
||||||
export enum NapCatCoreWorkingEnv {
|
export enum NapCatCoreWorkingEnv {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
Shell = 1,
|
Shell = 1,
|
||||||
@@ -42,6 +43,7 @@ export class NapCatCore {
|
|||||||
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session);
|
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session);
|
||||||
this.initNapCatCoreListeners().then().catch(console.error);
|
this.initNapCatCoreListeners().then().catch(console.error);
|
||||||
this.ApiContext = {
|
this.ApiContext = {
|
||||||
|
CollectionApi:new NTQQCollectionApi(this.context, this),
|
||||||
WebApi: new NTQQWebApi(this.context, this),
|
WebApi: new NTQQWebApi(this.context, this),
|
||||||
FriendApi: new NTQQFriendApi(this.context, this),
|
FriendApi: new NTQQFriendApi(this.context, this),
|
||||||
MsgApi: new NTQQMsgApi(this.context, this),
|
MsgApi: new NTQQMsgApi(this.context, this),
|
||||||
|
@@ -5,6 +5,7 @@ import { SelfInfo } from "../entities";
|
|||||||
import { NodeIKernelLoginService } from "../services";
|
import { NodeIKernelLoginService } from "../services";
|
||||||
import { WrapperNodeApi, NodeIQQNTWrapperSession } from "@/core";
|
import { WrapperNodeApi, NodeIQQNTWrapperSession } from "@/core";
|
||||||
import { NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi, NTQQWebApi } from "../apis";
|
import { NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi, NTQQWebApi } from "../apis";
|
||||||
|
import { NTQQCollectionApi } from "../apis/collection";
|
||||||
|
|
||||||
export interface InstanceContext {
|
export interface InstanceContext {
|
||||||
readonly workingEnv: NapCatCoreWorkingEnv;
|
readonly workingEnv: NapCatCoreWorkingEnv;
|
||||||
@@ -15,6 +16,7 @@ export interface InstanceContext {
|
|||||||
readonly basicInfoWrapper: QQBasicInfoWrapper;
|
readonly basicInfoWrapper: QQBasicInfoWrapper;
|
||||||
}
|
}
|
||||||
export interface NTApiContext {
|
export interface NTApiContext {
|
||||||
|
CollectionApi: NTQQCollectionApi,
|
||||||
WebApi: NTQQWebApi,
|
WebApi: NTQQWebApi,
|
||||||
FriendApi: NTQQFriendApi,
|
FriendApi: NTQQFriendApi,
|
||||||
MsgApi: NTQQMsgApi,
|
MsgApi: NTQQMsgApi,
|
||||||
|
64
src/onebot/action/BaseAction.ts
Normal file
64
src/onebot/action/BaseAction.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { ActionName, BaseCheckResult } from './types';
|
||||||
|
import { OB11Response } from './OB11Response';
|
||||||
|
import { OB11Return } from '@/onebot/types';
|
||||||
|
import Ajv, { ErrorObject, ValidateFunction } from 'ajv';
|
||||||
|
import { NapCatCore } from '@/core';
|
||||||
|
|
||||||
|
class BaseAction<PayloadType, ReturnDataType> {
|
||||||
|
actionName!: ActionName;
|
||||||
|
CoreContext!: NapCatCore;
|
||||||
|
private validate: undefined | ValidateFunction<any> = undefined;
|
||||||
|
PayloadSchema: any = undefined;
|
||||||
|
protected async check(payload: PayloadType): Promise<BaseCheckResult> {
|
||||||
|
if (this.PayloadSchema) {
|
||||||
|
this.validate = new Ajv({ allowUnionTypes: true }).compile(this.PayloadSchema);
|
||||||
|
}
|
||||||
|
if (this.validate && !this.validate(payload)) {
|
||||||
|
const errors = this.validate.errors as ErrorObject[];
|
||||||
|
const errorMessages: string[] = errors.map((e) => {
|
||||||
|
return `Key: ${e.instancePath.split('/').slice(1).join('.')}, Message: ${e.message}`;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: errorMessages.join('\n') as string || '未知错误'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async handle(payload: PayloadType): Promise<OB11Return<ReturnDataType | null>> {
|
||||||
|
const result = await this.check(payload);
|
||||||
|
if (!result.valid) {
|
||||||
|
return OB11Response.error(result.message, 400);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const resData = await this._handle(payload);
|
||||||
|
return OB11Response.ok(resData);
|
||||||
|
} catch (e: any) {
|
||||||
|
this.CoreContext.context.logger.logError('发生错误', e);
|
||||||
|
return OB11Response.error(e?.toString() || e?.stack?.toString() || '未知错误,可能操作超时', 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async websocketHandle(payload: PayloadType, echo: any): Promise<OB11Return<ReturnDataType | null>> {
|
||||||
|
const result = await this.check(payload);
|
||||||
|
if (!result.valid) {
|
||||||
|
return OB11Response.error(result.message, 1400);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const resData = await this._handle(payload);
|
||||||
|
return OB11Response.ok(resData, echo);
|
||||||
|
} catch (e: any) {
|
||||||
|
this.CoreContext.context.logger.logError('发生错误', e);
|
||||||
|
return OB11Response.error(e.stack?.toString() || e.toString(), 1200, echo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _handle(payload: PayloadType): Promise<ReturnDataType> {
|
||||||
|
throw `pleas override ${this.actionName} _handle`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BaseAction;
|
32
src/onebot/action/OB11Response.ts
Normal file
32
src/onebot/action/OB11Response.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { OB11Return } from '../types';
|
||||||
|
|
||||||
|
import { isNull } from '../../common/utils/helper';
|
||||||
|
|
||||||
|
export class OB11Response {
|
||||||
|
static res<T>(data: T, status: string, retcode: number, message: string = ''): OB11Return<T> {
|
||||||
|
return {
|
||||||
|
status: status,
|
||||||
|
retcode: retcode,
|
||||||
|
data: data,
|
||||||
|
message: message,
|
||||||
|
wording: message,
|
||||||
|
echo: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static ok<T>(data: T, echo: any = null) {
|
||||||
|
const res = OB11Response.res<T>(data, 'ok', 0);
|
||||||
|
if (!isNull(echo)) {
|
||||||
|
res.echo = echo;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static error(err: string, retcode: number, echo: any = null) {
|
||||||
|
const res = OB11Response.res(null, 'failed', retcode, err);
|
||||||
|
if (!isNull(echo)) {
|
||||||
|
res.echo = echo;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
27
src/onebot/action/extends/CreateCollection.ts
Normal file
27
src/onebot/action/extends/CreateCollection.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
rawData: { type: 'string' },
|
||||||
|
brief: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['brief', 'rawData'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class CreateCollection extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.CreateCollection;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
return await this.CoreContext.getApiContext().CollectionApi.createCollection(
|
||||||
|
|
||||||
|
this.CoreContext.selfInfo.uin,
|
||||||
|
this.CoreContext.selfInfo.uid,
|
||||||
|
this.CoreContext.selfInfo.nick,
|
||||||
|
payload.brief, payload.rawData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
43
src/onebot/action/extends/Debug.ts
Normal file
43
src/onebot/action/extends/Debug.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
// import * as ntqqApi from "../../../ntqqapi/api";
|
||||||
|
import {
|
||||||
|
NTQQMsgApi,
|
||||||
|
NTQQFriendApi,
|
||||||
|
NTQQGroupApi,
|
||||||
|
NTQQUserApi,
|
||||||
|
NTQQFileApi,
|
||||||
|
// NTQQFileCacheApi,
|
||||||
|
} from '@/core';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { log, logDebug } from '@/common/utils/log';
|
||||||
|
|
||||||
|
interface Payload {
|
||||||
|
method: string,
|
||||||
|
args: any[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Debug extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.Debug;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
//logDebug('debug call ntqq api', payload);
|
||||||
|
const ntqqApi = [NTQQMsgApi, NTQQFriendApi, NTQQGroupApi, NTQQUserApi, NTQQFileApi,
|
||||||
|
// NTQQFileCacheApi,
|
||||||
|
];
|
||||||
|
for (const ntqqApiClass of ntqqApi) {
|
||||||
|
// logDebug('ntqqApiClass', ntqqApiClass);
|
||||||
|
const method = (<any>ntqqApiClass)[payload.method];
|
||||||
|
if (method) {
|
||||||
|
const result = method(...payload.args);
|
||||||
|
if (method.constructor.name === 'AsyncFunction') {
|
||||||
|
return await result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw `${payload.method}方法 不存在`;
|
||||||
|
|
||||||
|
// const info = await NTQQApi.getUserDetailInfo(friends[0].uid);
|
||||||
|
// return info
|
||||||
|
}
|
||||||
|
}
|
22
src/onebot/action/extends/FetchCustomFace.ts
Normal file
22
src/onebot/action/extends/FetchCustomFace.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
count: { type: 'number' },
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class FetchCustomFace extends BaseAction<Payload, string[]> {
|
||||||
|
actionName = ActionName.FetchCustomFace;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
//48 可能正好是QQ需要的一个页面的数量 Tagged Mlikiowa
|
||||||
|
const ret = await NTQQMsgApi.fetchFavEmojiList(payload.count || 48);
|
||||||
|
return ret.emojiInfoList.map(e => e.url);
|
||||||
|
}
|
||||||
|
}
|
32
src/onebot/action/extends/FetchEmojioLike.ts
Normal file
32
src/onebot/action/extends/FetchEmojioLike.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//getMsgEmojiLikesList
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: 'string' },
|
||||||
|
group_id: { type: 'string' },
|
||||||
|
emojiId: { type: 'string' },
|
||||||
|
emojiType: { type: 'string' },
|
||||||
|
message_id: { type: ['string', 'number'] },
|
||||||
|
count: { type: 'number' }
|
||||||
|
},
|
||||||
|
required: ['emojiId', 'emojiType', 'message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class FetchEmojioLike extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.FetchEmojioLike;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if(!msgIdPeer) throw new Error('消息不存在');
|
||||||
|
const msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
|
||||||
|
const ret = await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer,msg.msgSeq,payload.emojiId,payload.emojiType,payload.count);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
26
src/onebot/action/extends/GetCollectionList.ts
Normal file
26
src/onebot/action/extends/GetCollectionList.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import { NTQQCollectionApi } from '@/core/apis/collection';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
category: { type: 'number' },
|
||||||
|
count: { type: 'number' }
|
||||||
|
},
|
||||||
|
required: ['category', 'count'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetCollectionList extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.GetCollectionList;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
return await NTQQCollectionApi.getAllCollection(payload.category, payload.count);
|
||||||
|
}
|
||||||
|
}
|
19
src/onebot/action/extends/GetFriendWithCategory.ts
Normal file
19
src/onebot/action/extends/GetFriendWithCategory.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { BuddyCategoryType } from '@/core/entities/';
|
||||||
|
import { NTQQFriendApi } from '@/core';
|
||||||
|
import { OB11Constructor } from '@/onebot11/constructor';
|
||||||
|
|
||||||
|
export class GetFriendWithCategory extends BaseAction<void, any> {
|
||||||
|
actionName = ActionName.GetFriendsWithCategory;
|
||||||
|
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
//全新逻辑
|
||||||
|
return OB11Constructor.friendsV2(await NTQQFriendApi.getBuddyV2ExWithCate(true));
|
||||||
|
} else {
|
||||||
|
throw new Error('this ntqq version not support, must be 26702 or later');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot/action/extends/GetGroupAddRequest.ts
Normal file
31
src/onebot/action/extends/GetGroupAddRequest.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { GroupNotify, GroupNotifyStatus } from '@/core/entities';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
|
||||||
|
interface OB11GroupRequestNotify {
|
||||||
|
group_id: number,
|
||||||
|
user_id: number,
|
||||||
|
flag: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class GetGroupAddRequest extends BaseAction<null, OB11GroupRequestNotify[] | null> {
|
||||||
|
actionName = ActionName.GetGroupIgnoreAddRequest;
|
||||||
|
|
||||||
|
protected async _handle(payload: null): Promise<OB11GroupRequestNotify[] | null> {
|
||||||
|
const data = await NTQQGroupApi.getGroupIgnoreNotifies();
|
||||||
|
// log(data);
|
||||||
|
// const notifies: GroupNotify[] = data.notifies.filter(notify => notify.status === GroupNotifyStatus.WAIT_HANDLE);
|
||||||
|
// const returnData: OB11GroupRequestNotify[] = [];
|
||||||
|
// for (const notify of notifies) {
|
||||||
|
// const uin = || (await NTQQUserApi.getUserDetailInfo(notify.user1.uid))?.uin;
|
||||||
|
// returnData.push({
|
||||||
|
// group_id: parseInt(notify.group.groupCode),
|
||||||
|
// user_id: parseInt(uin),
|
||||||
|
// flag: notify.seq
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
16
src/onebot/action/extends/GetProfileLike.ts
Normal file
16
src/onebot/action/extends/GetProfileLike.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
export class GetProfileLike extends BaseAction<void, any> {
|
||||||
|
actionName = ActionName.GetProfileLike;
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
const ret = await NTQQUserApi.getProfileLike(selfInfo.uid);
|
||||||
|
const listdata: any[] = ret.info.userLikeInfos[0].favoriteInfo.userInfos;
|
||||||
|
for (let i = 0; i < listdata.length; i++) {
|
||||||
|
listdata[i].uin = parseInt((await NTQQUserApi.getUinByUid(listdata[i].uid)) || '');
|
||||||
|
}
|
||||||
|
return listdata;
|
||||||
|
}
|
||||||
|
}
|
11
src/onebot/action/extends/GetRobotUinRange.ts
Normal file
11
src/onebot/action/extends/GetRobotUinRange.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
export class GetRobotUinRange extends BaseAction<void, Array<any>> {
|
||||||
|
actionName = ActionName.GetRobotUinRange;
|
||||||
|
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
// console.log(await NTQQUserApi.getRobotUinRange());
|
||||||
|
return await NTQQUserApi.getRobotUinRange();
|
||||||
|
}
|
||||||
|
}
|
46
src/onebot/action/extends/OCRImage.ts
Normal file
46
src/onebot/action/extends/OCRImage.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { DeviceList } from '@/onebot11/main';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||||
|
import { NTQQSystemApi } from '@/core';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
image: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['image']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class OCRImage extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.OCRImage;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const { path, isLocal, errMsg,success } = (await uri2local(payload.image));
|
||||||
|
if (!success) {
|
||||||
|
throw `OCR ${payload.image}失败,image字段可能格式不正确`;
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
|
const ret = await NTQQSystemApi.ORCImage(path);
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
throw `OCR ${payload.file}失败`;
|
||||||
|
}
|
||||||
|
return ret.result;
|
||||||
|
}
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
throw `OCR ${payload.file}失败,文件可能不存在`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class IOCRImage extends OCRImage {
|
||||||
|
actionName = ActionName.IOCRImage;
|
||||||
|
}
|
57
src/onebot/action/extends/SetGroupHeader.ts
Normal file
57
src/onebot/action/extends/SetGroupHeader.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
// import { log } from "../../../common/utils";
|
||||||
|
|
||||||
|
interface Payload {
|
||||||
|
file: string,
|
||||||
|
groupCode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SetGroupHeader extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetGroupHeader;
|
||||||
|
// 用不着复杂检测
|
||||||
|
protected async check(payload: Payload): Promise<BaseCheckResult> {
|
||||||
|
if (!payload.file || typeof payload.file != 'string' || !payload.groupCode || typeof payload.groupCode != 'string') {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'file和groupCode字段不能为空或者类型错误',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const { path, isLocal, errMsg,success } = (await uri2local(payload.file));
|
||||||
|
if (!success) {
|
||||||
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
|
const ret = await NTQQGroupApi.setGroupAvatar(payload.groupCode,path);
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
throw `头像${payload.file}设置失败,api无返回`;
|
||||||
|
}
|
||||||
|
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
||||||
|
// if (ret['result'] == 1004022) {
|
||||||
|
// throw `头像${payload.file}设置失败,文件可能不是图片格式`;
|
||||||
|
// } else if (ret['result'] != 0) {
|
||||||
|
// throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||||
|
// }
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
24
src/onebot/action/extends/SetLongNick.ts
Normal file
24
src/onebot/action/extends/SetLongNick.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
longNick: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: [ 'longNick'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class SetLongNick extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetLongNick;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const ret = await NTQQUserApi.setLongNick(payload.longNick);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
35
src/onebot/action/extends/SetOnlineStatus.ts
Normal file
35
src/onebot/action/extends/SetOnlineStatus.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
// 设置在线状态
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
status: { type: 'number' },
|
||||||
|
extStatus: { type: 'number' },
|
||||||
|
batteryStatus: { type: 'number' }
|
||||||
|
},
|
||||||
|
required: ['status', 'extStatus', 'batteryStatus'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class SetOnlineStatus extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetOnlineStatus;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
// 可设置状态
|
||||||
|
// { status: 10, extStatus: 1027, batteryStatus: 0 }
|
||||||
|
// { status: 30, extStatus: 0, batteryStatus: 0 }
|
||||||
|
// { status: 50, extStatus: 0, batteryStatus: 0 }
|
||||||
|
// { status: 60, extStatus: 0, batteryStatus: 0 }
|
||||||
|
// { status: 70, extStatus: 0, batteryStatus: 0 }
|
||||||
|
const ret = await NTQQUserApi.setSelfOnlineStatus(payload.status, payload.extStatus, payload.batteryStatus);
|
||||||
|
if (ret.result !== 0) {
|
||||||
|
throw new Error('设置在线状态失败');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
54
src/onebot/action/extends/SetQQAvatar.ts
Normal file
54
src/onebot/action/extends/SetQQAvatar.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||||
|
// import { log } from "../../../common/utils";
|
||||||
|
|
||||||
|
interface Payload {
|
||||||
|
file: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SetAvatar extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetQQAvatar;
|
||||||
|
// 用不着复杂检测
|
||||||
|
protected async check(payload: Payload): Promise<BaseCheckResult> {
|
||||||
|
if (!payload.file || typeof payload.file != 'string') {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'file字段不能为空或者类型错误',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const { path, isLocal, errMsg,success } = (await uri2local(payload.file));
|
||||||
|
if (!success) {
|
||||||
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
|
const ret = await NTQQUserApi.setQQAvatar(path);
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
throw `头像${payload.file}设置失败,api无返回`;
|
||||||
|
}
|
||||||
|
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
||||||
|
if (ret['result'] == 1004022) {
|
||||||
|
throw `头像${payload.file}设置失败,文件可能不是图片格式`;
|
||||||
|
} else if (ret['result'] != 0) {
|
||||||
|
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
32
src/onebot/action/extends/SetSelfProfile.ts
Normal file
32
src/onebot/action/extends/SetSelfProfile.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
nick: { type: 'string' },
|
||||||
|
longNick: { type: 'string' },
|
||||||
|
sex: { type: 'number' }//传Sex值?建议传0
|
||||||
|
},
|
||||||
|
required: ['nick', 'longNick', 'sex'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class SetSelfProfile extends BaseAction<Payload, any | null> {
|
||||||
|
actionName = ActionName.SetSelfProfile;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const ret = await NTQQUserApi.modifySelfProfile({
|
||||||
|
nick: payload.nick,
|
||||||
|
longNick: payload.longNick,
|
||||||
|
sex: payload.sex,
|
||||||
|
birthday: { birthday_year: '', birthday_month: '', birthday_day: '' },
|
||||||
|
location: undefined
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
28
src/onebot/action/extends/TestApi01.ts
Normal file
28
src/onebot/action/extends/TestApi01.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import { napCatCore, NTQQGroupApi } from '@/core';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
cmd: { type: 'string' },
|
||||||
|
param: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['cmd', 'param'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class TestApi01 extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.TestApi01;
|
||||||
|
// 用不着复杂检测
|
||||||
|
protected async check(payload: Payload): Promise<BaseCheckResult> {
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
return await napCatCore.session.getMsgService().sendSsoCmdReqByContend(payload.cmd, payload.param);
|
||||||
|
}
|
||||||
|
}
|
32
src/onebot/action/extends/TranslateEnWordToZn.ts
Normal file
32
src/onebot/action/extends/TranslateEnWordToZn.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import { NTQQSystemApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import Ajv from 'ajv';
|
||||||
|
// 设置在线状态
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
words: {
|
||||||
|
type: 'array',
|
||||||
|
items: { type: 'string' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['words'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class TranslateEnWordToZn extends BaseAction<Payload, Array<any> | null> {
|
||||||
|
actionName = ActionName.TranslateEnWordToZn;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
|
||||||
|
const ret = await NTQQSystemApi.translateEnWordToZn(payload.words);
|
||||||
|
if (ret.result !== 0) {
|
||||||
|
throw new Error('翻译失败');
|
||||||
|
}
|
||||||
|
return ret.words;
|
||||||
|
}
|
||||||
|
}
|
45
src/onebot/action/extends/sharePeer.ts
Normal file
45
src/onebot/action/extends/sharePeer.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { NTQQGroupApi, NTQQUserApi } from '@/core';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { BuddyCategoryType } from '@/core/entities/';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: 'string' },
|
||||||
|
group_id: { type: 'string' },
|
||||||
|
phoneNumber: { type: 'string' },
|
||||||
|
},
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
|
||||||
|
export class sharePeer extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SharePeer;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
if (payload.group_id) {
|
||||||
|
return await NTQQGroupApi.getGroupRecommendContactArkJson(payload.group_id);
|
||||||
|
} else if (payload.user_id) {
|
||||||
|
return await NTQQUserApi.getBuddyRecommendContactArkJson(payload.user_id, payload.phoneNumber || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const SchemaDataGroupEx = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type PayloadGroupEx = FromSchema<typeof SchemaDataGroupEx>;
|
||||||
|
export class shareGroupEx extends BaseAction<PayloadGroupEx, any> {
|
||||||
|
actionName = ActionName.ShareGroupEx;
|
||||||
|
PayloadSchema = SchemaDataGroupEx;
|
||||||
|
protected async _handle(payload: PayloadGroupEx) {
|
||||||
|
return await NTQQGroupApi.getArkJsonGroupShare(payload.group_id);
|
||||||
|
}
|
||||||
|
}
|
23
src/onebot/action/file/DelGroupFile.ts
Normal file
23
src/onebot/action/file/DelGroupFile.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['string', 'number'] },
|
||||||
|
file_id: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'file_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class DelGroupFile extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.DelGroupFile;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
return await NTQQGroupApi.DelGroupFile(payload.group_id.toString(), [payload.file_id]);
|
||||||
|
}
|
||||||
|
}
|
23
src/onebot/action/file/DelGroupFileFolder.ts
Normal file
23
src/onebot/action/file/DelGroupFileFolder.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['string', 'number'] },
|
||||||
|
folder_id: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'folder_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class DelGroupFileFolder extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.DelGroupFileFolder;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
return (await NTQQGroupApi.DelGroupFileFolder(payload.group_id.toString(), payload.folder_id)).groupFileCommonResult;
|
||||||
|
}
|
||||||
|
}
|
227
src/onebot/action/file/GetFile.ts
Normal file
227
src/onebot/action/file/GetFile.ts
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import { ob11Config } from '@/onebot11/config';
|
||||||
|
import { UUIDConverter } from '@/common/utils/helper';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import { ChatType, ElementType, FileElement, Peer, RawMessage, VideoElement } from '@/core/entities';
|
||||||
|
import { NTQQFileApi, NTQQFriendApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { getGroup } from '@/core/data';
|
||||||
|
|
||||||
|
export interface GetFilePayload {
|
||||||
|
file: string; // 文件名或者fileUuid
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetFileResponse {
|
||||||
|
file?: string; // path
|
||||||
|
url?: string;
|
||||||
|
file_size?: string;
|
||||||
|
file_name?: string;
|
||||||
|
base64?: string;
|
||||||
|
}
|
||||||
|
const GetFileBase_PayloadSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
file: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['file']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
||||||
|
PayloadSchema: any = GetFileBase_PayloadSchema;
|
||||||
|
private getElement(msg: RawMessage): { id: string, element: VideoElement | FileElement } {
|
||||||
|
let element = msg.elements.find(e => e.fileElement);
|
||||||
|
if (!element) {
|
||||||
|
element = msg.elements.find(e => e.videoElement);
|
||||||
|
if (element) {
|
||||||
|
return { id: element.elementId, element: element.videoElement };
|
||||||
|
} else {
|
||||||
|
throw new Error('找不到文件');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { id: element.elementId, element: element.fileElement };
|
||||||
|
}
|
||||||
|
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
|
||||||
|
const { enableLocalFile2Url } = ob11Config;
|
||||||
|
let UuidData: {
|
||||||
|
high: string;
|
||||||
|
low: string;
|
||||||
|
} | undefined;
|
||||||
|
try {
|
||||||
|
UuidData = UUIDConverter.decode(payload.file);
|
||||||
|
if (UuidData) {
|
||||||
|
const peerUin = UuidData.high;
|
||||||
|
const msgId = UuidData.low;
|
||||||
|
const isGroup = await getGroup(peerUin);
|
||||||
|
let peer: Peer | undefined;
|
||||||
|
//识别Peer
|
||||||
|
if (isGroup) {
|
||||||
|
peer = { chatType: ChatType.group, peerUid: peerUin };
|
||||||
|
}
|
||||||
|
const PeerUid = await NTQQUserApi.getUidByUin(peerUin);
|
||||||
|
if (PeerUid) {
|
||||||
|
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
|
||||||
|
if (isBuddy) {
|
||||||
|
peer = { chatType: ChatType.friend, peerUid: PeerUid };
|
||||||
|
} else {
|
||||||
|
peer = { chatType: ChatType.temp, peerUid: PeerUid };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!peer) {
|
||||||
|
throw new Error('chattype not support');
|
||||||
|
}
|
||||||
|
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]);
|
||||||
|
if (msgList.msgList.length == 0) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
const msg = msgList.msgList[0];
|
||||||
|
const findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT);
|
||||||
|
if (!findEle) {
|
||||||
|
throw new Error('element not found');
|
||||||
|
}
|
||||||
|
const downloadPath = await NTQQFileApi.downloadMedia(msgId, msg.chatType, msg.peerUid, findEle.elementId, '', '');
|
||||||
|
const fileSize = findEle?.videoElement?.fileSize || findEle?.fileElement?.fileSize || findEle?.pttElement?.fileSize || '0';
|
||||||
|
const fileName = findEle?.videoElement?.fileName || findEle?.fileElement?.fileName || findEle?.pttElement?.fileName || '';
|
||||||
|
const res: GetFileResponse = {
|
||||||
|
file: downloadPath,
|
||||||
|
url: downloadPath,
|
||||||
|
file_size: fileSize,
|
||||||
|
file_name: fileName
|
||||||
|
};
|
||||||
|
if (enableLocalFile2Url) {
|
||||||
|
try {
|
||||||
|
res.base64 = await fs.readFile(downloadPath, 'base64');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('文件下载失败. ' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//不手动删除?文件持久化了
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;
|
||||||
|
if (NTSearchNameResult.length !== 0) {
|
||||||
|
const MsgId = NTSearchNameResult[0].msgId;
|
||||||
|
let peer: Peer | undefined = undefined;
|
||||||
|
if (NTSearchNameResult[0].chatType == ChatType.group) {
|
||||||
|
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
|
||||||
|
}
|
||||||
|
if (!peer) {
|
||||||
|
throw new Error('chattype not support');
|
||||||
|
}
|
||||||
|
const msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList;
|
||||||
|
if (!msgList || msgList.length == 0) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
const msg = msgList[0];
|
||||||
|
const file = msg.elements.filter(e => e.elementType == NTSearchNameResult[0].elemType);
|
||||||
|
if (file.length == 0) {
|
||||||
|
throw new Error('file not found');
|
||||||
|
}
|
||||||
|
const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, file[0].elementId, '', '');
|
||||||
|
const res: GetFileResponse = {
|
||||||
|
file: downloadPath,
|
||||||
|
url: downloadPath,
|
||||||
|
file_size: NTSearchNameResult[0].fileSize.toString(),
|
||||||
|
file_name: NTSearchNameResult[0].fileName
|
||||||
|
};
|
||||||
|
if (enableLocalFile2Url) {
|
||||||
|
try {
|
||||||
|
res.base64 = await fs.readFile(downloadPath, 'base64');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('文件下载失败. ' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//不手动删除?文件持久化了
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
throw new Error('file not found');
|
||||||
|
// let cache = await dbUtil.getFileCacheByName(payload.file);
|
||||||
|
// if (!cache) {
|
||||||
|
// cache = await dbUtil.getFileCacheByUuid(payload.file);
|
||||||
|
// }
|
||||||
|
// if (!cache) {
|
||||||
|
// throw new Error('file not found');
|
||||||
|
// }
|
||||||
|
// const { enableLocalFile2Url } = ob11Config;
|
||||||
|
// try {
|
||||||
|
// await fs.access(cache.path, fs.constants.F_OK);
|
||||||
|
// } catch (e) {
|
||||||
|
// logDebug('local file not found, start download...');
|
||||||
|
// // if (cache.url) {
|
||||||
|
// // const downloadResult = await uri2local(cache.url);
|
||||||
|
// // if (downloadResult.success) {
|
||||||
|
// // cache.path = downloadResult.path;
|
||||||
|
// // dbUtil.updateFileCache(cache).then();
|
||||||
|
// // } else {
|
||||||
|
// // throw new Error('file download failed. ' + downloadResult.errMsg);
|
||||||
|
// // }
|
||||||
|
// // } else {
|
||||||
|
// // // 没有url的可能是私聊文件或者群文件,需要自己下载
|
||||||
|
// // log('需要调用 NTQQ 下载文件api');
|
||||||
|
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
|
||||||
|
// let msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
|
||||||
|
// // log('文件 msg', msg);
|
||||||
|
// if (msg) {
|
||||||
|
// // 构建下载函数
|
||||||
|
// const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
|
||||||
|
// cache.elementId, '', '');
|
||||||
|
// // await sleep(1000);
|
||||||
|
|
||||||
|
// // log('download result', downloadPath);
|
||||||
|
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
|
||||||
|
// msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
|
||||||
|
// // log('下载完成后的msg', msg);
|
||||||
|
// cache.path = downloadPath!;
|
||||||
|
// dbUtil.updateFileCache(cache).then();
|
||||||
|
// // log('下载完成后的msg', msg);
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// // log('file found', cache);
|
||||||
|
// const res: GetFileResponse = {
|
||||||
|
// file: cache.path,
|
||||||
|
// url: cache.url,
|
||||||
|
// file_size: cache.size.toString(),
|
||||||
|
// file_name: cache.name
|
||||||
|
// };
|
||||||
|
// if (enableLocalFile2Url) {
|
||||||
|
// if (!cache.url) {
|
||||||
|
// try {
|
||||||
|
// res.base64 = await fs.readFile(cache.path, 'base64');
|
||||||
|
// } catch (e) {
|
||||||
|
// throw new Error('文件下载失败. ' + e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetFile_PayloadSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
file_id: { type: 'string' },
|
||||||
|
file: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['file_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type GetFile_Payload_Internal = FromSchema<typeof GetFile_PayloadSchema>;
|
||||||
|
|
||||||
|
interface GetFile_Payload extends GetFile_Payload_Internal {
|
||||||
|
file: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class GetFile extends GetFileBase {
|
||||||
|
actionName = ActionName.GetFile;
|
||||||
|
PayloadSchema = GetFile_PayloadSchema;
|
||||||
|
protected async _handle(payload: GetFile_Payload): Promise<GetFileResponse> {
|
||||||
|
payload.file = payload.file_id;
|
||||||
|
return super._handle(payload);
|
||||||
|
}
|
||||||
|
}
|
23
src/onebot/action/file/GetGroupFileCount.ts
Normal file
23
src/onebot/action/file/GetGroupFileCount.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['string', 'number'] },
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetGroupFileCount extends BaseAction<Payload, { count: number }> {
|
||||||
|
actionName = ActionName.GetGroupFileCount;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const ret = await NTQQGroupApi.GetGroupFileCount([payload.group_id?.toString()]);
|
||||||
|
return { count: ret.groupFileCounts[0] };
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot/action/file/GetGroupFileList.ts
Normal file
31
src/onebot/action/file/GetGroupFileList.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['string', 'number'] },
|
||||||
|
start_index: { type: 'number' },
|
||||||
|
file_count: { type: 'number' },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'start_index', 'file_count']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetGroupFileList extends BaseAction<Payload, { FileList: Array<any> }> {
|
||||||
|
actionName = ActionName.GetGroupFileList;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const ret = await NTQQMsgApi.getGroupFileList(payload.group_id.toString(), {
|
||||||
|
sortType: 1,
|
||||||
|
fileCount: payload.file_count,
|
||||||
|
startIndex: payload.start_index,
|
||||||
|
sortOrder: 2,
|
||||||
|
showOnlinedocFolder: 0
|
||||||
|
}).catch((e) => { return []; });
|
||||||
|
return { FileList: ret };
|
||||||
|
}
|
||||||
|
}
|
7
src/onebot/action/file/GetImage.ts
Normal file
7
src/onebot/action/file/GetImage.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { GetFileBase } from './GetFile';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
|
||||||
|
|
||||||
|
export default class GetImage extends GetFileBase {
|
||||||
|
actionName = ActionName.GetImage;
|
||||||
|
}
|
15
src/onebot/action/file/GetRecord.ts
Normal file
15
src/onebot/action/file/GetRecord.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { GetFileBase, GetFilePayload, GetFileResponse } from './GetFile';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
|
||||||
|
interface Payload extends GetFilePayload {
|
||||||
|
out_format: 'mp3' | 'amr' | 'wma' | 'm4a' | 'spx' | 'ogg' | 'wav' | 'flac'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class GetRecord extends GetFileBase {
|
||||||
|
actionName = ActionName.GetRecord;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<GetFileResponse> {
|
||||||
|
const res = super._handle(payload);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
23
src/onebot/action/file/SetGroupFileFolder.ts
Normal file
23
src/onebot/action/file/SetGroupFileFolder.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['string', 'number'] },
|
||||||
|
folder_name: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'folder_name']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class SetGroupFileFolder extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetGroupFileFolder;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
return (await NTQQGroupApi.CreatGroupFileFolder(payload.group_id.toString(), payload.folder_name)).resultWithGroupItem;
|
||||||
|
}
|
||||||
|
}
|
82
src/onebot/action/go-cqhttp/DownloadFile.ts
Normal file
82
src/onebot/action/go-cqhttp/DownloadFile.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { join as joinPath } from 'node:path';
|
||||||
|
import { calculateFileMD5, getTempDir, httpDownload } from '@/common/utils/file';
|
||||||
|
import { randomUUID } from 'crypto';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
interface FileResponse {
|
||||||
|
file: string;
|
||||||
|
}
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
thread_count: { type: 'number' },
|
||||||
|
url: { type: 'string' },
|
||||||
|
base64: { type: 'string' },
|
||||||
|
name: { type: 'string' },
|
||||||
|
headers: {
|
||||||
|
type: ['string', 'array'],
|
||||||
|
items: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileResponse> {
|
||||||
|
actionName = ActionName.GoCQHTTP_DownloadFile;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<FileResponse> {
|
||||||
|
const isRandomName = !payload.name;
|
||||||
|
const name = payload.name || randomUUID();
|
||||||
|
const filePath = joinPath(getTempDir(), name);
|
||||||
|
|
||||||
|
if (payload.base64) {
|
||||||
|
fs.writeFileSync(filePath, payload.base64, 'base64');
|
||||||
|
} else if (payload.url) {
|
||||||
|
const headers = this.getHeaders(payload.headers);
|
||||||
|
const buffer = await httpDownload({ url: payload.url, headers: headers });
|
||||||
|
fs.writeFileSync(filePath, Buffer.from(buffer), 'binary');
|
||||||
|
} else {
|
||||||
|
throw new Error('不存在任何文件, 无法下载');
|
||||||
|
}
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
|
||||||
|
if (isRandomName) {
|
||||||
|
// 默认实现要名称未填写时文件名为文件 md5
|
||||||
|
const md5 = await calculateFileMD5(filePath);
|
||||||
|
const newPath = joinPath(getTempDir(), md5);
|
||||||
|
fs.renameSync(filePath, newPath);
|
||||||
|
return { file: newPath };
|
||||||
|
}
|
||||||
|
return { file: filePath };
|
||||||
|
} else {
|
||||||
|
throw new Error('文件写入失败, 检查权限');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders(headersIn?: string | string[]): Record<string, string> {
|
||||||
|
const headers: Record<string, string> = {};
|
||||||
|
if (typeof headersIn == 'string') {
|
||||||
|
headersIn = headersIn.split('[\\r\\n]');
|
||||||
|
}
|
||||||
|
if (Array.isArray(headersIn)) {
|
||||||
|
for (const headerItem of headersIn) {
|
||||||
|
const spilt = headerItem.indexOf('=');
|
||||||
|
if (spilt < 0) {
|
||||||
|
headers[headerItem] = '';
|
||||||
|
} else {
|
||||||
|
const key = headerItem.substring(0, spilt);
|
||||||
|
headers[key] = headerItem.substring(0, spilt + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!headers['Content-Type']) {
|
||||||
|
headers['Content-Type'] = 'application/octet-stream';
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
52
src/onebot/action/go-cqhttp/GetForwardMsg.ts
Normal file
52
src/onebot/action/go-cqhttp/GetForwardMsg.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: 'string' },
|
||||||
|
id: { type: 'string' }
|
||||||
|
},
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
interface Response {
|
||||||
|
messages: (OB11Message & { content: OB11MessageData })[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.GoCQHTTP_GetForwardMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const msgId = payload.message_id || payload.id;
|
||||||
|
if (!msgId) {
|
||||||
|
throw Error('message_id is required');
|
||||||
|
}
|
||||||
|
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId);
|
||||||
|
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId));
|
||||||
|
if (!rootMsg) {
|
||||||
|
throw Error('msg not found');
|
||||||
|
}
|
||||||
|
const data = await NTQQMsgApi.getMultiMsg(rootMsg.Peer, rootMsg.MsgId, rootMsg.MsgId);
|
||||||
|
if (!data || data.result !== 0) {
|
||||||
|
throw Error('找不到相关的聊天记录' + data?.errMsg);
|
||||||
|
}
|
||||||
|
const msgList = data.msgList;
|
||||||
|
const messages = await Promise.all(msgList.map(async msg => {
|
||||||
|
const resMsg = await OB11Constructor.message(msg);
|
||||||
|
resMsg.message_id = await MessageUnique.createMsg({ guildId:'',chatType:msg.chatType,peerUid:msg.peerUid },msg.msgId)!;
|
||||||
|
return resMsg;
|
||||||
|
}));
|
||||||
|
messages.map(msg => {
|
||||||
|
(<OB11ForwardMessage>msg).content = msg.message;
|
||||||
|
delete (<any>msg).message;
|
||||||
|
});
|
||||||
|
return { messages };
|
||||||
|
}
|
||||||
|
}
|
57
src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts
Normal file
57
src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { OB11Message, OB11User } from '../../types';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { ChatType, RawMessage } from '@/core/entities';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQFriendApi, NTQQUserApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
interface Response {
|
||||||
|
messages: OB11Message[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
message_seq: { type: 'number' },
|
||||||
|
count: { type: 'number' },
|
||||||
|
reverseOrder: { type: 'boolean' }
|
||||||
|
},
|
||||||
|
required: ['user_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
||||||
|
actionName = ActionName.GetFriendMsgHistory;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<Response> {
|
||||||
|
//处理参数
|
||||||
|
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
const MsgCount = payload.count || 20;
|
||||||
|
const isReverseOrder = payload.reverseOrder || true;
|
||||||
|
if (!uid) throw `记录${payload.user_id}不存在`;
|
||||||
|
const friend = await NTQQFriendApi.isBuddy(uid);
|
||||||
|
const peer = { chatType: friend ? ChatType.friend : ChatType.temp, peerUid: uid };
|
||||||
|
|
||||||
|
//拉取消息
|
||||||
|
let msgList: RawMessage[];
|
||||||
|
if (!payload.message_seq || payload.message_seq == 0) {
|
||||||
|
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, MsgCount)).msgList;
|
||||||
|
} else {
|
||||||
|
const startMsgId = MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq)?.MsgId;
|
||||||
|
if (!startMsgId) throw `消息${payload.message_seq}不存在`;
|
||||||
|
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList;
|
||||||
|
}
|
||||||
|
if(isReverseOrder) msgList.reverse();
|
||||||
|
await Promise.all(msgList.map(async msg => {
|
||||||
|
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
|
}));
|
||||||
|
//转换消息
|
||||||
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
|
return { 'messages': ob11MsgList };
|
||||||
|
}
|
||||||
|
}
|
26
src/onebot/action/go-cqhttp/GetGroupHonorInfo.ts
Normal file
26
src/onebot/action/go-cqhttp/GetGroupHonorInfo.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { WebApi, WebHonorType } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
type: { enum: [WebHonorType.ALL, WebHonorType.EMOTION, WebHonorType.LEGEND, WebHonorType.PERFROMER, WebHonorType.STORONGE_NEWBI, WebHonorType.TALKACTIVE] }
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
// enum是不是有点抽象
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetGroupHonorInfo extends BaseAction<Payload, Array<any>> {
|
||||||
|
actionName = ActionName.GetGroupHonorInfo;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
if (!payload.type) {
|
||||||
|
payload.type = WebHonorType.ALL;
|
||||||
|
}
|
||||||
|
return await WebApi.getGroupHonorInfo(payload.group_id.toString(), payload.type);
|
||||||
|
}
|
||||||
|
}
|
56
src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts
Normal file
56
src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { OB11Message, OB11User } from '../../types';
|
||||||
|
import { getGroup, groups } from '@/core/data';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { ChatType, Peer, RawMessage } from '@/core/entities';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
interface Response {
|
||||||
|
messages: OB11Message[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
message_seq: { type: 'number' },
|
||||||
|
count: { type: 'number' },
|
||||||
|
reverseOrder: { type: 'boolean' }
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Response> {
|
||||||
|
actionName = ActionName.GoCQHTTP_GetGroupMsgHistory;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<Response> {
|
||||||
|
//处理参数
|
||||||
|
const group = await getGroup(payload.group_id.toString());
|
||||||
|
const isReverseOrder = payload.reverseOrder || true;
|
||||||
|
const MsgCount = payload.count || 20;
|
||||||
|
const peer: Peer = { chatType: ChatType.group, peerUid: payload.group_id.toString() };
|
||||||
|
if (!group) throw `群${payload.group_id}不存在`;
|
||||||
|
|
||||||
|
//拉取消息
|
||||||
|
let msgList: RawMessage[];
|
||||||
|
if (!payload.message_seq || payload.message_seq == 0) {
|
||||||
|
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, MsgCount)).msgList;
|
||||||
|
} else {
|
||||||
|
const startMsgId = MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq)?.MsgId;
|
||||||
|
if (!startMsgId) throw `消息${payload.message_seq}不存在`;
|
||||||
|
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList;
|
||||||
|
}
|
||||||
|
if(isReverseOrder) msgList.reverse();
|
||||||
|
await Promise.all(msgList.map(async msg => {
|
||||||
|
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
|
}));
|
||||||
|
|
||||||
|
//转换消息
|
||||||
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
|
return { 'messages': ob11MsgList };
|
||||||
|
}
|
||||||
|
}
|
23
src/onebot/action/go-cqhttp/GetOnlineClient.ts
Normal file
23
src/onebot/action/go-cqhttp/GetOnlineClient.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { DeviceList } from '@/onebot11/main';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQSystemApi } from '@/core';
|
||||||
|
import { sleep } from '@/common/utils/helper';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
no_cache: { type: 'boolean' },
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
export class GetOnlineClient extends BaseAction<void, Array<any>> {
|
||||||
|
actionName = ActionName.GetOnlineClient;
|
||||||
|
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
NTQQSystemApi.getOnlineDev();
|
||||||
|
await sleep(500);
|
||||||
|
return DeviceList;
|
||||||
|
}
|
||||||
|
}
|
66
src/onebot/action/go-cqhttp/GetStrangerInfo.ts
Normal file
66
src/onebot/action/go-cqhttp/GetStrangerInfo.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { OB11User, OB11UserSex } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { calcQQLevel } from '@/common/utils/qqlevel';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
},
|
||||||
|
required: ['user_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11User> {
|
||||||
|
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<OB11User> {
|
||||||
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
|
const user_id = payload.user_id.toString();
|
||||||
|
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
|
||||||
|
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
||||||
|
if (!uid || uid.indexOf('*') != -1) {
|
||||||
|
const ret = {
|
||||||
|
...extendData,
|
||||||
|
user_id: parseInt(extendData.info.uin) || 0,
|
||||||
|
nickname: extendData.info.nick,
|
||||||
|
sex: OB11UserSex.unknown,
|
||||||
|
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
|
||||||
|
qid: extendData.info.qid,
|
||||||
|
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
|
||||||
|
login_days: 0,
|
||||||
|
uid: ''
|
||||||
|
};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||||
|
return OB11Constructor.stranger(data);
|
||||||
|
} else {
|
||||||
|
const user_id = payload.user_id.toString();
|
||||||
|
const extendData = await NTQQUserApi.getUserDetailInfoByUinV2(user_id);
|
||||||
|
//console.log(extendData);
|
||||||
|
const uid = (await NTQQUserApi.getUidByUin(user_id))!;
|
||||||
|
if (!uid || uid.indexOf('*') != -1) {
|
||||||
|
const ret = {
|
||||||
|
...extendData,
|
||||||
|
user_id: parseInt(extendData.detail.uin) || 0,
|
||||||
|
nickname: extendData.detail.simpleInfo.coreInfo.nick,
|
||||||
|
sex: OB11UserSex.unknown,
|
||||||
|
age: 0,
|
||||||
|
level: extendData.detail.commonExt.qqLevel && calcQQLevel(extendData.detail.commonExt.qqLevel) || 0,
|
||||||
|
login_days: 0,
|
||||||
|
uid: ''
|
||||||
|
};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
|
||||||
|
return OB11Constructor.stranger(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/onebot/action/go-cqhttp/QuickAction.ts
Normal file
17
src/onebot/action/go-cqhttp/QuickAction.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { log } from '@/common/utils/log';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { QuickAction, QuickActionEvent, handleQuickOperation } from '@/onebot11/server/postOB11Event';
|
||||||
|
|
||||||
|
interface Payload{
|
||||||
|
context: QuickActionEvent,
|
||||||
|
operation: QuickAction
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GoCQHTTPHandleQuickAction extends BaseAction<Payload, null>{
|
||||||
|
actionName = ActionName.GoCQHTTP_HandleQuickAction;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
handleQuickOperation(payload.context, payload.operation).then().catch(log);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
20
src/onebot/action/go-cqhttp/SendForwardMsg.ts
Normal file
20
src/onebot/action/go-cqhttp/SendForwardMsg.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import SendMsg, { normalize } from '../msg/SendMsg';
|
||||||
|
import { OB11PostSendMsg } from '../../types';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
// 未验证
|
||||||
|
export class GoCQHTTPSendForwardMsg extends SendMsg {
|
||||||
|
actionName = ActionName.GoCQHTTP_SendForwardMsg;
|
||||||
|
|
||||||
|
protected async check(payload: OB11PostSendMsg) {
|
||||||
|
if (payload.messages) payload.message = normalize(payload.messages);
|
||||||
|
return super.check(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendForwardMsg {
|
||||||
|
actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GoCQHTTPSendGroupForwardMsg extends GoCQHTTPSendForwardMsg {
|
||||||
|
actionName = ActionName.GoCQHTTP_SendGroupForwardMsg;
|
||||||
|
}
|
61
src/onebot/action/go-cqhttp/SendGroupNotice.ts
Normal file
61
src/onebot/action/go-cqhttp/SendGroupNotice.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi, WebApi } from '@/core/apis';
|
||||||
|
import { unlink } from 'node:fs';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
content: { type: 'string' },
|
||||||
|
image: { type: 'string' },
|
||||||
|
pinned: { type: 'number' },
|
||||||
|
confirmRequired: { type: 'number' }
|
||||||
|
},
|
||||||
|
required: ['group_id', 'content']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class SendGroupNotice extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.GoCQHTTP_SendGroupNotice;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
let UploadImage: { id: string, width: number, height: number } | undefined = undefined;
|
||||||
|
if (payload.image) {
|
||||||
|
//公告图逻辑
|
||||||
|
const { errMsg, path, isLocal, success } = (await uri2local(payload.image));
|
||||||
|
if (!success) {
|
||||||
|
throw `群公告${payload.image}设置失败,image字段可能格式不正确`;
|
||||||
|
}
|
||||||
|
if (!path) {
|
||||||
|
throw `群公告${payload.image}设置失败,获取资源失败`;
|
||||||
|
}
|
||||||
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
|
const ImageUploadResult = await NTQQGroupApi.uploadGroupBulletinPic(payload.group_id.toString(), path);
|
||||||
|
if (ImageUploadResult.errCode != 0) {
|
||||||
|
throw `群公告${payload.image}设置失败,图片上传失败`;
|
||||||
|
}
|
||||||
|
if (!isLocal) {
|
||||||
|
unlink(path, () => { });
|
||||||
|
}
|
||||||
|
UploadImage = ImageUploadResult.picInfo;
|
||||||
|
}
|
||||||
|
let Notice_Pinned = 0;
|
||||||
|
let Notice_confirmRequired = 0;
|
||||||
|
if (!payload.pinned) {
|
||||||
|
Notice_Pinned = 0;
|
||||||
|
}
|
||||||
|
if (!payload.confirmRequired) {
|
||||||
|
Notice_confirmRequired = 0;
|
||||||
|
}
|
||||||
|
const PublishGroupBulletinResult = await NTQQGroupApi.publishGroupBulletin(payload.group_id.toString(), payload.content, UploadImage, Notice_Pinned, Notice_confirmRequired);
|
||||||
|
|
||||||
|
if (PublishGroupBulletinResult.result != 0) {
|
||||||
|
throw `设置群公告失败,错误信息:${PublishGroupBulletinResult.errMsg}`;
|
||||||
|
}
|
||||||
|
// 下面实现扬了
|
||||||
|
//await WebApi.setGroupNotice(payload.group_id, payload.content) ;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
44
src/onebot/action/go-cqhttp/UploadGroupFile.ts
Normal file
44
src/onebot/action/go-cqhttp/UploadGroupFile.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { getGroup } from '@/core/data';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { SendMsgElementConstructor } from '@/core/entities/constructor';
|
||||||
|
import { ChatType, SendFileElement } from '@/core/entities';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { SendMsg, sendMsg } from '@/onebot11/action/msg/SendMsg';
|
||||||
|
import { uri2local } from '@/common/utils/file';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
file: { type: 'string' },
|
||||||
|
name: { type: 'string' },
|
||||||
|
folder: { type: 'string' },
|
||||||
|
folder_id: { type: 'string' }//临时扩展
|
||||||
|
},
|
||||||
|
required: ['group_id', 'file', 'name']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.GoCQHTTP_UploadGroupFile;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const group = await getGroup(payload.group_id.toString());
|
||||||
|
if (!group) {
|
||||||
|
throw new Error(`群组${payload.group_id}不存在`);
|
||||||
|
}
|
||||||
|
let file = payload.file;
|
||||||
|
if (fs.existsSync(file)) {
|
||||||
|
file = `file://${file}`;
|
||||||
|
}
|
||||||
|
const downloadResult = await uri2local(file);
|
||||||
|
if (!downloadResult.success) {
|
||||||
|
throw new Error(downloadResult.errMsg);
|
||||||
|
}
|
||||||
|
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id);
|
||||||
|
await sendMsg({ chatType: ChatType.group, peerUid: group.groupCode }, [sendFileEle], [], true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
51
src/onebot/action/go-cqhttp/UploadPrivareFile.ts
Normal file
51
src/onebot/action/go-cqhttp/UploadPrivareFile.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { getGroup } from '@/core/data';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { SendMsgElementConstructor } from '@/core/entities/constructor';
|
||||||
|
import { ChatType, Peer, SendFileElement } from '@/core/entities';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { SendMsg, sendMsg } from '@/onebot11/action/msg/SendMsg';
|
||||||
|
import { uri2local } from '@/common/utils/file';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQFriendApi, NTQQUserApi } from '@/core';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
file: { type: 'string' },
|
||||||
|
name: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['user_id', 'file', 'name']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.GOCQHTTP_UploadPrivateFile;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
async getPeer(payload: Payload): Promise<Peer> {
|
||||||
|
if (payload.user_id) {
|
||||||
|
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
if (!peerUid) {
|
||||||
|
throw `私聊${payload.user_id}不存在`;
|
||||||
|
}
|
||||||
|
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
|
||||||
|
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid };
|
||||||
|
}
|
||||||
|
throw '缺少参数 user_id';
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const peer = await this.getPeer(payload);
|
||||||
|
let file = payload.file;
|
||||||
|
if (fs.existsSync(file)) {
|
||||||
|
file = `file://${file}`;
|
||||||
|
}
|
||||||
|
const downloadResult = await uri2local(file);
|
||||||
|
if (!downloadResult.success) {
|
||||||
|
throw new Error(downloadResult.errMsg);
|
||||||
|
}
|
||||||
|
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name);
|
||||||
|
await sendMsg(peer, [sendFileEle], [], true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot/action/group/DelEssenceMsg.ts
Normal file
31
src/onebot/action/group/DelEssenceMsg.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class DelEssenceMsg extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.DelEssenceMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
return await NTQQGroupApi.removeGroupEssence(
|
||||||
|
msg.Peer.peerUid,
|
||||||
|
msg.MsgId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot/action/group/GetGroupEssence.ts
Normal file
31
src/onebot/action/group/GetGroupEssence.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { getGroup } from '@/core/data';
|
||||||
|
import { OB11Group } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
|
import { GroupEssenceMsgRet, WebApi } from '@/core/apis/webapi';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
pages: { type: 'number' },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'pages']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetGroupEssence extends BaseAction<Payload, GroupEssenceMsgRet> {
|
||||||
|
actionName = ActionName.GoCQHTTP_GetEssenceMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const ret = await WebApi.getGroupEssenceMsg(payload.group_id.toString(), payload.pages.toString());
|
||||||
|
if (!ret) {
|
||||||
|
throw new Error('获取失败');
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot/action/group/GetGroupInfo.ts
Normal file
31
src/onebot/action/group/GetGroupInfo.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { getGroup } from '@/core/data';
|
||||||
|
import { OB11Group } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class GetGroupInfo extends BaseAction<Payload, OB11Group> {
|
||||||
|
actionName = ActionName.GetGroupInfo;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const group = await getGroup(payload.group_id.toString());
|
||||||
|
if (group) {
|
||||||
|
return OB11Constructor.group(group);
|
||||||
|
} else {
|
||||||
|
throw `群${payload.group_id}不存在`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GetGroupInfo;
|
27
src/onebot/action/group/GetGroupList.ts
Normal file
27
src/onebot/action/group/GetGroupList.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { OB11Group } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis';
|
||||||
|
import { Group } from '@/core/entities';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
// no_cache get时传字符串
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
no_cache: { type: ['boolean', 'string'] },
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class GetGroupList extends BaseAction<Payload, OB11Group[]> {
|
||||||
|
actionName = ActionName.GetGroupList;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
|
||||||
|
return OB11Constructor.groups(groupList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GetGroupList;
|
79
src/onebot/action/group/GetGroupMemberInfo.ts
Normal file
79
src/onebot/action/group/GetGroupMemberInfo.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { OB11GroupMember } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { logDebug } from '@/common/utils/log';
|
||||||
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { getGroupMember, selfInfo } from '@/core/data';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
no_cache: { type: ['boolean', 'string'] },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'user_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||||
|
actionName = ActionName.GetGroupMemberInfo;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||||
|
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
if (!uid) {
|
||||||
|
throw (`Uin2Uid Error ${payload.user_id}不存在`);
|
||||||
|
}
|
||||||
|
const member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
|
||||||
|
if (!member) {
|
||||||
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
||||||
|
logDebug('群成员详细信息结果', info);
|
||||||
|
Object.assign(member, info);
|
||||||
|
} catch (e) {
|
||||||
|
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||||
|
}
|
||||||
|
const date = Math.round(Date.now() / 1000);
|
||||||
|
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||||
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
|
const SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), selfInfo.uid, isNocache);
|
||||||
|
let isPrivilege = false;
|
||||||
|
if (SelfInfoInGroup) {
|
||||||
|
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;
|
||||||
|
}
|
||||||
|
if (isPrivilege) {
|
||||||
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
|
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||||
|
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||||
|
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||||
|
retMember.qage = webGroupMembers[i]?.qage;
|
||||||
|
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||||
|
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||||
|
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||||
|
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||||
|
retMember.last_sent_time = parseInt(last_send_time);
|
||||||
|
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
|
||||||
|
retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
|
||||||
|
}
|
||||||
|
return retMember;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default GetGroupMemberInfo;
|
84
src/onebot/action/group/GetGroupMemberInfoOld.ts
Normal file
84
src/onebot/action/group/GetGroupMemberInfoOld.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { OB11GroupMember } from '../../types';
|
||||||
|
import { getGroup, getGroupMember, groupMembers, selfInfo } from '@/core/data';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { logDebug } from '@/common/utils/log';
|
||||||
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
|
// no_cache get时传字符串
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
no_cache: { type: ['boolean', 'string'] },
|
||||||
|
},
|
||||||
|
required: ['group_id', 'user_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
||||||
|
actionName = ActionName.GetGroupMemberInfo;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
const V2Data = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), payload.user_id.toString(), payload.no_cache == true || payload.no_cache === 'true');
|
||||||
|
if (V2Data) {
|
||||||
|
return OB11Constructor.groupMember(payload.group_id.toString(), V2Data);
|
||||||
|
} else {
|
||||||
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const group = await getGroup(payload.group_id.toString());
|
||||||
|
const role = (await getGroupMember(payload.group_id, selfInfo.uin))?.role;
|
||||||
|
const isPrivilege = role === 3 || role === 4;
|
||||||
|
if (!group) {
|
||||||
|
throw (`群(${payload.group_id})不存在`);
|
||||||
|
}
|
||||||
|
if (payload.no_cache == true || payload.no_cache === 'true') {
|
||||||
|
groupMembers.set(group.groupCode, await NTQQGroupApi.getGroupMembers(payload.group_id.toString()));
|
||||||
|
}
|
||||||
|
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString());
|
||||||
|
//早返回
|
||||||
|
if (!member) {
|
||||||
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
|
}
|
||||||
|
//console.log('GetGroupMemberInfo', JSON.stringify(await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), member.uid, true), null, 4));
|
||||||
|
try {
|
||||||
|
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
||||||
|
logDebug('群成员详细信息结果', info);
|
||||||
|
Object.assign(member, info);
|
||||||
|
} catch (e) {
|
||||||
|
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||||
|
}
|
||||||
|
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||||
|
if (isPrivilege) {
|
||||||
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
|
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||||
|
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||||
|
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||||
|
retMember.qage = webGroupMembers[i]?.qage;
|
||||||
|
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||||
|
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||||
|
const last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||||
|
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||||
|
retMember.last_sent_time = parseInt(last_send_time);
|
||||||
|
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retMember;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default GetGroupMemberInfo;
|
104
src/onebot/action/group/GetGroupMemberList.ts
Normal file
104
src/onebot/action/group/GetGroupMemberList.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { getGroup, getGroupMember, selfInfo } from '@/core/data';
|
||||||
|
import { OB11GroupMember } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
no_cache: { type: ['boolean', 'string'] },
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
||||||
|
actionName = ActionName.GetGroupMemberList;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
|
||||||
|
|
||||||
|
const GroupList = await NTQQGroupApi.getGroups(isNocache);
|
||||||
|
const group = GroupList.find(item => item.groupCode == payload.group_id);
|
||||||
|
if (!group) {
|
||||||
|
throw (`群${payload.group_id}不存在`);
|
||||||
|
}
|
||||||
|
const groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
|
||||||
|
let _groupMembers = Array.from(groupMembers.values())
|
||||||
|
.map(item => { return OB11Constructor.groupMember(group.groupCode, item); });
|
||||||
|
|
||||||
|
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
|
||||||
|
// 转为Map 方便索引
|
||||||
|
const date = Math.round(Date.now() / 1000);
|
||||||
|
for (let i = 0, len = _groupMembers.length; i < len; i++) {
|
||||||
|
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
|
||||||
|
_groupMembers[i].join_time = date;
|
||||||
|
_groupMembers[i].last_sent_time = date;
|
||||||
|
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!requireMinNTQQBuild('26702')) {
|
||||||
|
const selfRole = groupMembers.get(selfInfo.uid)?.role;
|
||||||
|
const isPrivilege = selfRole === 3 || selfRole === 4;
|
||||||
|
|
||||||
|
if (isPrivilege) {
|
||||||
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
|
if (!webGroupMembers[i]?.uin) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
|
||||||
|
if (MemberData) {
|
||||||
|
MemberData.join_time = webGroupMembers[i]?.join_time;
|
||||||
|
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
|
||||||
|
MemberData.qage = webGroupMembers[i]?.qage;
|
||||||
|
MemberData.level = webGroupMembers[i]?.lv.level.toString();
|
||||||
|
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isNocache) {
|
||||||
|
const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取
|
||||||
|
for (const DateUin of DateMap.keys()) {
|
||||||
|
const MemberData = MemberMap.get(parseInt(DateUin));
|
||||||
|
if (MemberData) {
|
||||||
|
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
|
||||||
|
//join_time 有基础数据兜底
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_groupMembers.forEach(item => {
|
||||||
|
item.last_sent_time = date;
|
||||||
|
item.join_time = date;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_groupMembers.forEach(async item => {
|
||||||
|
item.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.lastSpeakTime || date.toString());
|
||||||
|
item.join_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.joinTime || date.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 还原索引到Array 一同返回
|
||||||
|
|
||||||
|
// let retData: any[] = [];
|
||||||
|
// for (let retMem of MemberMap.values()) {
|
||||||
|
// retMem.level = TypeConvert.toString(retMem.level) as any;
|
||||||
|
// retData.push(retMem)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _groupMembers = Array.from(retData);
|
||||||
|
|
||||||
|
_groupMembers = Array.from(MemberMap.values());
|
||||||
|
return _groupMembers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GetGroupMemberList;
|
57
src/onebot/action/group/GetGroupNotice.ts
Normal file
57
src/onebot/action/group/GetGroupNotice.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { WebApi, WebApiGroupNoticeFeed, WebApiGroupNoticeRet } from '@/core/apis/webapi';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
interface GroupNotice {
|
||||||
|
sender_id: number
|
||||||
|
publish_time: number
|
||||||
|
message: {
|
||||||
|
text: string
|
||||||
|
image: Array<{
|
||||||
|
height: string
|
||||||
|
width: string
|
||||||
|
id: string
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
type ApiGroupNotice = GroupNotice & WebApiGroupNoticeFeed;
|
||||||
|
export class GetGroupNotice extends BaseAction<Payload, GroupNotice[]> {
|
||||||
|
actionName = ActionName.GoCQHTTP_GetGroupNotice;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const group = payload.group_id.toString();
|
||||||
|
const ret = await WebApi.getGrouptNotice(group);
|
||||||
|
if (!ret) {
|
||||||
|
throw new Error('获取公告失败');
|
||||||
|
}
|
||||||
|
const retNotices: GroupNotice[] = new Array<ApiGroupNotice>();
|
||||||
|
for (const key in ret.feeds) {
|
||||||
|
const retApiNotice: WebApiGroupNoticeFeed = ret.feeds[key];
|
||||||
|
const retNotice: GroupNotice = {
|
||||||
|
// ...ret.feeds[key],
|
||||||
|
sender_id: retApiNotice.u,
|
||||||
|
publish_time: retApiNotice.pubt,
|
||||||
|
message: {
|
||||||
|
text: retApiNotice.msg.text,
|
||||||
|
image: retApiNotice.msg.pics?.map((pic) => {
|
||||||
|
return { id: pic.id, height: pic.h, width: pic.w };
|
||||||
|
}) || []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
retNotices.push(retNotice);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retNotices;
|
||||||
|
}
|
||||||
|
}
|
47
src/onebot/action/group/GetGroupSystemMsg.ts
Normal file
47
src/onebot/action/group/GetGroupSystemMsg.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { NTQQGroupApi, NTQQUserApi } from '@/core';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetGroupSystemMsg extends BaseAction<void, any> {
|
||||||
|
actionName = ActionName.GetGroupSystemMsg;
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
// 默认10条 该api未完整实现 包括响应数据规范化 类型规范化
|
||||||
|
const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(10);
|
||||||
|
const retData: any = { InvitedRequest: [], join_requests: [] };
|
||||||
|
for (const SSNotify of SingleScreenNotifies) {
|
||||||
|
if (SSNotify.type == 1) {
|
||||||
|
retData.InvitedRequest.push({
|
||||||
|
request_id: SSNotify.seq,
|
||||||
|
invitor_uin: await NTQQUserApi.getUinByUid(SSNotify.user1?.uid),
|
||||||
|
invitor_nick: SSNotify.user1?.nickName,
|
||||||
|
group_id: SSNotify.group?.groupCode,
|
||||||
|
group_name: SSNotify.group?.groupName,
|
||||||
|
checked: SSNotify.status === 1 ? false : true,
|
||||||
|
actor: await NTQQUserApi.getUinByUid(SSNotify.user2?.uid) || 0,
|
||||||
|
});
|
||||||
|
} else if (SSNotify.type == 7) {
|
||||||
|
retData.join_requests.push({
|
||||||
|
request_id: SSNotify.seq,
|
||||||
|
requester_uin: await NTQQUserApi.getUinByUid(SSNotify.user1?.uid),
|
||||||
|
requester_nick: SSNotify.user1?.nickName,
|
||||||
|
group_id: SSNotify.group?.groupCode,
|
||||||
|
group_name: SSNotify.group?.groupName,
|
||||||
|
checked: SSNotify.status === 1 ? false : true,
|
||||||
|
actor: await NTQQUserApi.getUinByUid(SSNotify.user2?.uid) || 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retData;
|
||||||
|
}
|
||||||
|
}
|
10
src/onebot/action/group/GetGuildList.ts
Normal file
10
src/onebot/action/group/GetGuildList.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
|
||||||
|
export default class GetGuildList extends BaseAction<null, null> {
|
||||||
|
actionName = ActionName.GetGuildList;
|
||||||
|
|
||||||
|
protected async _handle(payload: null): Promise<null> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
17
src/onebot/action/group/SendGroupMsg.ts
Normal file
17
src/onebot/action/group/SendGroupMsg.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import SendMsg, { ContextMode } from '../msg/SendMsg';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import { OB11PostSendMsg } from '../../types';
|
||||||
|
|
||||||
|
// 未检测参数
|
||||||
|
class SendGroupMsg extends SendMsg {
|
||||||
|
actionName = ActionName.SendGroupMsg;
|
||||||
|
contextMode: ContextMode = ContextMode.Group;
|
||||||
|
|
||||||
|
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
|
||||||
|
delete payload.user_id;
|
||||||
|
payload.message_type = 'group';
|
||||||
|
return super.check(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SendGroupMsg;
|
30
src/onebot/action/group/SetEssenceMsg.ts
Normal file
30
src/onebot/action/group/SetEssenceMsg.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQGroupApi, NTQQMsgApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetEssenceMsg extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetEssenceMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
return await NTQQGroupApi.addGroupEssence(
|
||||||
|
msg.Peer.peerUid,
|
||||||
|
msg.MsgId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot/action/group/SetGroupAddRequest.ts
Normal file
31
src/onebot/action/group/SetGroupAddRequest.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { GroupRequestOperateTypes } from '@/core/entities';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
flag: { type: 'string' },
|
||||||
|
approve: { type: ['string', 'boolean'] },
|
||||||
|
reason: { type: 'string', nullable: true, }
|
||||||
|
},
|
||||||
|
required: ['flag'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetGroupAddRequest extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetGroupAddRequest;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const flag = payload.flag.toString();
|
||||||
|
const approve = payload.approve?.toString() !== 'false';
|
||||||
|
await NTQQGroupApi.handleGroupRequest(flag,
|
||||||
|
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
||||||
|
payload.reason
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
33
src/onebot/action/group/SetGroupAdmin.ts
Normal file
33
src/onebot/action/group/SetGroupAdmin.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { getGroupMember } from '@/core/data';
|
||||||
|
import { GroupMemberRole } from '@/core/entities';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
user_id: { type: [ 'number' , 'string' ] },
|
||||||
|
enable: { type: 'boolean' }
|
||||||
|
},
|
||||||
|
required: ['group_id', 'user_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetGroupAdmin extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetGroupAdmin;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const member = await getGroupMember(payload.group_id, payload.user_id);
|
||||||
|
// 已经前置验证类型
|
||||||
|
const enable = payload.enable?.toString() !== 'false';
|
||||||
|
if (!member) {
|
||||||
|
throw `群成员${payload.user_id}不存在`;
|
||||||
|
}
|
||||||
|
await NTQQGroupApi.setMemberRole(payload.group_id.toString(), member.uid, enable ? GroupMemberRole.admin : GroupMemberRole.normal);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
31
src/onebot/action/group/SetGroupBan.ts
Normal file
31
src/onebot/action/group/SetGroupBan.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { getGroupMember } from '@/core/data';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
duration: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['group_id', 'user_id', 'duration']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetGroupBan extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetGroupBan;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const member = await getGroupMember(payload.group_id, payload.user_id);
|
||||||
|
if (!member) {
|
||||||
|
throw `群成员${payload.user_id}不存在`;
|
||||||
|
}
|
||||||
|
await NTQQGroupApi.banMember(payload.group_id.toString(),
|
||||||
|
[{ uid: member.uid, timeStamp: parseInt(payload.duration.toString()) }]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
30
src/onebot/action/group/SetGroupCard.ts
Normal file
30
src/onebot/action/group/SetGroupCard.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { getGroupMember } from '@/core/data';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
user_id: { type: [ 'number' , 'string' ] },
|
||||||
|
card: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['group_id', 'user_id', 'card']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetGroupCard extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetGroupCard;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const member = await getGroupMember(payload.group_id, payload.user_id);
|
||||||
|
if (!member) {
|
||||||
|
throw `群成员${payload.user_id}不存在`;
|
||||||
|
}
|
||||||
|
await NTQQGroupApi.setMemberCard(payload.group_id.toString(), member.uid, payload.card || '');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
32
src/onebot/action/group/SetGroupKick.ts
Normal file
32
src/onebot/action/group/SetGroupKick.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { getGroupMember } from '@/core/data';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
user_id: { type: [ 'number' , 'string' ] },
|
||||||
|
reject_add_request: { type: [ 'boolean' , 'string' ] }
|
||||||
|
},
|
||||||
|
required: ['group_id', 'user_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetGroupKick extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetGroupKick;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const member = await getGroupMember(payload.group_id, payload.user_id);
|
||||||
|
if (!member) {
|
||||||
|
throw `群成员${payload.user_id}不存在`;
|
||||||
|
}
|
||||||
|
const rejectReq = payload.reject_add_request?.toString() == 'true';
|
||||||
|
await NTQQGroupApi.kickMember(payload.group_id.toString(), [member.uid], rejectReq);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
29
src/onebot/action/group/SetGroupLeave.ts
Normal file
29
src/onebot/action/group/SetGroupLeave.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
import { log, logError } from '@/common/utils/log';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { deleteGroup } from '@/core/data';
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
is_dismiss: { type: 'boolean' }
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
export default class SetGroupLeave extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetGroupLeave;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
try {
|
||||||
|
await NTQQGroupApi.quitGroup(payload.group_id.toString());
|
||||||
|
deleteGroup(payload.group_id.toString());
|
||||||
|
} catch (e) {
|
||||||
|
logError('退群失败', e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/onebot/action/group/SetGroupName.ts
Normal file
24
src/onebot/action/group/SetGroupName.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
group_name: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['group_id', 'group_name']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
export default class SetGroupName extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetGroupName;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
|
||||||
|
await NTQQGroupApi.setGroupName(payload.group_id.toString(), payload.group_name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
25
src/onebot/action/group/SetGroupWholeBan.ts
Normal file
25
src/onebot/action/group/SetGroupWholeBan.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: [ 'number' , 'string' ] },
|
||||||
|
enable: { type: ['boolean','string'] }
|
||||||
|
},
|
||||||
|
required: ['group_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetGroupWholeBan extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetGroupWholeBan;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const enable = payload.enable?.toString() !== 'false';
|
||||||
|
await NTQQGroupApi.banGroup(payload.group_id.toString(), enable);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
180
src/onebot/action/index.ts
Normal file
180
src/onebot/action/index.ts
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import GetMsg from './msg/GetMsg';
|
||||||
|
import GetLoginInfo from './system/GetLoginInfo';
|
||||||
|
import GetFriendList from './user/GetFriendList';
|
||||||
|
import GetGroupList from './group/GetGroupList';
|
||||||
|
import GetGroupInfo from './group/GetGroupInfo';
|
||||||
|
import GetGroupMemberList from './group/GetGroupMemberList';
|
||||||
|
import GetGroupMemberInfo from './group/GetGroupMemberInfo';
|
||||||
|
import SendGroupMsg from './group/SendGroupMsg';
|
||||||
|
import SendPrivateMsg from './msg/SendPrivateMsg';
|
||||||
|
import SendMsg from './msg/SendMsg';
|
||||||
|
import DeleteMsg from './msg/DeleteMsg';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
import GetVersionInfo from './system/GetVersionInfo';
|
||||||
|
import CanSendRecord from './system/CanSendRecord';
|
||||||
|
import CanSendImage from './system/CanSendImage';
|
||||||
|
import GetStatus from './system/GetStatus';
|
||||||
|
import {
|
||||||
|
GoCQHTTPSendForwardMsg,
|
||||||
|
GoCQHTTPSendGroupForwardMsg,
|
||||||
|
GoCQHTTPSendPrivateForwardMsg
|
||||||
|
} from './go-cqhttp/SendForwardMsg';
|
||||||
|
import GoCQHTTPGetStrangerInfo from './go-cqhttp/GetStrangerInfo';
|
||||||
|
import SendLike from './user/SendLike';
|
||||||
|
import SetGroupAddRequest from './group/SetGroupAddRequest';
|
||||||
|
import SetGroupLeave from './group/SetGroupLeave';
|
||||||
|
import GetGuildList from './group/GetGuildList';
|
||||||
|
import Debug from '@/onebot11/action/extends/Debug';
|
||||||
|
import SetFriendAddRequest from './user/SetFriendAddRequest';
|
||||||
|
import SetGroupWholeBan from './group/SetGroupWholeBan';
|
||||||
|
import SetGroupName from './group/SetGroupName';
|
||||||
|
import SetGroupBan from './group/SetGroupBan';
|
||||||
|
import SetGroupKick from './group/SetGroupKick';
|
||||||
|
import SetGroupAdmin from './group/SetGroupAdmin';
|
||||||
|
import SetGroupCard from './group/SetGroupCard';
|
||||||
|
import GetImage from './file/GetImage';
|
||||||
|
import GetRecord from './file/GetRecord';
|
||||||
|
import { GoCQHTTPMarkMsgAsRead, MarkAllMsgAsRead, MarkGroupMsgAsRead, MarkPrivateMsgAsRead } from './msg/MarkMsgAsRead';
|
||||||
|
import CleanCache from './system/CleanCache';
|
||||||
|
import GoCQHTTPUploadGroupFile from './go-cqhttp/UploadGroupFile';
|
||||||
|
import { GetConfigAction, SetConfigAction } from '@/onebot11/action/extends/Config';
|
||||||
|
import GetGroupAddRequest from '@/onebot11/action/extends/GetGroupAddRequest';
|
||||||
|
import SetQQAvatar from '@/onebot11/action/extends/SetQQAvatar';
|
||||||
|
import GoCQHTTPDownloadFile from './go-cqhttp/DownloadFile';
|
||||||
|
import GoCQHTTPGetGroupMsgHistory from './go-cqhttp/GetGroupMsgHistory';
|
||||||
|
import GetFile from './file/GetFile';
|
||||||
|
import { GoCQHTTPGetForwardMsgAction } from './go-cqhttp/GetForwardMsg';
|
||||||
|
import GetFriendMsgHistory from './go-cqhttp/GetFriendMsgHistory';
|
||||||
|
import { GetCookies } from './user/GetCookies';
|
||||||
|
import { SetMsgEmojiLike } from '@/onebot11/action/msg/SetMsgEmojiLike';
|
||||||
|
import { GetRobotUinRange } from './extends/GetRobotUinRange';
|
||||||
|
import { SetOnlineStatus } from './extends/SetOnlineStatus';
|
||||||
|
import { GetGroupNotice } from './group/GetGroupNotice';
|
||||||
|
import { GetGroupEssence } from './group/GetGroupEssence';
|
||||||
|
import { ForwardFriendSingleMsg, ForwardGroupSingleMsg } from '@/onebot11/action/msg/ForwardSingleMsg';
|
||||||
|
import { GetFriendWithCategory } from './extends/GetFriendWithCategory';
|
||||||
|
import { SendGroupNotice } from './go-cqhttp/SendGroupNotice';
|
||||||
|
import { Reboot, RebootNormal } from './system/Reboot';
|
||||||
|
import { GetGroupHonorInfo } from './go-cqhttp/GetGroupHonorInfo';
|
||||||
|
import { GoCQHTTPHandleQuickAction } from './go-cqhttp/QuickAction';
|
||||||
|
import { GetGroupSystemMsg } from './group/GetGroupSystemMsg';
|
||||||
|
import { GetOnlineClient } from './go-cqhttp/GetOnlineClient';
|
||||||
|
import { IOCRImage, OCRImage } from './extends/OCRImage';
|
||||||
|
import { GetGroupFileCount } from './file/GetGroupFileCount';
|
||||||
|
import { GetGroupFileList } from './file/GetGroupFileList';
|
||||||
|
import { TranslateEnWordToZn } from './extends/TranslateEnWordToZn';
|
||||||
|
import { SetGroupFileFolder } from './file/SetGroupFileFolder';
|
||||||
|
import { DelGroupFile } from './file/DelGroupFile';
|
||||||
|
import { DelGroupFileFolder } from './file/DelGroupFileFolder';
|
||||||
|
import { SetSelfProfile } from './extends/SetSelfProfile';
|
||||||
|
import { shareGroupEx, sharePeer } from './extends/sharePeer';
|
||||||
|
import { CreateCollection } from './extends/CreateCollection';
|
||||||
|
import { SetLongNick } from './extends/SetLongNick';
|
||||||
|
import DelEssenceMsg from './group/DelEssenceMsg';
|
||||||
|
import SetEssenceMsg from './group/SetEssenceMsg';
|
||||||
|
import GetRecentContact from './user/GetRecentContact';
|
||||||
|
import { GetProfileLike } from './extends/GetProfileLike';
|
||||||
|
import SetGroupHeader from './extends/SetGroupHeader';
|
||||||
|
import { FetchCustomFace } from './extends/FetchCustomFace';
|
||||||
|
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
|
||||||
|
import TestApi01 from './extends/TestApi01';
|
||||||
|
import { FetchEmojioLike } from './extends/FetchEmojioLike';
|
||||||
|
|
||||||
|
export const actionHandlers = [
|
||||||
|
new FetchEmojioLike(),
|
||||||
|
new RebootNormal(),
|
||||||
|
new GetFile(),
|
||||||
|
new Debug(),
|
||||||
|
new Reboot(),
|
||||||
|
new SetSelfProfile(),
|
||||||
|
new shareGroupEx(),
|
||||||
|
new sharePeer(),
|
||||||
|
new CreateCollection(),
|
||||||
|
new SetLongNick(),
|
||||||
|
new ForwardFriendSingleMsg(),
|
||||||
|
new ForwardGroupSingleMsg(),
|
||||||
|
new MarkGroupMsgAsRead(),
|
||||||
|
new MarkPrivateMsgAsRead(),
|
||||||
|
new SetQQAvatar(),
|
||||||
|
new TranslateEnWordToZn(),
|
||||||
|
new GetGroupFileCount(),
|
||||||
|
new GetGroupFileList(),
|
||||||
|
new SetGroupFileFolder(),
|
||||||
|
new DelGroupFile(),
|
||||||
|
new DelGroupFileFolder(),
|
||||||
|
// onebot11
|
||||||
|
new SendLike(),
|
||||||
|
new GetMsg(),
|
||||||
|
new GetLoginInfo(),
|
||||||
|
new GetFriendList(),
|
||||||
|
new GetGroupList(),
|
||||||
|
new GetGroupInfo(),
|
||||||
|
new GetGroupMemberList(),
|
||||||
|
new GetGroupMemberInfo(),
|
||||||
|
new SendGroupMsg(),
|
||||||
|
new SendPrivateMsg(),
|
||||||
|
new SendMsg(),
|
||||||
|
new DeleteMsg(),
|
||||||
|
new SetGroupAddRequest(),
|
||||||
|
new SetFriendAddRequest(),
|
||||||
|
new SetGroupLeave(),
|
||||||
|
new GetVersionInfo(),
|
||||||
|
new CanSendRecord(),
|
||||||
|
new CanSendImage(),
|
||||||
|
new GetStatus(),
|
||||||
|
new SetGroupWholeBan(),
|
||||||
|
new SetGroupBan(),
|
||||||
|
new SetGroupKick(),
|
||||||
|
new SetGroupAdmin(),
|
||||||
|
new SetGroupName(),
|
||||||
|
new SetGroupCard(),
|
||||||
|
new GetImage(),
|
||||||
|
new GetRecord(),
|
||||||
|
new SetMsgEmojiLike(),
|
||||||
|
new GetCookies(),
|
||||||
|
new SetOnlineStatus(),
|
||||||
|
new GetRobotUinRange(),
|
||||||
|
new GetFriendWithCategory(),
|
||||||
|
//以下为go-cqhttp api
|
||||||
|
new GetOnlineClient(),
|
||||||
|
new OCRImage(),
|
||||||
|
new IOCRImage(),
|
||||||
|
new GetGroupHonorInfo(),
|
||||||
|
new SendGroupNotice(),
|
||||||
|
new GetGroupNotice(),
|
||||||
|
new GetGroupEssence(),
|
||||||
|
new GoCQHTTPSendForwardMsg(),
|
||||||
|
new GoCQHTTPSendGroupForwardMsg(),
|
||||||
|
new GoCQHTTPSendPrivateForwardMsg(),
|
||||||
|
new GoCQHTTPGetStrangerInfo(),
|
||||||
|
new GoCQHTTPDownloadFile(),
|
||||||
|
new GetGuildList(),
|
||||||
|
new GoCQHTTPMarkMsgAsRead(),
|
||||||
|
new GoCQHTTPUploadGroupFile(),
|
||||||
|
new GoCQHTTPGetGroupMsgHistory(),
|
||||||
|
new GoCQHTTPGetForwardMsgAction(),
|
||||||
|
new GetFriendMsgHistory(),
|
||||||
|
new GoCQHTTPHandleQuickAction(),
|
||||||
|
new GetGroupSystemMsg(),
|
||||||
|
new DelEssenceMsg(),
|
||||||
|
new SetEssenceMsg(),
|
||||||
|
new GetRecentContact(),
|
||||||
|
new MarkAllMsgAsRead(),
|
||||||
|
new GetProfileLike(),
|
||||||
|
new SetGroupHeader(),
|
||||||
|
new FetchCustomFace(),
|
||||||
|
new GoCQHTTPUploadPrivateFile(),
|
||||||
|
new TestApi01()
|
||||||
|
];
|
||||||
|
function initActionMap() {
|
||||||
|
const actionMap = new Map<string, BaseAction<any, any>>();
|
||||||
|
for (const action of actionHandlers) {
|
||||||
|
actionMap.set(action.actionName, action);
|
||||||
|
actionMap.set(action.actionName + '_async', action);
|
||||||
|
actionMap.set(action.actionName + '_rate_limited', action);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actionMap = initActionMap();
|
54
src/onebot/action/msg/DeleteMsg.ts
Normal file
54
src/onebot/action/msg/DeleteMsg.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
import { sleep } from '@/common/utils/helper';
|
||||||
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
|
import { NodeIKernelMsgListener } from '@/core';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: {
|
||||||
|
oneOf: [
|
||||||
|
{ type: 'number' },
|
||||||
|
{ type: 'string' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class DeleteMsg extends BaseAction<Payload, void> {
|
||||||
|
actionName = ActionName.DeleteMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const msg = MessageUnique.getMsgIdAndPeerByShortId(Number(payload.message_id));
|
||||||
|
if (msg) {
|
||||||
|
let ret = NTEventDispatch.RegisterListen<NodeIKernelMsgListener['onMsgInfoListUpdate']>
|
||||||
|
(
|
||||||
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
|
1,
|
||||||
|
5000,
|
||||||
|
(msgs) => {
|
||||||
|
if (msgs.find(m => m.msgId === msg.MsgId && m.recallTime !== '0')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
).catch(e => new Promise<undefined>((resolve, reject) => { resolve(undefined) }));
|
||||||
|
await NTQQMsgApi.recallMsg(msg.Peer, [msg.MsgId]);
|
||||||
|
let data = await ret;
|
||||||
|
if (!data) {
|
||||||
|
throw new Error('Recall failed');
|
||||||
|
}
|
||||||
|
//await sleep(100);
|
||||||
|
//await NTQQMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DeleteMsg;
|
57
src/onebot/action/msg/ForwardSingleMsg.ts
Normal file
57
src/onebot/action/msg/ForwardSingleMsg.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
import { ChatType, Peer } from '@/core/entities';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: 'number' },
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
user_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class ForwardSingleMsg extends BaseAction<Payload, null> {
|
||||||
|
protected async getTargetPeer(payload: Payload): Promise<Peer> {
|
||||||
|
if (payload.user_id) {
|
||||||
|
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
if (!peerUid) {
|
||||||
|
throw new Error(`无法找到私聊对象${payload.user_id}`);
|
||||||
|
}
|
||||||
|
return { chatType: ChatType.friend, peerUid };
|
||||||
|
}
|
||||||
|
return { chatType: ChatType.group, peerUid: payload.group_id!.toString() };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(payload.message_id);
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error(`无法找到消息${payload.message_id}`);
|
||||||
|
}
|
||||||
|
const peer = await this.getTargetPeer(payload);
|
||||||
|
const ret = await NTQQMsgApi.forwardMsg(msg.Peer,
|
||||||
|
peer,
|
||||||
|
[msg.MsgId],
|
||||||
|
);
|
||||||
|
if (ret.result !== 0) {
|
||||||
|
throw new Error(`转发消息失败 ${ret.errMsg}`);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ForwardFriendSingleMsg extends ForwardSingleMsg {
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
actionName = ActionName.ForwardFriendSingleMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ForwardGroupSingleMsg extends ForwardSingleMsg {
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
actionName = ActionName.ForwardGroupSingleMsg;
|
||||||
|
}
|
50
src/onebot/action/msg/GetMsg.ts
Normal file
50
src/onebot/action/msg/GetMsg.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { OB11Message } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
import { NTQQMsgApi } from '@/core';
|
||||||
|
|
||||||
|
|
||||||
|
export type ReturnDataType = OB11Message
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['number', 'string'] },
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class GetMsg extends BaseAction<Payload, OB11Message> {
|
||||||
|
actionName = ActionName.GetMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
// log("history msg ids", Object.keys(msgHistory));
|
||||||
|
if (!payload.message_id) {
|
||||||
|
throw Error('参数message_id不能为空');
|
||||||
|
}
|
||||||
|
const MsgShortId = await MessageUnique.getShortIdByMsgId(payload.message_id.toString());
|
||||||
|
const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
|
||||||
|
if (!msgIdWithPeer) {
|
||||||
|
throw ('消息不存在');
|
||||||
|
}
|
||||||
|
const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType };
|
||||||
|
const msg = await NTQQMsgApi.getMsgsByMsgId(
|
||||||
|
peer,
|
||||||
|
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
|
||||||
|
const retMsg = await OB11Constructor.message(msg.msgList[0]);
|
||||||
|
try {
|
||||||
|
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!;
|
||||||
|
retMsg.message_seq = retMsg.message_id;
|
||||||
|
retMsg.real_id = retMsg.message_id;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
return retMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GetMsg;
|
71
src/onebot/action/msg/MarkMsgAsRead.ts
Normal file
71
src/onebot/action/msg/MarkMsgAsRead.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { ChatType, Peer } from '@/core/entities';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQFriendApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
group_id: { type: ['number', 'string'] }
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type PlayloadType = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
|
||||||
|
async getPeer(payload: PlayloadType): Promise<Peer> {
|
||||||
|
if (payload.user_id) {
|
||||||
|
const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
if (!peerUid) {
|
||||||
|
throw `私聊${payload.user_id}不存在`;
|
||||||
|
}
|
||||||
|
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
|
||||||
|
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid };
|
||||||
|
}
|
||||||
|
if (!payload.group_id) {
|
||||||
|
throw '缺少参数 group_id 或 user_id';
|
||||||
|
}
|
||||||
|
return { chatType: ChatType.group, peerUid: payload.group_id.toString() };
|
||||||
|
}
|
||||||
|
protected async _handle(payload: PlayloadType): Promise<null> {
|
||||||
|
// 调用API
|
||||||
|
const ret = await NTQQMsgApi.setMsgRead(await this.getPeer(payload));
|
||||||
|
if (ret.result != 0) {
|
||||||
|
throw ('设置已读失败,' + ret.errMsg);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 以下为非标准实现
|
||||||
|
export class MarkPrivateMsgAsRead extends MarkMsgAsRead {
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
actionName = ActionName.MarkPrivateMsgAsRead;
|
||||||
|
}
|
||||||
|
export class MarkGroupMsgAsRead extends MarkMsgAsRead {
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
actionName = ActionName.MarkGroupMsgAsRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface Payload {
|
||||||
|
message_id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GoCQHTTPMarkMsgAsRead extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.GoCQHTTP_MarkMsgAsRead;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MarkAllMsgAsRead extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName._MarkAllMsgAsRead;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
await NTQQMsgApi.markallMsgAsRead();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
36
src/onebot/action/msg/SendMsg/check-send-message.ts
Normal file
36
src/onebot/action/msg/SendMsg/check-send-message.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { OB11MessageData } from '@/onebot11/types';
|
||||||
|
|
||||||
|
function checkSendMessage(sendMsgList: OB11MessageData[]) {
|
||||||
|
function checkUri(uri: string): boolean {
|
||||||
|
const pattern = /^(file:\/\/|http:\/\/|https:\/\/|base64:\/\/)/;
|
||||||
|
return pattern.test(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const msg of sendMsgList) {
|
||||||
|
if (msg['type'] && msg['data']) {
|
||||||
|
const type = msg['type'];
|
||||||
|
const data = msg['data'];
|
||||||
|
if (type === 'text' && !data['text']) {
|
||||||
|
return 400;
|
||||||
|
} else if (['image', 'voice', 'record'].includes(type)) {
|
||||||
|
if (!data['file']) {
|
||||||
|
return 400;
|
||||||
|
} else {
|
||||||
|
if (checkUri(data['file'])) {
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (type === 'at' && !data['qq']) {
|
||||||
|
return 400;
|
||||||
|
} else if (type === 'reply' && !data['id']) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 200;
|
||||||
|
}
|
248
src/onebot/action/msg/SendMsg/create-send-elements.ts
Normal file
248
src/onebot/action/msg/SendMsg/create-send-elements.ts
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import { OB11MessageData, OB11MessageDataType, OB11MessageFileBase } from '@/onebot11/types';
|
||||||
|
import {
|
||||||
|
AtType,
|
||||||
|
CustomMusicSignPostData,
|
||||||
|
Group,
|
||||||
|
IdMusicSignPostData,
|
||||||
|
NTQQFileApi,
|
||||||
|
NTQQMsgApi,
|
||||||
|
Peer,
|
||||||
|
SendArkElement,
|
||||||
|
SendMessageElement,
|
||||||
|
SendMsgElementConstructor,
|
||||||
|
SignMusicWrapper
|
||||||
|
} from '@/core';
|
||||||
|
import { getGroupMember } from '@/core/data';
|
||||||
|
import { logError, logWarn } from '@/common/utils/log';
|
||||||
|
import { uri2local } from '@/common/utils/file';
|
||||||
|
import { ob11Config } from '@/onebot11/config';
|
||||||
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
console.log(process.pid)
|
||||||
|
export type MessageContext = {
|
||||||
|
deleteAfterSentFiles: string[],
|
||||||
|
peer:Peer
|
||||||
|
}
|
||||||
|
async function handleOb11FileLikeMessage(
|
||||||
|
{ data: inputdata }: OB11MessageFileBase,
|
||||||
|
{ deleteAfterSentFiles }: MessageContext
|
||||||
|
) {
|
||||||
|
//有的奇怪的框架将url作为参数 而不是file 此时优先url 同时注意可能传入的是非file://开头的目录 By Mlikiowa
|
||||||
|
const { path, isLocal, fileName, errMsg,success } = (await uri2local(inputdata?.url || inputdata.file));
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
logError('文件下载失败', errMsg);
|
||||||
|
throw Error('文件下载失败' + errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLocal) { // 只删除http和base64转过来的文件
|
||||||
|
deleteAfterSentFiles.push(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { path, fileName: inputdata.name || fileName };
|
||||||
|
}
|
||||||
|
|
||||||
|
const _handlers: {
|
||||||
|
[Key in OB11MessageDataType]: (
|
||||||
|
sendMsg: Extract<OB11MessageData, { type: Key }>,
|
||||||
|
// This picks the correct message type out
|
||||||
|
// How great the type system of TypeScript is!
|
||||||
|
context: MessageContext
|
||||||
|
) => Promise<SendMessageElement | undefined>
|
||||||
|
} = {
|
||||||
|
[OB11MessageDataType.text]: async ({ data: { text } }) => SendMsgElementConstructor.text(text),
|
||||||
|
|
||||||
|
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => {
|
||||||
|
if (!context.peer) return undefined;
|
||||||
|
|
||||||
|
if (atQQ === 'all') return SendMsgElementConstructor.at(atQQ, atQQ, AtType.atAll, '全体成员');
|
||||||
|
|
||||||
|
// then the qq is a group member
|
||||||
|
const atMember = await getGroupMember(context.peer.peerUid, atQQ);
|
||||||
|
return atMember ?
|
||||||
|
SendMsgElementConstructor.at(atQQ, atMember.uid, AtType.atUser, atMember.cardName || atMember.nick) :
|
||||||
|
undefined;
|
||||||
|
},
|
||||||
|
[OB11MessageDataType.reply]: async ({ data: { id } }) => {
|
||||||
|
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
|
||||||
|
if (!replyMsgM) {
|
||||||
|
logWarn('回复消息不存在', id);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const replyMsg = (await NTQQMsgApi.getMsgsByMsgId(replyMsgM?.Peer!, [replyMsgM?.MsgId!])).msgList[0];
|
||||||
|
return replyMsg ?
|
||||||
|
SendMsgElementConstructor.reply(replyMsg.msgSeq, replyMsg.msgId, replyMsg.senderUin!, replyMsg.senderUin!) :
|
||||||
|
undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
[OB11MessageDataType.face]: async ({ data: { id } }) => SendMsgElementConstructor.face(parseInt(id)),
|
||||||
|
|
||||||
|
[OB11MessageDataType.mface]: async ({
|
||||||
|
data: {
|
||||||
|
emoji_package_id,
|
||||||
|
emoji_id,
|
||||||
|
key,
|
||||||
|
summary
|
||||||
|
}
|
||||||
|
}) => SendMsgElementConstructor.mface(emoji_package_id, emoji_id, key, summary),
|
||||||
|
|
||||||
|
// File service
|
||||||
|
|
||||||
|
[OB11MessageDataType.image]: async (sendMsg, context) => {
|
||||||
|
const PicEle = await SendMsgElementConstructor.pic(
|
||||||
|
(await handleOb11FileLikeMessage(sendMsg, context)).path,
|
||||||
|
sendMsg.data.summary || '',
|
||||||
|
sendMsg.data.subType || 0
|
||||||
|
);
|
||||||
|
context.deleteAfterSentFiles.push(PicEle.picElement.sourcePath);
|
||||||
|
return PicEle;
|
||||||
|
}
|
||||||
|
, // currently not supported
|
||||||
|
|
||||||
|
[OB11MessageDataType.file]: async (sendMsg, context) => {
|
||||||
|
const { path, fileName } = await handleOb11FileLikeMessage(sendMsg, context);
|
||||||
|
//logDebug('发送文件', path, fileName);
|
||||||
|
const FileEle = await SendMsgElementConstructor.file(path, fileName);
|
||||||
|
// 清除Upload的应该
|
||||||
|
// context.deleteAfterSentFiles.push(fileName || FileEle.fileElement.filePath);
|
||||||
|
return FileEle;
|
||||||
|
},
|
||||||
|
|
||||||
|
[OB11MessageDataType.video]: async (sendMsg, context) => {
|
||||||
|
const { path, fileName } = await handleOb11FileLikeMessage(sendMsg, context);
|
||||||
|
|
||||||
|
//logDebug('发送视频', path, fileName);
|
||||||
|
let thumb = sendMsg.data.thumb;
|
||||||
|
if (thumb) {
|
||||||
|
const uri2LocalRes = await uri2local(thumb);
|
||||||
|
if (uri2LocalRes.success) thumb = uri2LocalRes.path;
|
||||||
|
}
|
||||||
|
const videoEle = await SendMsgElementConstructor.video(path, fileName, thumb);
|
||||||
|
//未测试
|
||||||
|
context.deleteAfterSentFiles.push(videoEle.videoElement.filePath);
|
||||||
|
return videoEle;
|
||||||
|
},
|
||||||
|
[OB11MessageDataType.miniapp]: async ({ data: any }) => SendMsgElementConstructor.miniapp(),
|
||||||
|
|
||||||
|
[OB11MessageDataType.voice]: async (sendMsg, context) =>
|
||||||
|
SendMsgElementConstructor.ptt((await handleOb11FileLikeMessage(sendMsg, context)).path),
|
||||||
|
|
||||||
|
[OB11MessageDataType.json]: async ({ data: { data } }) => SendMsgElementConstructor.ark(data),
|
||||||
|
|
||||||
|
[OB11MessageDataType.dice]: async ({ data: { result } }) => SendMsgElementConstructor.dice(result),
|
||||||
|
|
||||||
|
[OB11MessageDataType.RPS]: async ({ data: { result } }) => SendMsgElementConstructor.rps(result),
|
||||||
|
|
||||||
|
[OB11MessageDataType.markdown]: async ({ data: { content } }) => SendMsgElementConstructor.markdown(content),
|
||||||
|
|
||||||
|
[OB11MessageDataType.music]: async ({ data }) => {
|
||||||
|
// 保留, 直到...找到更好的解决方案
|
||||||
|
if (data.type === 'custom') {
|
||||||
|
if (!data.url) {
|
||||||
|
logError('自定义音卡缺少参数url');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!data.audio) {
|
||||||
|
logError('自定义音卡缺少参数audio');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!data.title) {
|
||||||
|
logError('自定义音卡缺少参数title');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!['qq', '163'].includes(data.type)) {
|
||||||
|
logError('音乐卡片type错误, 只支持qq、163、custom,当前type:', data.type);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!data.id) {
|
||||||
|
logError('音乐卡片缺少参数id');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let postData: IdMusicSignPostData | CustomMusicSignPostData;
|
||||||
|
if (data.type === 'custom' && data.content) {
|
||||||
|
const { content, ...others } = data;
|
||||||
|
postData = { singer: content, ...others };
|
||||||
|
} else {
|
||||||
|
postData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signUrl = ob11Config.musicSignUrl;
|
||||||
|
if (!signUrl) {
|
||||||
|
if (data.type === 'qq') {
|
||||||
|
const musicJson = (await SignMusicWrapper(data.id.toString())).data.arkResult.slice(0, -1);
|
||||||
|
return SendMsgElementConstructor.ark(musicJson);
|
||||||
|
}
|
||||||
|
throw Error('音乐消息签名地址未配置');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const musicJson = await RequestUtil.HttpGetJson<any>(signUrl, 'POST', postData);
|
||||||
|
return SendMsgElementConstructor.ark(musicJson);
|
||||||
|
} catch (e) {
|
||||||
|
logError('生成音乐消息失败', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
[OB11MessageDataType.node]: async () => undefined,
|
||||||
|
|
||||||
|
[OB11MessageDataType.forward]: async () => undefined,
|
||||||
|
|
||||||
|
[OB11MessageDataType.xml]: async () => undefined,
|
||||||
|
|
||||||
|
[OB11MessageDataType.poke]: async () => undefined,
|
||||||
|
|
||||||
|
[OB11MessageDataType.Location]: async () => {
|
||||||
|
return SendMsgElementConstructor.location();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlers = <{
|
||||||
|
[Key in OB11MessageDataType]: (
|
||||||
|
sendMsg: OB11MessageData,
|
||||||
|
context: MessageContext
|
||||||
|
) => Promise<SendMessageElement | undefined>
|
||||||
|
}>_handlers;
|
||||||
|
|
||||||
|
export default async function createSendElements(
|
||||||
|
messageData: OB11MessageData[],
|
||||||
|
peer: Peer,
|
||||||
|
ignoreTypes: OB11MessageDataType[] = []
|
||||||
|
) {
|
||||||
|
const deleteAfterSentFiles: string[] = [];
|
||||||
|
const callResultList: Array<Promise<SendMessageElement | undefined>> = [];
|
||||||
|
for (const sendMsg of messageData) {
|
||||||
|
if (ignoreTypes.includes(sendMsg.type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const callResult = handlers[sendMsg.type](
|
||||||
|
sendMsg,
|
||||||
|
{ peer, deleteAfterSentFiles }
|
||||||
|
)?.catch(undefined);
|
||||||
|
callResultList.push(callResult);
|
||||||
|
}
|
||||||
|
const ret = await Promise.all(callResultList);
|
||||||
|
const sendElements: SendMessageElement[] = ret.filter(ele => ele) as SendMessageElement[];
|
||||||
|
return { sendElements, deleteAfterSentFiles };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createSendElementsParallel(
|
||||||
|
messageData: OB11MessageData[],
|
||||||
|
peer: Peer,
|
||||||
|
ignoreTypes: OB11MessageDataType[] = []
|
||||||
|
) {
|
||||||
|
const deleteAfterSentFiles: string[] = [];
|
||||||
|
const sendElements = <SendMessageElement[]>(
|
||||||
|
await Promise.all(
|
||||||
|
messageData.map(async sendMsg => ignoreTypes.includes(sendMsg.type) ?
|
||||||
|
undefined :
|
||||||
|
handlers[sendMsg.type](sendMsg, { peer, deleteAfterSentFiles }))
|
||||||
|
).then(
|
||||||
|
results => results.filter(
|
||||||
|
element => element !== undefined
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return { sendElements, deleteAfterSentFiles };
|
||||||
|
}
|
120
src/onebot/action/msg/SendMsg/handle-forward-node.ts
Normal file
120
src/onebot/action/msg/SendMsg/handle-forward-node.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { ChatType, ElementType, Group, NTQQMsgApi, Peer, RawMessage, SendMessageElement } from '@/core';
|
||||||
|
import { OB11MessageDataType, OB11MessageNode } from '@/onebot11/types';
|
||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
import createSendElements from '@/onebot11/action/msg/SendMsg/create-send-elements';
|
||||||
|
import { logDebug, logError } from '@/common/utils/log';
|
||||||
|
import { sleep } from '@/common/utils/helper';
|
||||||
|
import { normalize, sendMsg } from '@/onebot11/action/msg/SendMsg/index';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
async function cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
|
||||||
|
const selfPeer = {
|
||||||
|
chatType: ChatType.friend,
|
||||||
|
peerUid: selfInfo.uid
|
||||||
|
};
|
||||||
|
|
||||||
|
//logDebug('克隆的目标消息', msg);
|
||||||
|
|
||||||
|
const sendElements: SendMessageElement[] = [];
|
||||||
|
|
||||||
|
for (const element of msg.elements) {
|
||||||
|
sendElements.push(element as SendMessageElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendElements.length === 0) {
|
||||||
|
logDebug('需要clone的消息无法解析,将会忽略掉', msg);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const nodeMsg = await NTQQMsgApi.sendMsg(selfPeer, sendElements, true);
|
||||||
|
return nodeMsg;
|
||||||
|
} catch (e) {
|
||||||
|
logError(e, '克隆转发消息失败,将忽略本条消息', msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function handleForwardNode(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
|
||||||
|
const selfPeer = {
|
||||||
|
chatType: ChatType.friend,
|
||||||
|
peerUid: selfInfo.uid
|
||||||
|
};
|
||||||
|
let nodeMsgIds: string[] = [];
|
||||||
|
for (const messageNode of messageNodes) {
|
||||||
|
const nodeId = messageNode.data.id;
|
||||||
|
if (nodeId) {
|
||||||
|
//对Mgsid和OB11ID混用情况兜底
|
||||||
|
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
|
||||||
|
if (!nodeMsg) {
|
||||||
|
logError('转发消息失败,未找到消息', nodeId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nodeMsgIds.push(nodeMsg.MsgId);
|
||||||
|
} else {
|
||||||
|
// 自定义的消息
|
||||||
|
try {
|
||||||
|
let OB11Data = normalize(messageNode.data.content);
|
||||||
|
//筛选node消息
|
||||||
|
let isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
|
||||||
|
if (isNodeMsg !== 0) {
|
||||||
|
if (isNodeMsg !== OB11Data.length) { logError('子消息中包含非node消息 跳过不合法部分'); continue; }
|
||||||
|
const nodeMsg = await handleForwardNode(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
|
||||||
|
if (nodeMsg) { nodeMsgIds.push(nodeMsg.msgId); MessageUnique.createMsg(selfPeer, nodeMsg.msgId) };
|
||||||
|
//完成子卡片生成跳过后续
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { sendElements } = await createSendElements(OB11Data, destPeer);
|
||||||
|
//拆分消息
|
||||||
|
let MixElement = sendElements.filter(element => element.elementType !== ElementType.FILE && element.elementType !== ElementType.VIDEO);
|
||||||
|
let SingleElement = sendElements.filter(element => element.elementType === ElementType.FILE || element.elementType === ElementType.VIDEO).map(e => [e]);
|
||||||
|
let AllElement: SendMessageElement[][] = [MixElement, ...SingleElement].filter(e => e !== undefined && e.length !== 0);
|
||||||
|
const MsgNodeList: Promise<RawMessage | undefined>[] = [];
|
||||||
|
for (const sendElementsSplitElement of AllElement) {
|
||||||
|
MsgNodeList.push(sendMsg(selfPeer, sendElementsSplitElement, [], true).catch(e => new Promise((resolve, reject) => { resolve(undefined) })));
|
||||||
|
}
|
||||||
|
(await Promise.allSettled(MsgNodeList)).map((result) => {
|
||||||
|
if (result.status === 'fulfilled' && result.value) {
|
||||||
|
nodeMsgIds.push(result.value.msgId);
|
||||||
|
MessageUnique.createMsg(selfPeer, result.value.msgId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
logDebug('生成转发消息节点失败', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const nodeMsgArray: Array<RawMessage> = [];
|
||||||
|
let srcPeer: Peer | undefined = undefined;
|
||||||
|
let needSendSelf = false;
|
||||||
|
//检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送
|
||||||
|
for (let msgId of nodeMsgIds) {
|
||||||
|
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
|
||||||
|
if (!nodeMsgPeer) {
|
||||||
|
logError('转发消息失败,未找到消息', msgId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
|
||||||
|
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
|
||||||
|
if (srcPeer.peerUid !== nodeMsg.peerUid) {
|
||||||
|
needSendSelf = true;
|
||||||
|
}
|
||||||
|
nodeMsgArray.push(nodeMsg);
|
||||||
|
}
|
||||||
|
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
||||||
|
let retMsgIds: string[] = [];
|
||||||
|
if (needSendSelf) {
|
||||||
|
for (const [index, msg] of nodeMsgArray.entries()) {
|
||||||
|
if (msg.peerUid === selfInfo.uid) continue;
|
||||||
|
const ClonedMsg = await cloneMsg(msg);
|
||||||
|
if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retMsgIds = nodeMsgIds;
|
||||||
|
}
|
||||||
|
if (nodeMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
|
||||||
|
try {
|
||||||
|
logDebug('开发转发', srcPeer, destPeer, nodeMsgIds);
|
||||||
|
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds);
|
||||||
|
} catch (e) {
|
||||||
|
logError('forward failed', e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
166
src/onebot/action/msg/SendMsg/index.ts
Normal file
166
src/onebot/action/msg/SendMsg/index.ts
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import BaseAction from '@/onebot11/action/BaseAction';
|
||||||
|
import {
|
||||||
|
OB11MessageData,
|
||||||
|
OB11MessageDataType,
|
||||||
|
OB11MessageMixType,
|
||||||
|
OB11MessageNode,
|
||||||
|
OB11PostSendMsg
|
||||||
|
} from '@/onebot11/types';
|
||||||
|
import { ActionName, BaseCheckResult } from '@/onebot11/action/types';
|
||||||
|
import { getGroup } from '@/core/data';
|
||||||
|
import { ChatType, ElementType, Group, NTQQFileApi, NTQQFriendApi, NTQQMsgApi, NTQQUserApi, Peer, SendMessageElement, } from '@/core';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import fsPromise from 'node:fs/promises';
|
||||||
|
import { logDebug, logError } from '@/common/utils/log';
|
||||||
|
import { decodeCQCode } from '@/onebot11/cqcode';
|
||||||
|
import createSendElements from './create-send-elements';
|
||||||
|
import { handleForwardNode } from '@/onebot11/action/msg/SendMsg/handle-forward-node';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
export interface ReturnDataType {
|
||||||
|
message_id: number;
|
||||||
|
}
|
||||||
|
export enum ContextMode {
|
||||||
|
Normal = 0,
|
||||||
|
Private = 1,
|
||||||
|
Group = 2
|
||||||
|
}
|
||||||
|
// Normalizes a mixed type (CQCode/a single segment/segment array) into a segment array.
|
||||||
|
export function normalize(message: OB11MessageMixType, autoEscape = false): OB11MessageData[] {
|
||||||
|
return typeof message === 'string' ? (
|
||||||
|
autoEscape ?
|
||||||
|
[{ type: OB11MessageDataType.text, data: { text: message } }] :
|
||||||
|
decodeCQCode(message)
|
||||||
|
) : Array.isArray(message) ? message : [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
export { createSendElements };
|
||||||
|
|
||||||
|
export async function sendMsg(peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[], waitComplete = true) {
|
||||||
|
if (!sendElements.length) {
|
||||||
|
throw ('消息体无法解析, 请检查是否发送了不支持的消息类型');
|
||||||
|
}
|
||||||
|
let totalSize = 0;
|
||||||
|
let timeout = 10000;
|
||||||
|
try {
|
||||||
|
for (const fileElement of sendElements) {
|
||||||
|
if (fileElement.elementType === ElementType.PTT) {
|
||||||
|
totalSize += fs.statSync(fileElement.pttElement.filePath).size;
|
||||||
|
}
|
||||||
|
if (fileElement.elementType === ElementType.FILE) {
|
||||||
|
totalSize += fs.statSync(fileElement.fileElement.filePath).size;
|
||||||
|
}
|
||||||
|
if (fileElement.elementType === ElementType.VIDEO) {
|
||||||
|
totalSize += fs.statSync(fileElement.videoElement.filePath).size;
|
||||||
|
}
|
||||||
|
if (fileElement.elementType === ElementType.PIC) {
|
||||||
|
totalSize += fs.statSync(fileElement.picElement.sourcePath).size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//且 PredictTime ((totalSize / 1024 / 512) * 1000)不等于Nan
|
||||||
|
const PredictTime = totalSize / 1024 / 256 * 1000;
|
||||||
|
if (!Number.isNaN(PredictTime)) {
|
||||||
|
timeout += PredictTime;// 10S Basic Timeout + PredictTime( For File 512kb/s )
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logError('发送消息计算预计时间异常', e);
|
||||||
|
}
|
||||||
|
const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
|
||||||
|
try {
|
||||||
|
returnMsg!.id = await MessageUnique.createMsg({ chatType: peer.chatType, guildId: '', peerUid: peer.peerUid }, returnMsg!.msgId);
|
||||||
|
} catch (e: any) {
|
||||||
|
logDebug('发送消息id获取失败', e);
|
||||||
|
returnMsg!.id = 0;
|
||||||
|
}
|
||||||
|
deleteAfterSentFiles.map((f) => { fsPromise.unlink(f).then().catch(e => logError('发送消息删除文件失败', e)); });
|
||||||
|
return returnMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode): Promise<Peer> {
|
||||||
|
// This function determines the type of message by the existence of user_id / group_id,
|
||||||
|
// not message_type.
|
||||||
|
// This redundant design of Ob11 here should be blamed.
|
||||||
|
|
||||||
|
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
|
||||||
|
const group = (await getGroup(payload.group_id))!; // checked before
|
||||||
|
return {
|
||||||
|
chatType: ChatType.group,
|
||||||
|
peerUid: group.groupCode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
|
||||||
|
const Uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
|
||||||
|
//console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy);
|
||||||
|
return {
|
||||||
|
chatType: isBuddy ? ChatType.friend : ChatType.temp,
|
||||||
|
peerUid: Uid!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw '请指定 group_id 或 user_id';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType): number {
|
||||||
|
if (Array.isArray(payload.message)) {
|
||||||
|
return payload.message.filter(msg => msg.type == msgType).length;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
||||||
|
actionName = ActionName.SendMsg;
|
||||||
|
contextMode = ContextMode.Normal;
|
||||||
|
|
||||||
|
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
|
||||||
|
const messages = normalize(payload.message);
|
||||||
|
const nodeElementLength = getSpecialMsgNum(payload, OB11MessageDataType.node);
|
||||||
|
if (nodeElementLength > 0 && nodeElementLength != messages.length) {
|
||||||
|
return { valid: false, message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素' };
|
||||||
|
}
|
||||||
|
if (payload.message_type !== 'private' && payload.group_id && !(await getGroup(payload.group_id))) {
|
||||||
|
return { valid: false, message: `群${payload.group_id}不存在` };
|
||||||
|
}
|
||||||
|
if (payload.user_id && payload.message_type !== 'group') {
|
||||||
|
const uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
|
const isBuddy = await NTQQFriendApi.isBuddy(uid!);
|
||||||
|
// 此处有问题
|
||||||
|
if (!isBuddy) {
|
||||||
|
//return { valid: false, message: '异常消息' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { valid: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
||||||
|
const peer = await createContext(payload, this.contextMode);
|
||||||
|
|
||||||
|
const messages = normalize(
|
||||||
|
payload.message,
|
||||||
|
payload.auto_escape === true || payload.auto_escape === 'true'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
||||||
|
const returnMsg = await handleForwardNode(peer, messages as OB11MessageNode[]);
|
||||||
|
if (returnMsg) {
|
||||||
|
const msgShortId = MessageUnique.createMsg({ guildId: '', peerUid: peer.peerUid, chatType: peer.chatType }, returnMsg!.msgId);
|
||||||
|
return { message_id: msgShortId! };
|
||||||
|
} else {
|
||||||
|
throw Error('发送转发消息失败');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if (getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
||||||
|
// const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic;
|
||||||
|
// if (music) {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// log("send msg:", peer, sendElements)
|
||||||
|
|
||||||
|
const { sendElements, deleteAfterSentFiles } = await createSendElements(messages, peer);
|
||||||
|
//console.log(peer, JSON.stringify(sendElements,null,2));
|
||||||
|
const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles);
|
||||||
|
return { message_id: returnMsg!.id! };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SendMsg;
|
15
src/onebot/action/msg/SendPrivateMsg.ts
Normal file
15
src/onebot/action/msg/SendPrivateMsg.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import SendMsg, { ContextMode } from './SendMsg';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import { OB11PostSendMsg } from '../../types';
|
||||||
|
// 未检测参数
|
||||||
|
class SendPrivateMsg extends SendMsg {
|
||||||
|
actionName = ActionName.SendPrivateMsg;
|
||||||
|
contextMode: ContextMode = ContextMode.Private;
|
||||||
|
|
||||||
|
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
|
||||||
|
payload.message_type = 'private';
|
||||||
|
return super.check(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SendPrivateMsg;
|
35
src/onebot/action/msg/SetMsgEmojiLike.ts
Normal file
35
src/onebot/action/msg/SetMsgEmojiLike.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { ActionName } from '../types';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['string', 'number'] },
|
||||||
|
emoji_id: { type: ['string', 'number'] }
|
||||||
|
},
|
||||||
|
required: ['message_id', 'emoji_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class SetMsgEmojiLike extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetMsgEmojiLike;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
if (!payload.emoji_id) {
|
||||||
|
throw new Error('emojiId not found');
|
||||||
|
}
|
||||||
|
const msgData = (await NTQQMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId])).msgList;
|
||||||
|
if (!msgData || msgData.length == 0 || !msgData[0].msgSeq) {
|
||||||
|
throw new Error('find msg by msgid error');
|
||||||
|
}
|
||||||
|
return await NTQQMsgApi.setEmojiLike(msg.Peer, msgData[0].msgSeq, payload.emoji_id.toString(), true);
|
||||||
|
}
|
||||||
|
}
|
10
src/onebot/action/system/CanSendImage.ts
Normal file
10
src/onebot/action/system/CanSendImage.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { ActionName } from '../types';
|
||||||
|
import CanSendRecord from './CanSendRecord';
|
||||||
|
|
||||||
|
interface ReturnType {
|
||||||
|
yes: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CanSendImage extends CanSendRecord {
|
||||||
|
actionName = ActionName.CanSendImage;
|
||||||
|
}
|
16
src/onebot/action/system/CanSendRecord.ts
Normal file
16
src/onebot/action/system/CanSendRecord.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
|
||||||
|
interface ReturnType {
|
||||||
|
yes: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CanSendRecord extends BaseAction<any, ReturnType> {
|
||||||
|
actionName = ActionName.CanSendRecord;
|
||||||
|
|
||||||
|
protected async _handle(_payload: void): Promise<ReturnType> {
|
||||||
|
return {
|
||||||
|
yes: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
93
src/onebot/action/system/CleanCache.ts
Normal file
93
src/onebot/action/system/CleanCache.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import fs from 'fs';
|
||||||
|
import Path from 'path';
|
||||||
|
import {
|
||||||
|
ChatType,
|
||||||
|
ChatCacheListItemBasic,
|
||||||
|
CacheFileType
|
||||||
|
} from '@/core/entities';
|
||||||
|
import { NTQQFileApi, NTQQFileCacheApi } from '@/core/apis/file';
|
||||||
|
import { logError } from '@/common/utils/log';
|
||||||
|
|
||||||
|
export default class CleanCache extends BaseAction<void, void> {
|
||||||
|
actionName = ActionName.CleanCache;
|
||||||
|
|
||||||
|
protected _handle(): Promise<void> {
|
||||||
|
return new Promise<void>(async (res, rej) => {
|
||||||
|
try {
|
||||||
|
// dbUtil.clearCache();
|
||||||
|
const cacheFilePaths: string[] = [];
|
||||||
|
|
||||||
|
await NTQQFileCacheApi.setCacheSilentScan(false);
|
||||||
|
|
||||||
|
cacheFilePaths.push((await NTQQFileCacheApi.getHotUpdateCachePath()));
|
||||||
|
cacheFilePaths.push((await NTQQFileCacheApi.getDesktopTmpPath()));
|
||||||
|
(await NTQQFileCacheApi.getCacheSessionPathList()).forEach((e: { value: string; }) => cacheFilePaths.push(e.value));
|
||||||
|
|
||||||
|
// await NTQQApi.addCacheScannedPaths(); // XXX: 调用就崩溃,原因目前还未知
|
||||||
|
const cacheScanResult = await NTQQFileCacheApi.scanCache();
|
||||||
|
const cacheSize = parseInt(cacheScanResult.size[6]);
|
||||||
|
|
||||||
|
if (cacheScanResult.result !== 0) {
|
||||||
|
throw('Something went wrong while scanning cache. Code: ' + cacheScanResult.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
await NTQQFileCacheApi.setCacheSilentScan(true);
|
||||||
|
if (cacheSize > 0 && cacheFilePaths.length > 2) { // 存在缓存文件且大小不为 0 时执行清理动作
|
||||||
|
// await NTQQApi.clearCache([ 'tmp', 'hotUpdate', ...cacheScanResult ]) // XXX: 也是调用就崩溃,调用 fs 删除得了
|
||||||
|
deleteCachePath(cacheFilePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取聊天记录列表
|
||||||
|
// NOTE: 以防有人不需要删除聊天记录,暂时先注释掉,日后加个开关
|
||||||
|
// const privateChatCache = await getCacheList(ChatType.friend); // 私聊消息
|
||||||
|
// const groupChatCache = await getCacheList(ChatType.group); // 群聊消息
|
||||||
|
// const chatCacheList = [ ...privateChatCache, ...groupChatCache ];
|
||||||
|
const chatCacheList: ChatCacheListItemBasic[] = [];
|
||||||
|
|
||||||
|
// 获取聊天缓存文件列表
|
||||||
|
const cacheFileList: string[] = [];
|
||||||
|
|
||||||
|
for (const name in CacheFileType) {
|
||||||
|
if (!isNaN(parseInt(name))) continue;
|
||||||
|
|
||||||
|
const fileTypeAny: any = CacheFileType[name];
|
||||||
|
const fileType: CacheFileType = fileTypeAny;
|
||||||
|
|
||||||
|
cacheFileList.push(...(await NTQQFileCacheApi.getFileCacheInfo(fileType)).infos.map((file: { fileKey: any; }) => file.fileKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一并清除
|
||||||
|
await NTQQFileCacheApi.clearChatCache(chatCacheList, cacheFileList);
|
||||||
|
res();
|
||||||
|
} catch(e) {
|
||||||
|
logError('清理缓存时发生了错误');
|
||||||
|
rej(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCachePath(pathList: string[]) {
|
||||||
|
const emptyPath = (path: string) => {
|
||||||
|
if (!fs.existsSync(path)) return;
|
||||||
|
const files = fs.readdirSync(path);
|
||||||
|
files.forEach(file => {
|
||||||
|
const filePath = Path.resolve(path, file);
|
||||||
|
const stats = fs.statSync(filePath);
|
||||||
|
if (stats.isDirectory()) emptyPath(filePath);
|
||||||
|
else fs.unlinkSync(filePath);
|
||||||
|
});
|
||||||
|
fs.rmdirSync(path);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const path of pathList) {
|
||||||
|
emptyPath(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCacheList(type: ChatType) { // NOTE: 做这个方法主要是因为目前还不支持针对频道消息的清理
|
||||||
|
return new Promise<Array<ChatCacheListItemBasic>>((res, rej) => {
|
||||||
|
});
|
||||||
|
}
|
17
src/onebot/action/system/GetLoginInfo.ts
Normal file
17
src/onebot/action/system/GetLoginInfo.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
import { OB11User } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { napCatCore } from '@/core';
|
||||||
|
|
||||||
|
|
||||||
|
class GetLoginInfo extends BaseAction<null, OB11User> {
|
||||||
|
actionName = ActionName.GetLoginInfo;
|
||||||
|
|
||||||
|
protected async _handle(payload: null) {
|
||||||
|
return OB11Constructor.selfInfo(selfInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GetLoginInfo;
|
17
src/onebot/action/system/GetStatus.ts
Normal file
17
src/onebot/action/system/GetStatus.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { OB11Status } from '../../types';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { selfInfo, stat } from '@/core/data';
|
||||||
|
|
||||||
|
|
||||||
|
export default class GetStatus extends BaseAction<any, OB11Status> {
|
||||||
|
actionName = ActionName.GetStatus;
|
||||||
|
|
||||||
|
protected async _handle(payload: any): Promise<OB11Status> {
|
||||||
|
return {
|
||||||
|
online: !!selfInfo.online,
|
||||||
|
good: true,
|
||||||
|
stat
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
16
src/onebot/action/system/GetVersionInfo.ts
Normal file
16
src/onebot/action/system/GetVersionInfo.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { OB11Version } from '../../types';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { version } from '@/onebot11/version';
|
||||||
|
|
||||||
|
export default class GetVersionInfo extends BaseAction<any, OB11Version> {
|
||||||
|
actionName = ActionName.GetVersionInfo;
|
||||||
|
|
||||||
|
protected async _handle(payload: any): Promise<OB11Version> {
|
||||||
|
return {
|
||||||
|
app_name: 'NapCat.Onebot',
|
||||||
|
protocol_version: 'v11',
|
||||||
|
app_version: version
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
43
src/onebot/action/system/Reboot.ts
Normal file
43
src/onebot/action/system/Reboot.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { rebootWithNormolLogin, rebootWithQuickLogin } from '@/common/utils/reboot';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
delay: { type: 'number' }
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class Reboot extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.Reboot;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
if (payload.delay) {
|
||||||
|
setTimeout(() => {
|
||||||
|
rebootWithQuickLogin(selfInfo.uin);
|
||||||
|
}, payload.delay);
|
||||||
|
} else {
|
||||||
|
rebootWithQuickLogin(selfInfo.uin);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class RebootNormal extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.RebootNormal;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
if (payload.delay) {
|
||||||
|
setTimeout(() => {
|
||||||
|
rebootWithNormolLogin();
|
||||||
|
}, payload.delay);
|
||||||
|
} else {
|
||||||
|
rebootWithNormolLogin();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
107
src/onebot/action/types.ts
Normal file
107
src/onebot/action/types.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
export type BaseCheckResult = ValidCheckResult | InvalidCheckResult
|
||||||
|
|
||||||
|
export interface ValidCheckResult {
|
||||||
|
valid: true
|
||||||
|
|
||||||
|
[k: string | number]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InvalidCheckResult {
|
||||||
|
valid: false
|
||||||
|
message: string
|
||||||
|
|
||||||
|
[k: string | number]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ActionName {
|
||||||
|
// 以下为扩展napcat扩展
|
||||||
|
SharePeer = 'ArkShareGroup',
|
||||||
|
ShareGroupEx = 'ArkSharePeer',
|
||||||
|
RebootNormal = 'reboot_normal',//无快速登录重新启动
|
||||||
|
GetRobotUinRange = 'get_robot_uin_range',
|
||||||
|
SetOnlineStatus = 'set_online_status',
|
||||||
|
GetFriendsWithCategory = 'get_friends_with_category',
|
||||||
|
GetGroupIgnoreAddRequest = 'get_group_ignore_add_request',
|
||||||
|
SetQQAvatar = 'set_qq_avatar',
|
||||||
|
GetConfig = 'get_config',
|
||||||
|
SetConfig = 'set_config',
|
||||||
|
Debug = 'debug',
|
||||||
|
GetFile = 'get_file',
|
||||||
|
ForwardFriendSingleMsg = 'forward_friend_single_msg',
|
||||||
|
ForwardGroupSingleMsg = 'forward_group_single_msg',
|
||||||
|
TranslateEnWordToZn = 'translate_en2zh',
|
||||||
|
GetGroupFileCount = 'get_group_file_count',
|
||||||
|
GetGroupFileList = 'get_group_file_list',
|
||||||
|
SetGroupFileFolder = 'set_group_file_folder',
|
||||||
|
DelGroupFile = 'del_group_file',
|
||||||
|
DelGroupFileFolder = 'del_group_file_folder',
|
||||||
|
// onebot 11
|
||||||
|
Reboot = 'set_restart',
|
||||||
|
SendLike = 'send_like',
|
||||||
|
GetLoginInfo = 'get_login_info',
|
||||||
|
GetFriendList = 'get_friend_list',
|
||||||
|
GetGroupInfo = 'get_group_info',
|
||||||
|
GetGroupList = 'get_group_list',
|
||||||
|
GetGroupMemberInfo = 'get_group_member_info',
|
||||||
|
GetGroupMemberList = 'get_group_member_list',
|
||||||
|
GetMsg = 'get_msg',
|
||||||
|
SendMsg = 'send_msg',
|
||||||
|
SendGroupMsg = 'send_group_msg',
|
||||||
|
SendPrivateMsg = 'send_private_msg',
|
||||||
|
DeleteMsg = 'delete_msg',
|
||||||
|
SetMsgEmojiLike = 'set_msg_emoji_like',
|
||||||
|
SetGroupAddRequest = 'set_group_add_request',
|
||||||
|
SetFriendAddRequest = 'set_friend_add_request',
|
||||||
|
SetGroupLeave = 'set_group_leave',
|
||||||
|
GetVersionInfo = 'get_version_info',
|
||||||
|
GetStatus = 'get_status',
|
||||||
|
CanSendRecord = 'can_send_record',
|
||||||
|
CanSendImage = 'can_send_image',
|
||||||
|
SetGroupKick = 'set_group_kick',
|
||||||
|
SetGroupBan = 'set_group_ban',
|
||||||
|
SetGroupWholeBan = 'set_group_whole_ban',
|
||||||
|
SetGroupAdmin = 'set_group_admin',
|
||||||
|
SetGroupCard = 'set_group_card',
|
||||||
|
SetGroupName = 'set_group_name',
|
||||||
|
GetImage = 'get_image',
|
||||||
|
GetRecord = 'get_record',
|
||||||
|
CleanCache = 'clean_cache',
|
||||||
|
GetCookies = 'get_cookies',
|
||||||
|
// 以下为go-cqhttp api
|
||||||
|
GoCQHTTP_HandleQuickAction = '.handle_quick_operation',
|
||||||
|
GetGroupHonorInfo = 'get_group_honor_info',
|
||||||
|
GoCQHTTP_GetEssenceMsg = 'get_essence_msg_list',
|
||||||
|
GoCQHTTP_SendGroupNotice = '_send_group_notice',
|
||||||
|
GoCQHTTP_GetGroupNotice = '_get_group_notice',
|
||||||
|
GoCQHTTP_SendForwardMsg = 'send_forward_msg',
|
||||||
|
GoCQHTTP_SendGroupForwardMsg = 'send_group_forward_msg',
|
||||||
|
GoCQHTTP_SendPrivateForwardMsg = 'send_private_forward_msg',
|
||||||
|
GoCQHTTP_GetStrangerInfo = 'get_stranger_info',
|
||||||
|
GoCQHTTP_MarkMsgAsRead = 'mark_msg_as_read',
|
||||||
|
GetGuildList = 'get_guild_list',
|
||||||
|
MarkPrivateMsgAsRead = 'mark_private_msg_as_read',
|
||||||
|
MarkGroupMsgAsRead = 'mark_group_msg_as_read',
|
||||||
|
GoCQHTTP_UploadGroupFile = 'upload_group_file',
|
||||||
|
GoCQHTTP_DownloadFile = 'download_file',
|
||||||
|
GoCQHTTP_GetGroupMsgHistory = 'get_group_msg_history',
|
||||||
|
GoCQHTTP_GetForwardMsg = 'get_forward_msg',
|
||||||
|
GetFriendMsgHistory = 'get_friend_msg_history',
|
||||||
|
GetGroupSystemMsg = 'get_group_system_msg',
|
||||||
|
GetOnlineClient = 'get_online_clients',
|
||||||
|
OCRImage = 'ocr_image',
|
||||||
|
IOCRImage = '.ocr_image',
|
||||||
|
SetSelfProfile = 'set_self_profile',
|
||||||
|
CreateCollection = 'create_collection',
|
||||||
|
GetCollectionList = 'get_collection_list',
|
||||||
|
SetLongNick = 'set_self_longnick',
|
||||||
|
SetEssenceMsg = 'set_essence_msg',
|
||||||
|
DelEssenceMsg = 'delete_essence_msg',
|
||||||
|
GetRecentContact = 'get_recent_contact',
|
||||||
|
_MarkAllMsgAsRead = '_mark_all_as_read',
|
||||||
|
GetProfileLike = 'get_profile_like',
|
||||||
|
SetGroupHeader = 'set_group_head',
|
||||||
|
FetchCustomFace = 'fetch_custom_face',
|
||||||
|
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
|
||||||
|
TestApi01 = 'test_api_01',
|
||||||
|
FetchEmojioLike = 'fetch_emoji_like'
|
||||||
|
}
|
69
src/onebot/action/user/GetCookies.ts
Normal file
69
src/onebot/action/user/GetCookies.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi, WebApi } from '@/core/apis';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
interface Response {
|
||||||
|
cookies: string,
|
||||||
|
bkn: string
|
||||||
|
}
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
domain: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['domain']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetCookies extends BaseAction<Payload, Response> {
|
||||||
|
actionName = ActionName.GetCookies;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
// if (!payload.domain) {
|
||||||
|
// throw new Error('缺少参数 domain');
|
||||||
|
// }
|
||||||
|
// if (payload.domain.endsWith('qzone.qq.com')) {
|
||||||
|
// // 兼容整个 *.qzone.qq.com
|
||||||
|
// const 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;
|
||||||
|
// return { cookies: CookieValue };
|
||||||
|
// }
|
||||||
|
// // 取Skey
|
||||||
|
// // 先NodeIKernelTicketService.forceFetchClientKey('')
|
||||||
|
// // 返回值
|
||||||
|
// // {
|
||||||
|
// // result: 0,
|
||||||
|
// // errMsg: '',
|
||||||
|
// // url: '',
|
||||||
|
// // keyIndex: '19',
|
||||||
|
// // clientKey: 'clientKey',
|
||||||
|
// // expireTime: '7200'
|
||||||
|
// // }
|
||||||
|
// // request https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=1627126029&clientkey=key
|
||||||
|
// // &u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=keyIndex
|
||||||
|
// const _PSkey = (await NTQQUserApi.getPSkey([payload.domain]))[payload.domain];
|
||||||
|
// // 取Pskey
|
||||||
|
// // NodeIKernelTipOffService.getPskey([ 'qun.qq.com' ], true )
|
||||||
|
// // {
|
||||||
|
// // domainPskeyMap: 0,
|
||||||
|
// // errMsg: 'success',
|
||||||
|
// // domainPskeyMap: Map(1) {
|
||||||
|
// // 'qun.qq.com' => 'pskey'
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// if (!_PSkey || !_Skey) {
|
||||||
|
// throw new Error('获取Cookies失败');
|
||||||
|
// }
|
||||||
|
// const cookies = `p_skey=${_PSkey}; skey=${_Skey}; p_uin=o${selfInfo.uin}; uin=o${selfInfo.uin}`;
|
||||||
|
// return {
|
||||||
|
// cookies
|
||||||
|
// };
|
||||||
|
const cookiesObject = await NTQQUserApi.getCookies(payload.domain);
|
||||||
|
//把获取到的cookiesObject转换成 k=v; 格式字符串拼接在一起
|
||||||
|
const cookies = Object.entries(cookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||||
|
const bkn = WebApi.genBkn(cookiesObject.p_skey);
|
||||||
|
return { cookies, bkn };
|
||||||
|
}
|
||||||
|
}
|
40
src/onebot/action/user/GetFriendList.ts
Normal file
40
src/onebot/action/user/GetFriendList.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { OB11User } from '../../types';
|
||||||
|
import { OB11Constructor } from '../../constructor';
|
||||||
|
import { friends } from '@/core/data';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQFriendApi } from '@/core';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { requireMinNTQQBuild } from '@/common/utils/QQBasicInfo';
|
||||||
|
|
||||||
|
|
||||||
|
// no_cache get时传字符串
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
no_cache: { type: ['boolean', 'string'] },
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
export default class GetFriendList extends BaseAction<Payload, OB11User[]> {
|
||||||
|
actionName = ActionName.GetFriendList;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
if (requireMinNTQQBuild('26702')) {
|
||||||
|
//全新逻辑
|
||||||
|
return OB11Constructor.friendsV2(await NTQQFriendApi.getBuddyV2(payload?.no_cache === true || payload?.no_cache === 'true'));
|
||||||
|
}
|
||||||
|
if (friends.size === 0 || payload?.no_cache === true || payload?.no_cache === 'true') {
|
||||||
|
const _friends = await NTQQFriendApi.getFriends(true);
|
||||||
|
// log('强制刷新好友列表,结果: ', _friends)
|
||||||
|
if (_friends.length > 0) {
|
||||||
|
friends.clear();
|
||||||
|
for (const friend of _friends) {
|
||||||
|
friends.set(friend.uid, friend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OB11Constructor.friends(Array.from(friends.values()));
|
||||||
|
}
|
||||||
|
}
|
52
src/onebot/action/user/GetRecentContact.ts
Normal file
52
src/onebot/action/user/GetRecentContact.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQMsgApi, NTQQUserApi } from '@/core';
|
||||||
|
import { OB11Constructor } from '@/onebot11/constructor';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
count: { type: ['number', 'string'] }
|
||||||
|
}
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class GetRecentContact extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.GetRecentContact;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload) {
|
||||||
|
const ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString()));
|
||||||
|
const data = await Promise.all(ret.info.changedList.map(async (t) => {
|
||||||
|
const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
|
||||||
|
if (FastMsg.msgList.length > 0) {
|
||||||
|
//扩展ret.info.changedList
|
||||||
|
const lastestMsg = await OB11Constructor.message(FastMsg.msgList[0]);
|
||||||
|
return {
|
||||||
|
lastestMsg: lastestMsg,
|
||||||
|
peerUin: t.peerUin,
|
||||||
|
remark: t.remark,
|
||||||
|
msgTime: t.msgTime,
|
||||||
|
chatType: t.chatType,
|
||||||
|
msgId: t.msgId,
|
||||||
|
sendNickName: t.sendNickName,
|
||||||
|
sendMemberName: t.sendMemberName,
|
||||||
|
peerName: t.peerName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
peerUin: t.peerUin,
|
||||||
|
remark: t.remark,
|
||||||
|
msgTime: t.msgTime,
|
||||||
|
chatType: t.chatType,
|
||||||
|
msgId: t.msgId,
|
||||||
|
sendNickName: t.sendNickName,
|
||||||
|
sendMemberName: t.sendMemberName,
|
||||||
|
peerName: t.peerName
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
35
src/onebot/action/user/SendLike.ts
Normal file
35
src/onebot/action/user/SendLike.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user_id: { type: ['number', 'string'] },
|
||||||
|
times: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['user_id', 'times']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SendLike extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SendLike;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
//logDebug('点赞参数', payload);
|
||||||
|
try {
|
||||||
|
const qq = payload.user_id.toString();
|
||||||
|
const uid: string = await NTQQUserApi.getUidByUin(qq) || '';
|
||||||
|
const result = await NTQQUserApi.like(uid, parseInt(payload.times?.toString()) || 1);
|
||||||
|
//logDebug('点赞结果', result);
|
||||||
|
if (result.result !== 0) {
|
||||||
|
throw Error(result.errMsg);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw `点赞失败 ${e}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
26
src/onebot/action/user/SetFriendAddRequest.ts
Normal file
26
src/onebot/action/user/SetFriendAddRequest.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQFriendApi } from '@/core/apis/friend';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
flag: { type: 'string' },
|
||||||
|
approve: { type: ['string', 'boolean'] },
|
||||||
|
remark: { type: 'string' }
|
||||||
|
},
|
||||||
|
required: ['flag']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetFriendAddRequest extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName.SetFriendAddRequest;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
const approve = payload.approve?.toString() !== 'false';
|
||||||
|
await NTQQFriendApi.handleFriendRequest(payload.flag, approve);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
16
src/onebot/event/OB11BaseEvent.ts
Normal file
16
src/onebot/event/OB11BaseEvent.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
|
||||||
|
export enum EventType {
|
||||||
|
META = 'meta_event',
|
||||||
|
REQUEST = 'request',
|
||||||
|
NOTICE = 'notice',
|
||||||
|
MESSAGE = 'message',
|
||||||
|
MESSAGE_SENT = 'message_sent',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export abstract class OB11BaseEvent {
|
||||||
|
time = Math.floor(Date.now() / 1000);
|
||||||
|
self_id = parseInt(selfInfo.uin);
|
||||||
|
post_type: EventType = EventType.META;
|
||||||
|
}
|
5
src/onebot/event/message/OB11BaseMessageEvent.ts
Normal file
5
src/onebot/event/message/OB11BaseMessageEvent.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { EventType, OB11BaseEvent } from '../OB11BaseEvent';
|
||||||
|
|
||||||
|
export abstract class OB11BaseMessageEvent extends OB11BaseEvent {
|
||||||
|
post_type = EventType.MESSAGE;
|
||||||
|
}
|
6
src/onebot/event/meta/OB11BaseMetaEvent.ts
Normal file
6
src/onebot/event/meta/OB11BaseMetaEvent.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { EventType, OB11BaseEvent } from '../OB11BaseEvent';
|
||||||
|
|
||||||
|
export abstract class OB11BaseMetaEvent extends OB11BaseEvent {
|
||||||
|
post_type = EventType.META;
|
||||||
|
meta_event_type: string;
|
||||||
|
}
|
21
src/onebot/event/meta/OB11HeartbeatEvent.ts
Normal file
21
src/onebot/event/meta/OB11HeartbeatEvent.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { OB11BaseMetaEvent } from './OB11BaseMetaEvent';
|
||||||
|
|
||||||
|
interface HeartbeatStatus {
|
||||||
|
online: boolean | null,
|
||||||
|
good: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OB11HeartbeatEvent extends OB11BaseMetaEvent {
|
||||||
|
meta_event_type = 'heartbeat';
|
||||||
|
status: HeartbeatStatus;
|
||||||
|
interval: number;
|
||||||
|
|
||||||
|
public constructor(isOnline: boolean, isGood: boolean, interval: number) {
|
||||||
|
super();
|
||||||
|
this.interval = interval;
|
||||||
|
this.status = {
|
||||||
|
online: isOnline,
|
||||||
|
good: isGood
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
17
src/onebot/event/meta/OB11LifeCycleEvent.ts
Normal file
17
src/onebot/event/meta/OB11LifeCycleEvent.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { OB11BaseMetaEvent } from './OB11BaseMetaEvent';
|
||||||
|
|
||||||
|
export enum LifeCycleSubType {
|
||||||
|
ENABLE = 'enable',
|
||||||
|
DISABLE = 'disable',
|
||||||
|
CONNECT = 'connect'
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OB11LifeCycleEvent extends OB11BaseMetaEvent {
|
||||||
|
meta_event_type = 'lifecycle';
|
||||||
|
sub_type: LifeCycleSubType;
|
||||||
|
|
||||||
|
public constructor(subType: LifeCycleSubType) {
|
||||||
|
super();
|
||||||
|
this.sub_type = subType;
|
||||||
|
}
|
||||||
|
}
|
5
src/onebot/event/notice/OB11BaseNoticeEvent.ts
Normal file
5
src/onebot/event/notice/OB11BaseNoticeEvent.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { EventType, OB11BaseEvent } from '../OB11BaseEvent';
|
||||||
|
|
||||||
|
export abstract class OB11BaseNoticeEvent extends OB11BaseEvent {
|
||||||
|
post_type = EventType.NOTICE;
|
||||||
|
}
|
11
src/onebot/event/notice/OB11FriendAddNoticeEvent.ts
Normal file
11
src/onebot/event/notice/OB11FriendAddNoticeEvent.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
||||||
|
|
||||||
|
export class OB11FriendAddNoticeEvent extends OB11BaseNoticeEvent {
|
||||||
|
notice_type = 'friend_add';
|
||||||
|
user_id: number;
|
||||||
|
|
||||||
|
public constructor(user_Id: number) {
|
||||||
|
super();
|
||||||
|
this.user_id = user_Id;
|
||||||
|
}
|
||||||
|
}
|
13
src/onebot/event/notice/OB11FriendRecallNoticeEvent.ts
Normal file
13
src/onebot/event/notice/OB11FriendRecallNoticeEvent.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
||||||
|
|
||||||
|
export class OB11FriendRecallNoticeEvent extends OB11BaseNoticeEvent {
|
||||||
|
notice_type = 'friend_recall';
|
||||||
|
user_id: number;
|
||||||
|
message_id: number;
|
||||||
|
|
||||||
|
public constructor(userId: number, messageId: number) {
|
||||||
|
super();
|
||||||
|
this.user_id = userId;
|
||||||
|
this.message_id = messageId;
|
||||||
|
}
|
||||||
|
}
|
6
src/onebot/event/notice/OB11GroupAdminNoticeEvent.ts
Normal file
6
src/onebot/event/notice/OB11GroupAdminNoticeEvent.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
|
|
||||||
|
export class OB11GroupAdminNoticeEvent extends OB11GroupNoticeEvent {
|
||||||
|
notice_type = 'group_admin';
|
||||||
|
sub_type: 'set' | 'unset' = "set"; // "set" | "unset"
|
||||||
|
}
|
17
src/onebot/event/notice/OB11GroupBanEvent.ts
Normal file
17
src/onebot/event/notice/OB11GroupBanEvent.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
|
|
||||||
|
export class OB11GroupBanEvent extends OB11GroupNoticeEvent {
|
||||||
|
notice_type = 'group_ban';
|
||||||
|
operator_id: number;
|
||||||
|
duration: number;
|
||||||
|
sub_type: 'ban' | 'lift_ban';
|
||||||
|
|
||||||
|
constructor(groupId: number, userId: number, operatorId: number, duration: number, sub_type: 'ban' | 'lift_ban') {
|
||||||
|
super();
|
||||||
|
this.group_id = groupId;
|
||||||
|
this.operator_id = operatorId;
|
||||||
|
this.user_id = userId;
|
||||||
|
this.duration = duration;
|
||||||
|
this.sub_type = sub_type;
|
||||||
|
}
|
||||||
|
}
|
16
src/onebot/event/notice/OB11GroupCardEvent.ts
Normal file
16
src/onebot/event/notice/OB11GroupCardEvent.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
|
|
||||||
|
export class OB11GroupCardEvent extends OB11GroupNoticeEvent {
|
||||||
|
notice_type = 'group_card';
|
||||||
|
card_new: string;
|
||||||
|
card_old: string;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(groupId: number, userId: number, cardNew: string, cardOld: string) {
|
||||||
|
super();
|
||||||
|
this.group_id = groupId;
|
||||||
|
this.user_id = userId;
|
||||||
|
this.card_new = cardNew;
|
||||||
|
this.card_old = cardOld;
|
||||||
|
}
|
||||||
|
}
|
17
src/onebot/event/notice/OB11GroupDecreaseEvent.ts
Normal file
17
src/onebot/event/notice/OB11GroupDecreaseEvent.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
|
|
||||||
|
export type GroupDecreaseSubType = 'leave' | 'kick' | 'kick_me';
|
||||||
|
|
||||||
|
export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent {
|
||||||
|
notice_type = 'group_decrease';
|
||||||
|
sub_type: GroupDecreaseSubType = 'leave'; // TODO: 实现其他几种子类型的识别 ("leave" | "kick" | "kick_me")
|
||||||
|
operator_id: number;
|
||||||
|
|
||||||
|
constructor(groupId: number, userId: number, operatorId: number, subType: GroupDecreaseSubType = 'leave') {
|
||||||
|
super();
|
||||||
|
this.group_id = groupId;
|
||||||
|
this.operator_id = operatorId; // 实际上不应该这么实现,但是现在还没有办法识别用户是被踢出的,还是自己主动退出的
|
||||||
|
this.user_id = userId;
|
||||||
|
this.sub_type = subType;
|
||||||
|
}
|
||||||
|
}
|
14
src/onebot/event/notice/OB11GroupEssenceEvent.ts
Normal file
14
src/onebot/event/notice/OB11GroupEssenceEvent.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
|
export class OB11GroupEssenceEvent extends OB11GroupNoticeEvent {
|
||||||
|
notice_type = 'essence';
|
||||||
|
message_id: number;
|
||||||
|
sender_id: number;
|
||||||
|
sub_type: 'add' | 'delete' = 'add';
|
||||||
|
|
||||||
|
constructor(groupId: number, message_id: number, sender_id: number) {
|
||||||
|
super();
|
||||||
|
this.group_id = groupId;
|
||||||
|
this.message_id = message_id;
|
||||||
|
this.sender_id = sender_id;
|
||||||
|
}
|
||||||
|
}
|
15
src/onebot/event/notice/OB11GroupIncreaseEvent.ts
Normal file
15
src/onebot/event/notice/OB11GroupIncreaseEvent.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
|
|
||||||
|
type GroupIncreaseSubType = 'approve' | 'invite';
|
||||||
|
export class OB11GroupIncreaseEvent extends OB11GroupNoticeEvent {
|
||||||
|
notice_type = 'group_increase';
|
||||||
|
operator_id: number;
|
||||||
|
sub_type: GroupIncreaseSubType;
|
||||||
|
constructor(groupId: number, userId: number, operatorId: number, subType: GroupIncreaseSubType = 'approve') {
|
||||||
|
super();
|
||||||
|
this.group_id = groupId;
|
||||||
|
this.operator_id = operatorId;
|
||||||
|
this.user_id = userId;
|
||||||
|
this.sub_type = subType;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user