diff --git a/src/services/platform-management.ts b/src/services/platform-management.ts index 2188e3a..330cc06 100644 --- a/src/services/platform-management.ts +++ b/src/services/platform-management.ts @@ -147,10 +147,34 @@ export function renderManagedSession(session: Session): string { : [`Status reason\t${session.statusReason}`]), `Connection\t${session.type}`, `Number\t${session.testMode ? 'test' : 'real'}`, + ...newChatCapLine(session), `Updated\t${session.updatedAt}`, ].join('\n') } +/** WhatsApp's per-number new-chat cap, as Get session returns it. */ +interface NewChatCapView { + readonly enabled: boolean | null + readonly capped: boolean + readonly limit: number | null + readonly used: number | null + readonly resetsAt: string | null +} + +function newChatCapLine(session: Session): string[] { + const cap = (session as Session & {newChatCapping?: NewChatCapView | null}) + .newChatCapping + if (cap === undefined) return [] + if (cap === null) return ['New-chat cap\tnot observed yet'] + if (cap.enabled === null) return ['New-chat cap\tundetermined'] + if (!cap.enabled) return ['New-chat cap\tnot enabled by WhatsApp'] + const reset = cap.resetsAt === null ? '' : ` until ${cap.resetsAt}` + if (cap.capped) return [`New-chat cap\tcapped${reset}`] + if (cap.limit === null || cap.used === null) + return ['New-chat cap\tenabled by WhatsApp'] + return [`New-chat cap\t${cap.used} of ${cap.limit} used${reset}`] +} + export async function stopSessions( client: ClientInstance<'organization'>, sessionIds: readonly string[], diff --git a/test/services/new-chat-cap-render.test.ts b/test/services/new-chat-cap-render.test.ts new file mode 100644 index 0000000..30ee5d8 --- /dev/null +++ b/test/services/new-chat-cap-render.test.ts @@ -0,0 +1,50 @@ +import type {Session} from '@polymorfa/sdk' +import {describe, expect, test} from 'vitest' + +import {renderManagedSession} from '../../src/services/platform-management.js' + +const base = { + sessionId: 'session_1', + name: 'Support', + tenantId: 'team_1', + type: 'linked_device', + testMode: false, + status: 'CONNECTED', + createdAt: '2026-09-26T00:00:00.000Z', + updatedAt: '2026-09-27T00:00:00.000Z', +} as unknown as Session + +const cap = (newChatCapping: unknown) => + renderManagedSession({...base, newChatCapping} as unknown as Session) + .split('\n') + .find((line) => line.startsWith('New-chat cap')) + +describe('session view new-chat cap line', () => { + test('omits the line when the API returned no field', () => { + expect(renderManagedSession(base)).not.toContain('New-chat cap') + }) + + test('describes each WhatsApp state', () => { + const on = { + enabled: true, + capped: false, + limit: 50, + used: 26, + resetsAt: '2026-10-01T00:00:00.000Z', + } + expect(cap(null)).toBe('New-chat cap\tnot observed yet') + expect(cap({...on, enabled: null})).toBe('New-chat cap\tundetermined') + expect(cap({...on, enabled: false})).toBe( + 'New-chat cap\tnot enabled by WhatsApp', + ) + expect(cap(on)).toBe( + 'New-chat cap\t26 of 50 used until 2026-10-01T00:00:00.000Z', + ) + expect(cap({...on, capped: true, used: 50})).toBe( + 'New-chat cap\tcapped until 2026-10-01T00:00:00.000Z', + ) + expect(cap({...on, limit: null, used: null, resetsAt: null})).toBe( + 'New-chat cap\tenabled by WhatsApp', + ) + }) +})