Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/services/platform-management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[],
Expand Down
50 changes: 50 additions & 0 deletions test/services/new-chat-cap-render.test.ts
Original file line number Diff line number Diff line change
@@ -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',
)
})
})