Add group album media actions and API integration

Introduces new OneBot actions for group album media: listing, commenting, liking, and deleting. Adds supporting API methods and data structures for album media operations in NTQQWebApi and NodeIKernelAlbumService. Updates action router and index to register new actions.
This commit is contained in:
手瓜一十雪
2025-08-26 14:58:11 +08:00
parent f5052935bd
commit 75d26465f1
9 changed files with 444 additions and 6 deletions

View File

@@ -12,6 +12,7 @@ import { createReadStream, readFileSync, statSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { basename } from 'node:path';
import { qunAlbumControl } from '../data/webapi';
import { createAlbumCommentRequest, createAlbumFeedPublish, createAlbumMediaFeed } from '../data/album';
export class NTQQWebApi {
context: InstanceContext;
core: NapCatCore;
@@ -443,4 +444,73 @@ export class NTQQWebApi {
if (!session) throw new Error('创建群相册会话失败');
await this.uploadQunAlbumSlice(path, session, skey, pskey, uin, 16384);
}
async getAlbumMediaListByNTQQ(gc: string, albumId: string, attach_info: string = '') {
return (await this.context.session.getAlbumService().getMediaList({
qun_id: gc,
attach_info: attach_info,
seq: 0,
request_time_line: {
request_invoke_time: "0"
},
album_id: albumId,
lloc: '',
batch_id: ''
})).response;
}
async doAlbumMediaPlainCommentByNTQQ(
qunId: string,
albumId: string,
lloc: string,
content: string) {
const random_seq = Math.floor(Math.random() * 9000) + 1000;
const uin = this.core.selfInfo.uin || '10001';
//16位number数字
const client_key = Date.now() * 1000
return await this.context.session.getAlbumService().doQunComment(
random_seq, {
map_info: [],
map_bytes_info: [],
map_user_account: []
},
qunId,
2,
createAlbumMediaFeed(uin, albumId, lloc),
createAlbumCommentRequest(uin, content, client_key)
);
}
async deleteAlbumMediaByNTQQ(
qunId: string,
albumId: string,
lloc: string) {
const random_seq = Math.floor(Math.random() * 9000) + 1000;
return await this.context.session.getAlbumService().deleteMedias(
random_seq,
qunId,
albumId,
[lloc],
[]
);
}
async doAlbumMediaLikeByNTQQ(
qunId: string,
albumId: string,
lloc: string,
id: string) {
const random_seq = Math.floor(Math.random() * 9000) + 1000;
const uin = this.core.selfInfo.uin || '10001';
return await this.context.session.getAlbumService().doQunLike(
random_seq, {
map_info: [],
map_bytes_info: [],
map_user_account: []
}, {
id: id,
status: 1
},
createAlbumFeedPublish(qunId, uin, albumId, lloc)
)
}
}

221
src/core/data/album.ts Normal file
View File

@@ -0,0 +1,221 @@
/**
* 群相册列表请求参数接口
*/
export interface AlbumListRequest {
qun_id: string;
attach_info: string;
seq: number;
request_time_line: {
request_invoke_time: string;
};
album_id: string;
lloc: string;
batch_id: string;
}
/**
* 创建群相册列表请求参数
* @param qunId 群号
* @param albumId 相册ID
* @param seq 请求序列号默认值为0
* @returns 请求参数对象
*/
export function createAlbumListRequest(
qunId: string,
albumId: string,
seq: number = 0
): AlbumListRequest {
return {
qun_id: qunId,
attach_info: "",
seq: seq,
request_time_line: {
request_invoke_time: "0"
},
album_id: albumId,
lloc: "",
batch_id: ""
};
}
/**
* 相册媒体项请求接口
*/
export interface AlbumMediaFeed {
cell_common: {
time: string;
};
cell_user_info: {
user: {
uin: string;
};
};
cell_media: {
album_id: string;
batch_id: string;
media_items: Array<{
image: {
lloc: string;
};
}>;
};
}
/**
* 创建相册媒体请求参数
* @param uin 用户QQ号
* @param albumId 相册ID
* @param lloc
* @returns 媒体请求参数对象
*/
export function createAlbumMediaFeed(
uin: string,
albumId: string,
lloc: string
): AlbumMediaFeed {
return {
cell_common: {
time: ""
},
cell_user_info: {
user: {
uin: uin
}
},
cell_media: {
album_id: albumId,
batch_id: "",
media_items: [{
image: {
lloc: lloc
}
}]
}
};
}
/**
* 相册评论内容接口
*/
export interface AlbumCommentContent {
type: number;
content: string;
who: number;
uid: string;
name: string;
url: string;
}
/**
* 相册评论请求接口
*/
export interface AlbumCommentReplyContent {
client_key: number;
content: AlbumCommentContent[];
user: {
uin: string;
};
}
export enum RichMsgType {
KRICHMSGTYPEPLAINTEXT,
KRICHMSGTYPEAT,
KRICHMSGTYPEURL,
KRICHMSGTYPEMEDIA
}
/**
* 创建相册评论请求参数
* @param uin 用户QQ号
* @param content 评论内容
* @param client_key 客户端鉴权密钥
* @returns 评论请求参数对象
*/
export function createAlbumCommentRequest(
uin: string,
content: string,
client_key: number
): AlbumCommentReplyContent {
return {
client_key: client_key,
//暂时只支持纯文本吧
content: [{
type: RichMsgType.KRICHMSGTYPEPLAINTEXT,
content: content,
who: 0,
uid: "",
name: "",
url: ""
}],
user: {
uin: uin
}
};
}
export interface AlbumFeedLikePublish {
cell_common: {
time: number;
feed_id: string;
};
cell_user_info: {
user: {
uin: string;
};
};
cell_media: {
album_id: string;
batch_id: number;
media_items: Array<{
type: number;
image: {
lloc: string;
sloc: string;
};
}>;
};
cell_qun_info: {
qun_id: string;
};
}
/**
* 创建相册动态发布请求参数
* @param qunId 群号
* @param uin 用户QQ号
* @param albumId 相册ID
* @param lloc 信息
* @param sloc 信息(可选默认与lloc相同)
* @returns 动态发布请求参数对象
*/
export function createAlbumFeedPublish(
qunId: string,
uin: string,
albumId: string,
lloc: string,
sloc?: string
): AlbumFeedLikePublish {
return {
cell_common: {
time: Date.now(),
feed_id: ""
},
cell_user_info: {
user: {
uin: uin
}
},
cell_media: {
album_id: albumId,
batch_id: 0,
media_items: [{
type: 0,
image: {
lloc: lloc,
sloc: sloc || lloc
}
}]
},
cell_qun_info: {
qun_id: qunId
}
};
}

View File

@@ -1,3 +1,5 @@
import { AlbumCommentReplyContent, AlbumFeedLikePublish, AlbumListRequest, AlbumMediaFeed } from "../data/album";
export interface NodeIKernelAlbumService {
setAlbumServiceInfo(...args: unknown[]): unknown;// needs 3 arguments
@@ -19,7 +21,7 @@ export interface NodeIKernelAlbumService {
trace_id: string,
is_from_cache: boolean,
request_time_line: unknown,
album_list: Array<unknown>,
album_list: Array<{ name: string, album_id: string }>,
attach_info: string,
has_more: boolean,
right: unknown,
@@ -32,11 +34,19 @@ export interface NodeIKernelAlbumService {
addAlbum(...args: unknown[]): unknown;// needs 2 arguments
deleteMedias(...args: unknown[]): unknown;// needs 4 arguments
deleteMedias(seq: number, group_code: string, album_id: string, media_ids: string[], ban_ids: unknown[]): Promise<unknown>;// needs 4 arguments
modifyAlbum(...args: unknown[]): unknown;// needs 3 arguments
getMediaList(...args: unknown[]): unknown;// needs 1 arguments
getMediaList(param: AlbumListRequest): Promise<{
response: {
seq: number,
result: number,
errMs: string,//没错就是errMs不是errMsg
trace_id: string,
request_time_line: unknown,
}
}>;// needs 1 arguments
quoteToQzone(...args: unknown[]): unknown;// needs 1 arguments
@@ -55,12 +65,36 @@ export interface NodeIKernelAlbumService {
getQunLikes(...args: unknown[]): unknown;// needs 4 arguments
deleteQunFeed(...args: unknown[]): unknown;// needs 1 arguments
doQunComment(...args: unknown[]): unknown;// needs 6 arguments
//seq random
//stCommonExt {"map_info":[],"map_bytes_info":[],"map_user_account":[]}
//qunId string
doQunComment(seq: number, ext: {
map_info: unknown[],
map_bytes_info: unknown[],
map_user_account: unknown[]
},
qunId: string,
commentType: number,
feed: AlbumMediaFeed,
content: AlbumCommentReplyContent,
): Promise<unknown>;// needs 6 arguments
doQunReply(...args: unknown[]): unknown;// needs 7 arguments
doQunLike(...args: unknown[]): unknown;// needs 5 arguments
doQunLike(
seq: number,
ext: {
map_info: unknown[],
map_bytes_info: unknown[],
map_user_account: unknown[]
},
param: {
//{"id":"421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|V5bCgAxMDEyOTU5MjU3e*KqaLVYdic!^||^421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|17560336594^||^1","status":1}
id: string,
status: number
},
like: AlbumFeedLikePublish
): Promise<unknown>;// needs 5 arguments
getRedPoints(...args: unknown[]): unknown;// needs 3 arguments

View File

@@ -0,0 +1,24 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
album_id: Type.String(),
lloc: Type.String()
});
type Payload = Static<typeof SchemaData>;
export class DelGroupAlbumMedia extends OneBotAction<Payload, unknown> {
override actionName = ActionName.DelGroupAlbumMedia;
override payloadSchema = SchemaData;
async _handle(payload: Payload) {
return await this.core.apis.WebApi.deleteAlbumMediaByNTQQ(
payload.group_id,
payload.album_id,
payload.lloc
);
}
}

View File

@@ -0,0 +1,26 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
album_id: Type.String(),
lloc: Type.String(),
content: Type.String(),
});
type Payload = Static<typeof SchemaData>;
export class DoGroupAlbumComment extends OneBotAction<Payload, unknown> {
override actionName = ActionName.DoGroupAlbumComment;
override payloadSchema = SchemaData;
async _handle(payload: Payload) {
return await this.core.apis.WebApi.doAlbumMediaPlainCommentByNTQQ(
payload.group_id,
payload.album_id,
payload.lloc,
payload.content
);
}
}

View File

@@ -0,0 +1,24 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
album_id: Type.String(),
attach_info: Type.String({ default: "" }),
});
type Payload = Static<typeof SchemaData>;
export class GetGroupAlbumMediaList extends OneBotAction<Payload, unknown> {
override actionName = ActionName.GetGroupAlbumMediaList;
override payloadSchema = SchemaData;
async _handle(payload: Payload) {
return await this.core.apis.WebApi.getAlbumMediaListByNTQQ(
payload.group_id,
payload.album_id,
payload.attach_info
);
}
}

View File

@@ -0,0 +1,27 @@
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
album_id: Type.String(),
lloc: Type.String(),
id: Type.String(),//421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|V5bCgAxMDEyOTU5MjU3.PyqaPndPxg!^||^421_1_0_1012959257|V61Yiali4PELg90bThrH4Bo2iI1M5Kab|17560363448^||^1
set: Type.Boolean({default: true})//true=点赞 false=取消点赞 未实现
});
type Payload = Static<typeof SchemaData>;
export class SetGroupAlbumMediaLike extends OneBotAction<Payload, unknown> {
override actionName = ActionName.SetGroupAlbumMediaLike;
override payloadSchema = SchemaData;
async _handle(payload: Payload) {
return await this.core.apis.WebApi.doAlbumMediaLikeByNTQQ(
payload.group_id,
payload.album_id,
payload.lloc,
payload.id
);
}
}

View File

@@ -126,10 +126,18 @@ import { GetCollectionList } from './extends/GetCollectionList';
import { SetGroupTodo } from './packet/SetGroupTodo';
import { GetQunAlbumList } from './extends/GetQunAlbumList';
import { UploadImageToQunAlbum } from './extends/UploadImageToQunAlbum';
import { DoGroupAlbumComment } from './extends/DoGroupAlbumComment';
import { GetGroupAlbumMediaList } from './extends/GetGroupAlbumMediaList';
import { SetGroupAlbumMediaLike } from './extends/SetGroupAlbumMediaLike';
import { DelGroupAlbumMedia } from './extends/DelGroupAlbumMedia';
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
const actionHandlers = [
new DelGroupAlbumMedia(obContext, core),
new SetGroupAlbumMediaLike(obContext, core),
new DoGroupAlbumComment(obContext, core),
new GetGroupAlbumMediaList(obContext, core),
new GetQunAlbumList(obContext, core),
new UploadImageToQunAlbum(obContext, core),
new SetGroupTodo(obContext, core),

View File

@@ -10,6 +10,10 @@ export interface InvalidCheckResult {
}
export const ActionName = {
DelGroupAlbumMedia: 'del_group_album_media',
SetGroupAlbumMediaLike: 'set_group_album_media_like',
DoGroupAlbumComment: 'do_group_album_comment',
GetGroupAlbumMediaList: 'get_group_album_media_list',
UploadImageToQunAlbum: 'upload_image_to_qun_album',
GetQunAlbumList: 'get_qun_album_list',
SetGroupTodo: 'set_group_todo',