feat: show session ID in dashboard status display
Add session label (last 8 chars) next to each group status. Export buildStatusContent and DashboardOptions for testing.
This commit is contained in:
@@ -20,9 +20,11 @@ export const ASSISTANT_HAS_OWN_NUMBER =
|
|||||||
(process.env.ASSISTANT_HAS_OWN_NUMBER ||
|
(process.env.ASSISTANT_HAS_OWN_NUMBER ||
|
||||||
envConfig.ASSISTANT_HAS_OWN_NUMBER) === 'true';
|
envConfig.ASSISTANT_HAS_OWN_NUMBER) === 'true';
|
||||||
export const SESSION_COMMAND_USER_IDS = new Set(
|
export const SESSION_COMMAND_USER_IDS = new Set(
|
||||||
(process.env.SESSION_COMMAND_USER_IDS ||
|
(
|
||||||
|
process.env.SESSION_COMMAND_USER_IDS ||
|
||||||
envConfig.SESSION_COMMAND_USER_IDS ||
|
envConfig.SESSION_COMMAND_USER_IDS ||
|
||||||
'')
|
''
|
||||||
|
)
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((id) => id.trim())
|
.map((id) => id.trim())
|
||||||
.filter(Boolean),
|
.filter(Boolean),
|
||||||
|
|||||||
49
src/dashboard.test.ts
Normal file
49
src/dashboard.test.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { buildStatusContent, type DashboardOptions } from './dashboard.js';
|
||||||
|
|
||||||
|
function makeOptions(sessionId?: string): DashboardOptions {
|
||||||
|
const sessions: Record<string, string> = sessionId
|
||||||
|
? { 'group-folder': sessionId }
|
||||||
|
: {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
assistantName: 'Test',
|
||||||
|
channels: [],
|
||||||
|
getSessions: () => sessions,
|
||||||
|
queue: {
|
||||||
|
getStatuses: () => [
|
||||||
|
{
|
||||||
|
jid: 'dc:123',
|
||||||
|
status: 'inactive',
|
||||||
|
elapsedMs: null,
|
||||||
|
pendingMessages: false,
|
||||||
|
pendingTasks: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as DashboardOptions['queue'],
|
||||||
|
registeredGroups: () => ({
|
||||||
|
'dc:123': {
|
||||||
|
name: 'clone-test',
|
||||||
|
folder: 'group-folder',
|
||||||
|
trigger: '@bot',
|
||||||
|
added_at: '2026-03-16T00:00:00.000Z',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
statusChannelId: 'status',
|
||||||
|
statusUpdateInterval: 60000,
|
||||||
|
usageUpdateInterval: 60000,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('buildStatusContent', () => {
|
||||||
|
it('shows cleared sessions as empty', () => {
|
||||||
|
const content = buildStatusContent(makeOptions());
|
||||||
|
expect(content).toContain('세션 없음');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows a shortened session id when a session exists', () => {
|
||||||
|
const content = buildStatusContent(makeOptions('session-1234567890'));
|
||||||
|
expect(content).toContain('세션 34567890');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,9 +8,10 @@ import { GroupQueue, GroupStatus } from './group-queue.js';
|
|||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
import { Channel, ChannelMeta, RegisteredGroup } from './types.js';
|
import { Channel, ChannelMeta, RegisteredGroup } from './types.js';
|
||||||
|
|
||||||
interface DashboardOptions {
|
export interface DashboardOptions {
|
||||||
assistantName: string;
|
assistantName: string;
|
||||||
channels: Channel[];
|
channels: Channel[];
|
||||||
|
getSessions: () => Record<string, string>;
|
||||||
queue: GroupQueue;
|
queue: GroupQueue;
|
||||||
registeredGroups: () => Record<string, RegisteredGroup>;
|
registeredGroups: () => Record<string, RegisteredGroup>;
|
||||||
statusChannelId: string;
|
statusChannelId: string;
|
||||||
@@ -114,8 +115,16 @@ function getStatusLabel(status: GroupStatus): string {
|
|||||||
return '비활성';
|
return '비활성';
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildStatusContent(opts: DashboardOptions): string {
|
function getSessionLabel(sessionId: string | undefined): string {
|
||||||
|
if (!sessionId) return '세션 없음';
|
||||||
|
const shortId = sessionId.length > 8 ? sessionId.slice(-8) : sessionId;
|
||||||
|
return `세션 ${shortId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal - exported for testing */
|
||||||
|
export function buildStatusContent(opts: DashboardOptions): string {
|
||||||
const registeredGroups = opts.registeredGroups();
|
const registeredGroups = opts.registeredGroups();
|
||||||
|
const sessions = opts.getSessions();
|
||||||
const jids = Object.keys(registeredGroups);
|
const jids = Object.keys(registeredGroups);
|
||||||
const statuses = opts.queue.getStatuses(jids);
|
const statuses = opts.queue.getStatuses(jids);
|
||||||
|
|
||||||
@@ -153,8 +162,9 @@ function buildStatusContent(opts: DashboardOptions): string {
|
|||||||
const lines = categoryEntries.map((entry) => {
|
const lines = categoryEntries.map((entry) => {
|
||||||
const icon = STATUS_ICONS[entry.status.status] || '⚪';
|
const icon = STATUS_ICONS[entry.status.status] || '⚪';
|
||||||
const label = getStatusLabel(entry.status);
|
const label = getStatusLabel(entry.status);
|
||||||
|
const sessionLabel = getSessionLabel(sessions[entry.group.folder]);
|
||||||
const name = entry.meta?.name ? `#${entry.meta.name}` : entry.group.name;
|
const name = entry.meta?.name ? `#${entry.meta.name}` : entry.group.name;
|
||||||
return ` ${icon} **${name}** — ${label}`;
|
return ` ${icon} **${name}** — ${label} · ${sessionLabel}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (channelMetaCache.size > 0 && categoryName !== '기타') {
|
if (channelMetaCache.size > 0 && categoryName !== '기타') {
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ async function main(): Promise<void> {
|
|||||||
const dashboardOpts = {
|
const dashboardOpts = {
|
||||||
assistantName: ASSISTANT_NAME,
|
assistantName: ASSISTANT_NAME,
|
||||||
channels,
|
channels,
|
||||||
|
getSessions: () => sessions,
|
||||||
queue,
|
queue,
|
||||||
registeredGroups: () => registeredGroups,
|
registeredGroups: () => registeredGroups,
|
||||||
statusChannelId: STATUS_CHANNEL_ID,
|
statusChannelId: STATUS_CHANNEL_ID,
|
||||||
|
|||||||
@@ -238,7 +238,9 @@ describe('handleSessionCommand', () => {
|
|||||||
isAdminSender: vi.fn().mockReturnValue(true),
|
isAdminSender: vi.fn().mockReturnValue(true),
|
||||||
});
|
});
|
||||||
const result = await handleSessionCommand({
|
const result = await handleSessionCommand({
|
||||||
missedMessages: [makeMsg('/clear', { is_from_me: false, sender: 'discord-user-1' })],
|
missedMessages: [
|
||||||
|
makeMsg('/clear', { is_from_me: false, sender: 'discord-user-1' }),
|
||||||
|
],
|
||||||
isMainGroup: false,
|
isMainGroup: false,
|
||||||
groupName: 'test',
|
groupName: 'test',
|
||||||
triggerPattern: trigger,
|
triggerPattern: trigger,
|
||||||
|
|||||||
Reference in New Issue
Block a user