feat(rust): slash commands and the next wipe, server half (phase 16)
Five read-only commands registered with api.registerSlashCommands: /status, /wipe, /top, /online and /clan (D126). Every refusal is private, and any answer narrower than public (online names, a clan roster) goes to the caller alone (D127). No command asks a sidecar. The next wipe (D128, D130): six nullable columns on rust_servers, a pure nextWipe(row, now) with the zone arithmetic through Intl, computed on every read. The public server shape gains nextWipe; the admin shape gains the stored schedule; PUT /admin/rust/servers/:id takes the six fields and writes them only when wipeRule is present. server/commands joins ci/bundle.json, which checkBundle caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -24,6 +24,8 @@ async function listServers({ enabledOnly = false } = {}) {
|
||||
return core.query(
|
||||
`SELECT id, name, sidecar_base_url AS sidecarBaseUrl, sidecar_token_enc AS sidecarTokenEnc,
|
||||
protocol, enabled, sort_order AS sortOrder, announce_news AS announceNews,
|
||||
wipe_rule AS wipeRule, wipe_day AS wipeDay, wipe_time AS wipeTime, wipe_tz AS wipeTz,
|
||||
DATE_FORMAT(wipe_anchor, '%Y-%m-%d') AS wipeAnchor, wipe_once_at AS wipeOnceAt,
|
||||
created_at AS createdAt, updated_at AS updatedAt
|
||||
FROM ${SERVERS}
|
||||
${enabledOnly ? 'WHERE enabled = 1' : ''}
|
||||
@@ -34,7 +36,10 @@ async function listServers({ enabledOnly = false } = {}) {
|
||||
async function getServer(id) {
|
||||
const rows = await core.query(
|
||||
`SELECT id, name, sidecar_base_url AS sidecarBaseUrl, sidecar_token_enc AS sidecarTokenEnc,
|
||||
protocol, enabled, sort_order AS sortOrder, created_at AS createdAt, updated_at AS updatedAt
|
||||
protocol, enabled, sort_order AS sortOrder,
|
||||
wipe_rule AS wipeRule, wipe_day AS wipeDay, wipe_time AS wipeTime, wipe_tz AS wipeTz,
|
||||
DATE_FORMAT(wipe_anchor, '%Y-%m-%d') AS wipeAnchor, wipe_once_at AS wipeOnceAt,
|
||||
created_at AS createdAt, updated_at AS updatedAt
|
||||
FROM ${SERVERS}
|
||||
WHERE id = ?`,
|
||||
[id],
|
||||
@@ -71,6 +76,26 @@ async function upsertServer({ id, name, sidecarBaseUrl, sidecarTokenEnc, protoco
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write one server's wipe schedule (phase 16, D130), all six columns at once.
|
||||
*
|
||||
* Its own statement rather than six more columns on `upsertServer`, because the
|
||||
* schedule is written only when a save CARRIES one: a client that predates the
|
||||
* schedule and posts the rest of the row must not reset it to `none`.
|
||||
*
|
||||
* `wipeOnceAt` is a Date or null. The pool negotiates the session's zone
|
||||
* (`timezone: 'auto'` in core), so a Date written here reads back as the same
|
||||
* instant.
|
||||
*/
|
||||
async function setSchedule(id, { wipeRule, wipeDay, wipeTime, wipeTz, wipeAnchor, wipeOnceAt }) {
|
||||
await core.query(
|
||||
`UPDATE ${SERVERS}
|
||||
SET wipe_rule = ?, wipe_day = ?, wipe_time = ?, wipe_tz = ?, wipe_anchor = ?, wipe_once_at = ?
|
||||
WHERE id = ?`,
|
||||
[wipeRule, wipeDay, wipeTime, wipeTz, wipeAnchor, wipeOnceAt, id],
|
||||
)
|
||||
}
|
||||
|
||||
async function deleteServer(id) {
|
||||
await core.query(`DELETE FROM ${SERVERS} WHERE id = ?`, [id])
|
||||
}
|
||||
@@ -188,6 +213,7 @@ module.exports = {
|
||||
listServers,
|
||||
getServer,
|
||||
upsertServer,
|
||||
setSchedule,
|
||||
deleteServer,
|
||||
listState,
|
||||
getState,
|
||||
|
||||
Reference in New Issue
Block a user