fix(onebot): get_group_notice 返回「发给新成员」公告
get_t_list 会把「发给新成员」类公告单独放在 inst 数组里,之前只遍历 feeds, 这类公告(群规通常发在这里)完全拿不到。现在把 inst 条目追加在 feeds 之后返回, 已有条目的顺序不变,并按 fid 去重。 新增字段: - send_to_new_member:是否为「发给新成员」公告 - pinned:由 inst 条目的 settings.inst_no_pinned 映射(0 = 置顶), feeds 条目读不到置顶状态,不带该字段 Closes #2082
This commit is contained in:
@@ -78,6 +78,7 @@ export interface WebApiGroupNoticeFeed {
|
||||
remind_ts: number
|
||||
tip_window_type: number
|
||||
confirm_required: number
|
||||
inst_no_pinned?: number // 仅 inst 条目有:0 = 置顶中,1 = 未置顶
|
||||
};
|
||||
read_num: number;
|
||||
is_read: number;
|
||||
@@ -92,6 +93,7 @@ export interface WebApiGroupNoticeRet {
|
||||
read_only: number
|
||||
role: number
|
||||
feeds: WebApiGroupNoticeFeed[]
|
||||
inst?: WebApiGroupNoticeFeed[] // 「发给新成员」类公告,不在 feeds 里
|
||||
group: {
|
||||
group_id: number
|
||||
class_ext: number
|
||||
|
||||
@@ -21,6 +21,8 @@ const ReturnSchema = Type.Array(Type.Object({
|
||||
}, { description: '公告内容' }),
|
||||
settings: Type.Optional(Type.Any({ description: '设置项' })),
|
||||
read_num: Type.Optional(Type.Number({ description: '阅读数' })),
|
||||
send_to_new_member: Type.Optional(Type.Boolean({ description: '是否为「发给新成员」公告' })),
|
||||
pinned: Type.Optional(Type.Boolean({ description: '是否置顶(仅「发给新成员」公告可读到)' })),
|
||||
}), { description: '群公告列表' });
|
||||
|
||||
type ReturnType = Static<typeof ReturnSchema>;
|
||||
@@ -44,30 +46,44 @@ export class GetGroupNotice extends OneBotAction<PayloadType, ReturnType> {
|
||||
throw new Error('获取公告失败');
|
||||
}
|
||||
const retNotices: ReturnType = [];
|
||||
for (const key in ret.feeds) {
|
||||
if (!ret.feeds[key]) {
|
||||
continue;
|
||||
const seen = new Set<string>();
|
||||
// 「发给新成员」类公告只出现在 inst 里,追加在 feeds 之后,保持原有条目的顺序不变
|
||||
for (const [list, sendToNewMember] of [[ret.feeds, false], [ret.inst, true]] as const) {
|
||||
for (const retApiNotice of Array.isArray(list) ? list : []) {
|
||||
if (!retApiNotice || seen.has(retApiNotice.fid)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(retApiNotice.fid);
|
||||
retNotices.push(this.toNotice(retApiNotice, sendToNewMember));
|
||||
}
|
||||
const retApiNotice: WebApiGroupNoticeFeed = ret.feeds[key];
|
||||
const image = retApiNotice.msg.pics?.map((pic) => {
|
||||
return { id: pic.id, height: pic.h, width: pic.w };
|
||||
}) || [];
|
||||
|
||||
const retNotice: ReturnType[number] = {
|
||||
notice_id: retApiNotice.fid,
|
||||
sender_id: retApiNotice.u,
|
||||
publish_time: retApiNotice.pubt,
|
||||
message: {
|
||||
text: retApiNotice.msg.text,
|
||||
image,
|
||||
images: image,
|
||||
},
|
||||
settings: retApiNotice.settings,
|
||||
read_num: retApiNotice.read_num,
|
||||
};
|
||||
retNotices.push(retNotice);
|
||||
}
|
||||
|
||||
return retNotices;
|
||||
}
|
||||
|
||||
private toNotice (retApiNotice: WebApiGroupNoticeFeed, sendToNewMember: boolean): ReturnType[number] {
|
||||
const image = retApiNotice.msg.pics?.map((pic) => {
|
||||
return { id: pic.id, height: pic.h, width: pic.w };
|
||||
}) || [];
|
||||
|
||||
const retNotice: ReturnType[number] = {
|
||||
notice_id: retApiNotice.fid,
|
||||
sender_id: retApiNotice.u,
|
||||
publish_time: retApiNotice.pubt,
|
||||
message: {
|
||||
text: retApiNotice.msg.text,
|
||||
image,
|
||||
images: image,
|
||||
},
|
||||
settings: retApiNotice.settings,
|
||||
read_num: retApiNotice.read_num,
|
||||
send_to_new_member: sendToNewMember,
|
||||
};
|
||||
// 置顶状态只在 inst 条目的 settings.inst_no_pinned 里:0 = 置顶中,1 = 未置顶
|
||||
const instNoPinned = retApiNotice.settings?.inst_no_pinned;
|
||||
if (typeof instNoPinned === 'number') {
|
||||
retNotice.pinned = instNoPinned === 0;
|
||||
}
|
||||
return retNotice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// StreamBasic must be evaluated before OneBotAction: the two modules import each other,
|
||||
// and loading OneBotAction first leaves BasicStream extending an uninitialized binding.
|
||||
import '../napcat-onebot/action/stream/StreamBasic';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
import { GetGroupNotice } from '../napcat-onebot/action/group/GetGroupNotice';
|
||||
|
||||
function feed (fid: string, settings?: Record<string, number>) {
|
||||
return {
|
||||
u: 10001,
|
||||
fid,
|
||||
pubt: 1750000000,
|
||||
msg: { text: `notice ${fid}`, text_face: '', title: '' },
|
||||
type: 1,
|
||||
fn: 0,
|
||||
cn: 0,
|
||||
vn: 0,
|
||||
settings,
|
||||
read_num: 3,
|
||||
is_read: 0,
|
||||
is_all_confirm: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function createAction (ret: unknown) {
|
||||
return new GetGroupNotice(
|
||||
undefined as never,
|
||||
{ apis: { WebApi: { getGroupNotice: vi.fn().mockResolvedValue(ret) } } } as never
|
||||
);
|
||||
}
|
||||
|
||||
describe('_get_group_notice', () => {
|
||||
test('returns "send to new member" notices from inst after feeds', async () => {
|
||||
const action = createAction({
|
||||
ec: 0,
|
||||
feeds: [feed('feed-1'), feed('feed-2')],
|
||||
inst: [feed('inst-1', { inst_no_pinned: 0 })],
|
||||
});
|
||||
|
||||
const notices = await action._handle({ group_id: '123' });
|
||||
|
||||
expect(notices.map(n => n.notice_id)).toEqual(['feed-1', 'feed-2', 'inst-1']);
|
||||
expect(notices.map(n => n.send_to_new_member)).toEqual([false, false, true]);
|
||||
expect(notices[2]?.message.text).toBe('notice inst-1');
|
||||
});
|
||||
|
||||
test('maps inst_no_pinned to pinned only when the field is present', async () => {
|
||||
const action = createAction({
|
||||
ec: 0,
|
||||
feeds: [feed('feed-1', { tip_window_type: 1 })],
|
||||
inst: [feed('pinned', { inst_no_pinned: 0 }), feed('unpinned', { inst_no_pinned: 1 })],
|
||||
});
|
||||
|
||||
const notices = await action._handle({ group_id: '123' });
|
||||
|
||||
expect(notices.find(n => n.notice_id === 'feed-1')?.pinned).toBeUndefined();
|
||||
expect(notices.find(n => n.notice_id === 'pinned')?.pinned).toBe(true);
|
||||
expect(notices.find(n => n.notice_id === 'unpinned')?.pinned).toBe(false);
|
||||
});
|
||||
|
||||
test('keeps working when inst is missing and skips duplicated notices', async () => {
|
||||
const withoutInst = await createAction({ ec: 0, feeds: [feed('feed-1')] })._handle({ group_id: '123' });
|
||||
expect(withoutInst.map(n => n.notice_id)).toEqual(['feed-1']);
|
||||
|
||||
const duplicated = await createAction({
|
||||
ec: 0,
|
||||
feeds: [feed('same')],
|
||||
inst: [feed('same', { inst_no_pinned: 0 })],
|
||||
})._handle({ group_id: '123' });
|
||||
expect(duplicated.map(n => n.notice_id)).toEqual(['same']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user