feat: implement some group album actions
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
// Group album actions via TRPC protocol
|
||||
|
||||
import type { Bridge } from '../bridge';
|
||||
import { protoEncode, protoDecode } from '@/protobuf/decode';
|
||||
import { GetMediaListRequestSchema, GetMediaListResponseSchema } from '../proto/oidb-action';
|
||||
|
||||
export interface GroupAlbumMediaResult {
|
||||
mediaList: any[];
|
||||
nextAttachInfo: string;
|
||||
raw: any;
|
||||
}
|
||||
|
||||
function convertBigIntToString(obj: any): any {
|
||||
if (obj === null || obj === undefined) return obj;
|
||||
if (typeof obj === 'bigint') return obj.toString();
|
||||
if (Array.isArray(obj)) return obj.map(convertBigIntToString);
|
||||
if (typeof obj === 'object') {
|
||||
const result: any = {};
|
||||
for (const key in obj) {
|
||||
result[key] = convertBigIntToString(obj[key]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export async function getGroupAlbumMediaList(
|
||||
bridge: Bridge,
|
||||
groupId: number,
|
||||
albumId: string,
|
||||
attachInfo: string = ''
|
||||
): Promise<GroupAlbumMediaResult> {
|
||||
const traceId = `_${Date.now()}_${Math.floor(Math.random() * 100000)}`;
|
||||
|
||||
const body = protoEncode({
|
||||
field1: 0,
|
||||
field2: new Uint8Array(0),
|
||||
field3: new Uint8Array(0),
|
||||
reqInfo: {
|
||||
groupId: groupId.toString(),
|
||||
albumId: albumId,
|
||||
field3: 0,
|
||||
attachInfo: attachInfo, // 填入翻页游标
|
||||
field5: '',
|
||||
},
|
||||
traceId: traceId,
|
||||
extMap: [{ key: 'fc-appid', value: '100' }],
|
||||
}, GetMediaListRequestSchema);
|
||||
|
||||
const result = await bridge.sendRawPacket(
|
||||
'QunAlbum.trpc.qzone.webapp_qun_media.QunMedia.GetMediaList',
|
||||
body,
|
||||
15000
|
||||
);
|
||||
|
||||
if (!result.success || !result.gotResponse || !result.responseData) {
|
||||
throw new Error(result.errorMessage || 'failed to get album media list');
|
||||
}
|
||||
|
||||
const resp = protoDecode(result.responseData, GetMediaListResponseSchema);
|
||||
|
||||
// 校验外层 TRPC 状态码
|
||||
if (!resp || (resp as any).field1 !== 0) {
|
||||
throw new Error(`fetch album media list error: retCode ${(resp as any)?.field1 || 'unknown'}`);
|
||||
}
|
||||
|
||||
const data = (resp as any).data || {};
|
||||
const mediaList = data.mediaList || [];
|
||||
const nextAttachInfo = data.nextAttachInfo || '';
|
||||
|
||||
return convertBigIntToString({
|
||||
mediaList,
|
||||
nextAttachInfo,
|
||||
raw: resp
|
||||
});
|
||||
}
|
||||
@@ -111,7 +111,10 @@ import {
|
||||
getCookiesStr as getCookiesStr_,
|
||||
getCsrfToken as getCsrfToken_,
|
||||
getCredentials as getCredentials_,
|
||||
getGroupAlbumListWeb as getGroupAlbumListWeb_,
|
||||
uploadImageToGroupAlbumWeb as uploadImageToGroupAlbumWeb_,
|
||||
} from './web-actions';
|
||||
import { getGroupAlbumMediaList as getGroupAlbumMediaList_ } from './actions/group-album';
|
||||
import type { GroupFilesResult } from './actions/group-file';
|
||||
import type { MediaIndexNode } from './actions/shared';
|
||||
import { BridgeEventBus } from './event-bus';
|
||||
@@ -645,6 +648,18 @@ export class Bridge {
|
||||
return getGroupEssenceAll_(this, groupId);
|
||||
}
|
||||
|
||||
async getGroupAlbumList(groupId: number): Promise<any> {
|
||||
return getGroupAlbumListWeb_(this, groupId);
|
||||
}
|
||||
|
||||
async uploadImageToGroupAlbum(groupId: number, albumId: string, albumName: string, filePath: string): Promise<void> {
|
||||
return uploadImageToGroupAlbumWeb_(this, groupId, albumId, albumName, filePath);
|
||||
}
|
||||
|
||||
async getGroupAlbumMediaList(groupId: number, albumId: string): Promise<any> {
|
||||
return getGroupAlbumMediaList_(this, groupId, albumId);
|
||||
}
|
||||
|
||||
async sendGroupNotice(groupId: number, content: string, options?: any) {
|
||||
return sendGroupNotice_(this, groupId, content, options);
|
||||
}
|
||||
|
||||
@@ -1120,4 +1120,82 @@ export const Oidb0xf16ReqSchema = {
|
||||
field12: { field: 12, type: 'uint32' as const },
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const Oidb0xf16RespSchema = {} satisfies ProtoSchema;
|
||||
export const Oidb0xf16RespSchema = {} satisfies ProtoSchema;
|
||||
|
||||
///////// --- group album
|
||||
// --- Request Schemas ---
|
||||
|
||||
export const ExtMapEntrySchema = {
|
||||
key: { field: 1, type: 'string' as const },
|
||||
value: { field: 2, type: 'string' as const },
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const ReqInfoSchema = {
|
||||
groupId: { field: 1, type: 'string' as const },
|
||||
albumId: { field: 2, type: 'string' as const },
|
||||
field3: { field: 3, type: 'int32' as const },
|
||||
attachInfo: { field: 4, type: 'string' as const },
|
||||
field5: { field: 5, type: 'string' as const },
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const GetMediaListRequestSchema = {
|
||||
field1: { field: 1, type: 'int32' as const },
|
||||
field2: { field: 2, type: 'bytes' as const },
|
||||
field3: { field: 3, type: 'bytes' as const },
|
||||
reqInfo: { field: 4, type: 'message' as const, schema: ReqInfoSchema },
|
||||
traceId: { field: 5, type: 'string' as const },
|
||||
extMap: { field: 10, type: 'repeated_message' as const, schema: ExtMapEntrySchema },
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
// --- Response Schemas ---
|
||||
|
||||
export const UrlInfoSchema = {
|
||||
url: { field: 1, type: 'string' as const },
|
||||
width: { field: 2, type: 'uint32' as const },
|
||||
height: { field: 3, type: 'uint32' as const },
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const PhotoUrlSchema = {
|
||||
spec: { field: 1, type: 'uint32' as const },
|
||||
url: { field: 2, type: 'message' as const, schema: UrlInfoSchema }
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const ImageInfoSchema = {
|
||||
name: { field: 1, type: 'string' as const },
|
||||
sloc: { field: 2, type: 'string' as const },
|
||||
lloc: { field: 3, type: 'string' as const },
|
||||
photoUrls: { field: 4, type: 'repeated_message' as const, schema: PhotoUrlSchema },
|
||||
defaultUrl: { field: 5, type: 'message' as const, schema: UrlInfoSchema },
|
||||
isGif: { field: 6, type: 'bool' as const },
|
||||
hasRaw: { field: 7, type: 'bool' as const },
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const MediaInfoSchema = {
|
||||
type: { field: 1, type: 'uint32' as const },
|
||||
image: { field: 2, type: 'message' as const, schema: ImageInfoSchema },
|
||||
uploader: { field: 6, type: 'string' as const },
|
||||
batchId: { field: 7, type: 'uint64' as const },
|
||||
uploadTime: { field: 8, type: 'uint64' as const },
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const GetMediaListRspDataSchema = {
|
||||
albumInfo: {
|
||||
field: 1,
|
||||
type: 'message' as const,
|
||||
schema: {
|
||||
albumId: { field: 1, type: 'string' as const },
|
||||
owner: { field: 2, type: 'string' as const },
|
||||
name: { field: 3, type: 'string' as const },
|
||||
} satisfies ProtoSchema
|
||||
},
|
||||
mediaList: { field: 3, type: 'repeated_message' as const, schema: MediaInfoSchema },
|
||||
prevAttachInfo: { field: 4, type: 'string' as const },
|
||||
nextAttachInfo: { field: 5, type: 'string' as const }, // 用于翻页拉取的 token
|
||||
} satisfies ProtoSchema;
|
||||
|
||||
export const GetMediaListResponseSchema = {
|
||||
field1: { field: 1, type: 'int32' as const },
|
||||
field2: { field: 2, type: 'bytes' as const },
|
||||
field3: { field: 3, type: 'bytes' as const },
|
||||
data: { field: 4, type: 'message' as const, schema: GetMediaListRspDataSchema }, // 将原本的 bytes 修正为 message 解析
|
||||
} satisfies ProtoSchema;
|
||||
@@ -0,0 +1,29 @@
|
||||
// Group album actions via web API
|
||||
|
||||
import type { Bridge } from '../bridge';
|
||||
import { getGroupAlbumList, uploadImageToGroupAlbum } from '../web/group-album';
|
||||
import { getCookies } from './cookies';
|
||||
|
||||
export async function getGroupAlbumListWeb(bridge: Bridge, groupId: number) {
|
||||
const groupCode = groupId.toString();
|
||||
const uin = bridge.qqInfo.uin;
|
||||
const cookieObject = await getCookies(bridge, 'qzone.qq.com');
|
||||
|
||||
const albumData = await getGroupAlbumList(cookieObject, groupCode, uin);
|
||||
|
||||
return albumData?.album || [];
|
||||
}
|
||||
|
||||
export async function uploadImageToGroupAlbumWeb(
|
||||
bridge: Bridge,
|
||||
groupId: number,
|
||||
albumId: string,
|
||||
albumName: string,
|
||||
filePath: string
|
||||
): Promise<void> {
|
||||
const groupCode = groupId.toString();
|
||||
const uin = bridge.qqInfo.uin;
|
||||
const cookieObject = await getCookies(bridge, 'qzone.qq.com');
|
||||
|
||||
await uploadImageToGroupAlbum(cookieObject, groupCode, albumId, albumName, filePath, uin);
|
||||
}
|
||||
@@ -25,3 +25,8 @@ export {
|
||||
getGroupNotice,
|
||||
deleteGroupNoticeByFid,
|
||||
} from './group-notice';
|
||||
|
||||
export {
|
||||
getGroupAlbumListWeb,
|
||||
uploadImageToGroupAlbumWeb,
|
||||
} from './group-album';
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
import { RequestUtil, cookieToString, getBknFromCookie } from './request-util';
|
||||
import { createHash } from 'crypto';
|
||||
import { readFileSync, statSync, createReadStream, writeFileSync, unlinkSync } from 'fs';
|
||||
import { basename, join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
|
||||
// 群相册信息
|
||||
export interface GroupAlbumInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
picNum: number;
|
||||
createTime: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 获取群相册列表返回
|
||||
export interface GroupAlbumListRet {
|
||||
album: GroupAlbumInfo[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 相册媒体信息
|
||||
export interface AlbumMediaInfo {
|
||||
photoId: string;
|
||||
url: string;
|
||||
uploadTime: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群相册列表
|
||||
*/
|
||||
export async function getGroupAlbumList(
|
||||
cookieObject: Record<string, string>,
|
||||
groupId: string,
|
||||
uin: string
|
||||
): Promise<GroupAlbumListRet | undefined> {
|
||||
if (!cookieObject || typeof cookieObject !== 'object') {
|
||||
throw new Error('cookieObject is required');
|
||||
}
|
||||
|
||||
const bkn = getBknFromCookie(cookieObject);
|
||||
|
||||
const url = `https://h5.qzone.qq.com/proxy/domain/u.photo.qzone.qq.com/cgi-bin/upp/qun_list_album_v2?${new URLSearchParams({
|
||||
random: '7570',
|
||||
g_tk: bkn,
|
||||
format: 'json',
|
||||
inCharset: 'utf-8',
|
||||
outCharset: 'utf-8',
|
||||
qua: 'V1_IPH_SQ_6.2.0_0_HDBM_T',
|
||||
cmd: 'qunGetAlbumList',
|
||||
qunId: groupId,
|
||||
qunid: groupId,
|
||||
start: '0',
|
||||
num: '1000',
|
||||
uin,
|
||||
getMemberRole: '0',
|
||||
}).toString()}`;
|
||||
|
||||
const ret = await RequestUtil.HttpGetJson<{ data: GroupAlbumListRet }>(
|
||||
url,
|
||||
'GET',
|
||||
'',
|
||||
{ Cookie: cookieToString(cookieObject) }
|
||||
);
|
||||
|
||||
if (!ret || typeof ret !== 'object') {
|
||||
throw new Error('invalid response from qzone api');
|
||||
}
|
||||
|
||||
return ret.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建群相册上传会话
|
||||
*/
|
||||
async function createAlbumUploadSession(
|
||||
groupId: string,
|
||||
albumId: string,
|
||||
albumName: string,
|
||||
filePath: string,
|
||||
skey: string,
|
||||
pskey: string,
|
||||
imgMd5: string,
|
||||
uin: string
|
||||
): Promise<string> {
|
||||
const imgSize = statSync(filePath).size;
|
||||
const imgName = basename(filePath);
|
||||
const bkn = getBknFromCookie({ skey });
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
const body = {
|
||||
control_req: [{
|
||||
uin,
|
||||
token: { type: 4, data: pskey, appid: 5 },
|
||||
appid: 'qun',
|
||||
checksum: imgMd5,
|
||||
check_type: 0,
|
||||
file_len: imgSize,
|
||||
env: { refer: 'qzone', deviceInfo: 'h5' },
|
||||
model: 0,
|
||||
biz_req: {
|
||||
sPicTitle: imgName,
|
||||
sPicDesc: '',
|
||||
sAlbumName: albumName,
|
||||
sAlbumID: albumId,
|
||||
iAlbumTypeID: 0,
|
||||
iBitmap: 0,
|
||||
iUploadType: 0,
|
||||
iUpPicType: 0,
|
||||
iBatchID: timestamp,
|
||||
sPicPath: '',
|
||||
iPicWidth: 0,
|
||||
iPicHight: 0,
|
||||
iWaterType: 0,
|
||||
iDistinctUse: 0,
|
||||
iNeedFeeds: 1,
|
||||
iUploadTime: timestamp,
|
||||
mapExt: { appid: 'qun', userid: groupId },
|
||||
stExtendInfo: { mapParams: { photo_num: '1', video_num: '0', batch_num: '1' } },
|
||||
},
|
||||
session: '',
|
||||
asy_upload: 0,
|
||||
cmd: 'FileUpload',
|
||||
}],
|
||||
};
|
||||
|
||||
const api = `https://h5.qzone.qq.com/webapp/json/sliceUpload/FileBatchControl/${imgMd5}?g_tk=${bkn}`;
|
||||
const cookie = `p_uin=o${uin}; p_skey=${pskey}; skey=${skey}; uin=o${uin}`;
|
||||
|
||||
const response = await RequestUtil.HttpGetJson<{ data: { session: string }, ret: number, msg: string }>(
|
||||
api,
|
||||
'POST',
|
||||
JSON.stringify(body),
|
||||
{ Cookie: cookie, 'Content-Type': 'application/json' }
|
||||
);
|
||||
|
||||
if (response.ret !== 0 || !response.data?.session) {
|
||||
throw new Error(`创建上传会话失败: ${response.msg}`);
|
||||
}
|
||||
|
||||
return response.data.session;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修复后的上传图片分片方法
|
||||
*/
|
||||
async function uploadAlbumSlice(
|
||||
session: string,
|
||||
filePath: string,
|
||||
// 移除 offset 和 chunkSize 参数,改为在内部控制
|
||||
imgMd5: string,
|
||||
skey: string,
|
||||
pskey: string,
|
||||
uin: string
|
||||
): Promise<void> {
|
||||
const img_size = statSync(filePath).size;
|
||||
const slice_size = 16384; // 严格使用 16KB
|
||||
const bkn = getBknFromCookie({ skey });
|
||||
const cookie = `p_uin=o${uin}; p_skey=${pskey}; skey=${skey}; uin=o${uin}`;
|
||||
|
||||
const stream = createReadStream(filePath, { highWaterMark: slice_size });
|
||||
let seq = 0;
|
||||
let offset = 0;
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const end = Math.min(offset + chunk.length, img_size);
|
||||
|
||||
// 使用原生的 FormData 构建安全的 multipart 请求
|
||||
const form = new FormData();
|
||||
form.append('uin', uin);
|
||||
form.append('appid', 'qun');
|
||||
form.append('session', session);
|
||||
form.append('offset', offset.toString());
|
||||
form.append('data', new Blob([chunk as Buffer], { type: 'application/octet-stream' }), 'blob');
|
||||
form.append('checksum', '');
|
||||
form.append('check_type', '0');
|
||||
form.append('retry', '0');
|
||||
form.append('seq', seq.toString());
|
||||
form.append('end', end.toString());
|
||||
form.append('cmd', 'FileUpload');
|
||||
form.append('slice_size', slice_size.toString());
|
||||
form.append('biz_req.iUploadType', '0');
|
||||
|
||||
const api = `https://h5.qzone.qq.com/webapp/json/sliceUpload/FileUpload?seq=${seq}&retry=0&offset=${offset}&end=${end}&total=${img_size}&type=form&g_tk=${bkn}`;
|
||||
|
||||
// 放弃使用 HttpGetJson,改用原生 fetch,它能完美处理 FormData 和边界
|
||||
const response = await fetch(api, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Cookie: cookie,
|
||||
// 注意:不要手动设置 Content-Type,fetch 会自动加上带有正确 boundary 的 multipart/form-data
|
||||
},
|
||||
body: form,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const post = await response.json() as { ret: number, msg: string };
|
||||
if (post.ret !== 0) {
|
||||
throw new Error(`分片 ${seq} 上传失败: ${post.msg}`);
|
||||
}
|
||||
|
||||
offset += chunk.length;
|
||||
seq++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片到群相册
|
||||
*/
|
||||
export async function uploadImageToGroupAlbum(
|
||||
cookieObject: Record<string, string>,
|
||||
groupId: string,
|
||||
albumId: string,
|
||||
albumName: string,
|
||||
filePath: string,
|
||||
uin: string
|
||||
): Promise<void> {
|
||||
const { loadBinarySource } = await import('../highway/utils');
|
||||
const loaded = await loadBinarySource(filePath, 'album image');
|
||||
|
||||
let tempFile: string | null = null;
|
||||
let actualPath = filePath;
|
||||
|
||||
if (/^(https?:\/\/|base64:\/\/)/i.test(filePath)) {
|
||||
tempFile = join(tmpdir(), `album_${Date.now()}_${Math.random().toString(36).slice(2)}.tmp`);
|
||||
writeFileSync(tempFile, loaded.bytes);
|
||||
actualPath = tempFile;
|
||||
}
|
||||
|
||||
try {
|
||||
const imgMd5 = createHash('md5').update(loaded.bytes).digest('hex');
|
||||
// const imgSize = loaded.bytes.length;
|
||||
const skey = cookieObject.skey || '';
|
||||
const pskey = cookieObject.p_skey || '';
|
||||
|
||||
const session = await createAlbumUploadSession(groupId, albumId, albumName, actualPath, skey, pskey, imgMd5, uin);
|
||||
|
||||
await uploadAlbumSlice(session, actualPath, imgMd5, skey, pskey, uin);
|
||||
|
||||
} finally {
|
||||
if (tempFile) {
|
||||
try { unlinkSync(tempFile); } catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { ApiHandler, ApiActionContext } from '../api-handler';
|
||||
import { asNumber, asString } from '../api-handler';
|
||||
import { RETCODE, failedResponse, okResponse } from '../types';
|
||||
|
||||
export function register(h: ApiHandler, ctx: ApiActionContext): void {
|
||||
h.registerAction('get_group_album_list', async (params) => {
|
||||
const groupId = asNumber(params.group_id);
|
||||
if (!groupId) return failedResponse(RETCODE.BAD_REQUEST, 'group_id is required');
|
||||
|
||||
try {
|
||||
const albumList = await ctx.getGroupAlbumList(groupId);
|
||||
return okResponse(albumList);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'failed to get group album list';
|
||||
return failedResponse(RETCODE.INTERNAL_ERROR, message);
|
||||
}
|
||||
});
|
||||
|
||||
h.registerAction('upload_image_to_qun_album', async (params) => {
|
||||
const groupId = asNumber(params.group_id);
|
||||
const albumId = asString(params.album_id);
|
||||
const albumName = asString(params.album_name);
|
||||
const file = asString(params.file);
|
||||
|
||||
if (!groupId) return failedResponse(RETCODE.BAD_REQUEST, 'group_id is required');
|
||||
if (!albumId) return failedResponse(RETCODE.BAD_REQUEST, 'album_id is required');
|
||||
if (!albumName) return failedResponse(RETCODE.BAD_REQUEST, 'album_name is required');
|
||||
if (!file) return failedResponse(RETCODE.BAD_REQUEST, 'file is required');
|
||||
|
||||
try {
|
||||
await ctx.uploadImageToGroupAlbum(groupId, albumId, albumName, file);
|
||||
return okResponse(null);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'failed to upload image to group album';
|
||||
return failedResponse(RETCODE.INTERNAL_ERROR, message);
|
||||
}
|
||||
});
|
||||
|
||||
h.registerAction('get_group_album_media_list', async (params) => {
|
||||
const groupId = asNumber(params.group_id);
|
||||
const albumId = asString(params.album_id);
|
||||
|
||||
if (!groupId) return failedResponse(RETCODE.BAD_REQUEST, 'group_id is required');
|
||||
if (!albumId) return failedResponse(RETCODE.BAD_REQUEST, 'album_id is required');
|
||||
|
||||
try {
|
||||
const mediaList = await ctx.getGroupAlbumMediaList(groupId, albumId);
|
||||
return okResponse(mediaList);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'failed to get group album media list';
|
||||
return failedResponse(RETCODE.INTERNAL_ERROR, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import { register as registerGroupAdmin } from './actions/group-admin';
|
||||
import { register as registerGroupFile } from './actions/group-file';
|
||||
import { register as registerRequest } from './actions/request';
|
||||
import { register as registerExtended } from './actions/extended';
|
||||
import { register as registerGroupAlbum } from './actions/group-album';
|
||||
|
||||
import { WebHonorType } from '@/bridge/web/group-honor';
|
||||
// import {ClientKeyInfo} from "@/bridge/bridge";
|
||||
@@ -118,6 +119,9 @@ export interface ApiActionContext {
|
||||
getGroupHonorInfo: (groupId: number, type: WebHonorType | string) => Promise<any>;
|
||||
getGroupEssence: (groupId: number, pageStart?: number, pageLimit?: number) => Promise<GroupEssenceMsgRet>;
|
||||
getGroupEssenceAll: (groupId: number) => Promise<GroupEssenceMsgRet[]>;
|
||||
getGroupAlbumList: (groupId: number) => Promise<any>;
|
||||
uploadImageToGroupAlbum: (groupId: number, albumId: string, albumName: string, filePath: string) => Promise<void>;
|
||||
getGroupAlbumMediaList: (groupId: number, albumId: string) => Promise<any>;
|
||||
sendGroupNotice: (groupId: number, content: string, options?: any) => Promise<any>;
|
||||
getGroupNotice: (groupId: number) => Promise<any[]>;
|
||||
deleteGroupNotice: (groupId: number, fid: string) => Promise<boolean>;
|
||||
@@ -145,6 +149,7 @@ export class ApiHandler {
|
||||
registerGroupFile(this, context);
|
||||
registerRequest(this, context);
|
||||
registerExtended(this, context);
|
||||
registerGroupAlbum(this, context);
|
||||
}
|
||||
|
||||
registerAction(action: string, handler: ActionHandler): void {
|
||||
|
||||
@@ -187,6 +187,9 @@ export function buildApiContext(ref: OneBotInstanceContext): ApiActionContext {
|
||||
getGroupHonorInfo: (groupId: number, type: WebHonorType | string) => bridge.getGroupHonorInfo(groupId, type),
|
||||
getGroupEssence: (groupId, pageStart = 0, pageLimit = 50) => bridge.getGroupEssence(groupId, pageStart, pageLimit),
|
||||
getGroupEssenceAll: (groupId) => bridge.getGroupEssenceAll(groupId),
|
||||
getGroupAlbumList: (groupId) => bridge.getGroupAlbumList(groupId),
|
||||
uploadImageToGroupAlbum: (groupId, albumId, albumName, filePath) => bridge.uploadImageToGroupAlbum(groupId, albumId, albumName, filePath),
|
||||
getGroupAlbumMediaList: (groupId, albumId) => bridge.getGroupAlbumMediaList(groupId, albumId),
|
||||
sendGroupNotice: (groupId, content, options) => bridge.sendGroupNotice(groupId, content, options),
|
||||
getGroupNotice: (groupId) => bridge.getGroupNotice(groupId),
|
||||
deleteGroupNotice: (groupId, fid) => bridge.deleteGroupNotice(groupId, fid),
|
||||
|
||||
Reference in New Issue
Block a user