[api] msg delete

This commit is contained in:
Devin Zuczek
2026-08-22 12:23:31 -04:00
parent 8c5de75eed
commit 1a2643240b
3 changed files with 47 additions and 0 deletions
+9
View File
@@ -213,6 +213,15 @@ export const SendMultipleMessagesRequest = z.object({
Data: z.string().optional().describe('The message payload; often empty'),
})
/**
* `POST /api/messages/v3/delete` JSON body — the messages the client is dropping from
* its inbox. Ids are the `Id` of a stored message, which this server has never issued:
* with no message store the list is only ever echoed back as accepted.
*/
export const DeleteMessagesRequest = z.object({
MessageIds: z.array(z.int()).describe('Ids of the messages to delete'),
})
/**
* `POST /api/messages/v1/friendOnlineStatus` — how many of the caller's friends are
* online, wrapped in the client's `{ success, value }` envelope.
+23
View File
@@ -22,6 +22,7 @@ import { authedId, unauthorized } from '../http'
import {
AckResponse,
AUTHED,
DeleteMessagesRequest,
ErrorResponse,
form,
FriendOnlineCountResponse,
@@ -624,6 +625,28 @@ export const socialRoutes = new Hono<App>({ strict: false })
}),
(c) => c.json([])
)
// The inbox's delete button. A POST rather than a DELETE because the ids arrive as
// a JSON array body — the client batches a multi-select into one call, and its HTTP
// layer only ever sends a body on POST/PUT.
.post(
'/api/messages/v3/delete',
describeRoute({
tags: ['Social'],
summary: 'Delete messages',
description:
'Drops the given messages from the callers inbox. Nothing happens: there is no ' +
'message store behind `GET /api/messages/v2/get` (the `MessageReceived` ' +
'notification is the whole delivery — see `POST /api/messages/v2/send`), so ' +
'there are no ids to match and nothing to remove.\n\n' +
'Accepted unconditionally and answered 200 with an empty body, which is what the ' +
'client wants: it removes the rows locally and re-reads the empty list either ' +
'way. Not auth-gated, for the same reason `GET /api/messages/v2/get` isnt — ' +
'the call reaches no state to protect.',
requestBody: jsonBody(DeleteMessagesRequest, 'The messages to delete'),
responses: { 200: { description: 'Accepted (empty body)' } },
}),
(c) => c.body(null, 200)
)
// How many of the caller's friends are online — the friends panel's header count.
// Answered from the friend graph joined to live presence, so it agrees with the
// friends the panel then lists. Auth-gated: the count is the CALLER's own.
+15
View File
@@ -3418,6 +3418,20 @@ describe('messages', () => {
expect(res.status).toBe(401)
expect(await pushed()).toEqual([])
})
test('POST /api/messages/v3/delete accepts anything with an empty 200', async () => {
// No message store, so no id can be real and nothing is gated — an unknown id, an
// empty list and a missing body all land the same way.
for (const body of [{ MessageIds: [1787377235629] }, { MessageIds: [] }, {}]) {
const res = await exports.default.fetch(`${ORIGIN}/api/messages/v3/delete`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
expect(res.status).toBe(200)
expect(await res.text()).toBe('')
}
})
})
describe('mutual friends', () => {
@@ -4837,6 +4851,7 @@ describe('openapi', () => {
'POST /api/messages/v1/friendOnlineStatus',
'POST /api/messages/v1/sendMultiple',
'POST /api/messages/v2/send',
'POST /api/messages/v3/delete',
'POST /api/playerReputation/v1/bulk',
'POST /api/playerReputation/v2/bulk',
'POST /api/playerevents/v1/bulkInvite',