auto embed cleanup

This commit is contained in:
Raven Scott
2026-08-13 14:02:09 -04:00
parent a42b58655e
commit 9a810cdf74
12 changed files with 627 additions and 34 deletions
@@ -42,11 +42,14 @@ var BARE_OS_DISCORD_KNOWN_UNITS = [
]
var BARE_OS_DISCORD_EDIT_CHUNK = 4000
var BARE_OS_DISCORD_EDIT_MAX_CHUNKS = 5
var BARE_OS_DISCORD_EDIT_TTL_MS = 15 * 60 * 1000
var BARE_OS_DISCORD_IDLE_MS = 2 * 60 * 1000
var BARE_OS_DISCORD_EDIT_TTL_MS = BARE_OS_DISCORD_IDLE_MS
var BARE_OS_DISCORD_EDIT_SESSIONS = Object.create(null)
var BARE_OS_DISCORD_FM_PAGE = 20
var BARE_OS_DISCORD_FM_TTL_MS = 20 * 60 * 1000
var BARE_OS_DISCORD_FM_TTL_MS = BARE_OS_DISCORD_IDLE_MS
var BARE_OS_DISCORD_FM_SESSIONS = Object.create(null)
var BARE_OS_DISCORD_LIVE = Object.create(null)
var BARE_OS_DISCORD_IDLE_SEQ = 0
var BARE_OS_DISCORD_EDIT_SUGGEST = [
'~/notes.txt',
'~/.barerc',
@@ -430,7 +433,7 @@ function discordEmbed(opts) {
const e = {
color: o.color == null ? BARE_OS_DISCORD_COLOR : o.color,
timestamp: new Date().toISOString(),
footer: { text: o.footer || 'Bare OS · session identity' }
footer: { text: o.footer || 'Bare OS · expires after 2m idle' }
}
if (o.title) e.title = String(o.title).slice(0, 256)
if (o.desc) e.description = discordCmdClip(discordCmdRedact(o.desc), 1800)
@@ -2195,7 +2198,7 @@ function discordCreatePicker(ctx) {
}
var BARE_OS_DISCORD_SET_SESSIONS = Object.create(null)
var BARE_OS_DISCORD_SET_TTL_MS = 20 * 60 * 1000
var BARE_OS_DISCORD_SET_TTL_MS = BARE_OS_DISCORD_IDLE_MS
var BARE_OS_DISCORD_THEME_FALLBACK = [
'catppuccin_mocha',
'default',
@@ -3381,6 +3384,163 @@ async function discordBeginCreate(ctx, interaction, rawPath) {
}
}
function discordIdleNow() {
return Date.now()
}
function discordIdleSchedule(fn, ms) {
const tfn = typeof setTimeout === 'function' ? setTimeout : globalThis.setTimeout
if (typeof tfn !== 'function') return null
const t = tfn(fn, ms)
if (t && typeof t.unref === 'function') t.unref()
return t
}
function discordIdleClearTimer(t) {
const cfn = typeof clearTimeout === 'function' ? clearTimeout : globalThis.clearTimeout
if (t != null && typeof cfn === 'function') cfn(t)
}
function discordIdleExpiredEmbeds(embeds) {
const out = []
if (!Array.isArray(embeds)) return out
for (let i = 0; i < embeds.length; i++) {
const e = embeds[i] || {}
const n = {}
for (const k in e) n[k] = e[k]
n.footer = { text: 'Expired after 2 minutes idle' }
n.color = BARE_OS_DISCORD_COLOR_WARN
out.push(n)
}
return out
}
function discordIdleForgetSessions(userId) {
if (!userId) return
for (const k in BARE_OS_DISCORD_LIVE) {
const r = BARE_OS_DISCORD_LIVE[k]
if (r && r.userId === userId) return
}
delete BARE_OS_DISCORD_EDIT_SESSIONS[userId]
delete BARE_OS_DISCORD_FM_SESSIONS[userId]
delete BARE_OS_DISCORD_SET_SESSIONS[userId]
}
function discordUnwrapMessage(sent) {
if (!sent || typeof sent !== 'object') return null
if (sent.resource && sent.resource.message) return sent.resource.message
if (sent.message && (sent.message.id || typeof sent.message.edit === 'function')) {
return sent.message
}
if (sent.id || typeof sent.delete === 'function' || typeof sent.edit === 'function') return sent
return null
}
async function discordResolvePostedMessage(interaction, sent) {
const unwrapped = discordUnwrapMessage(sent)
if (unwrapped) return unwrapped
if (interaction && interaction.message && (interaction.message.id || typeof interaction.message.delete === 'function')) {
return interaction.message
}
if (interaction && typeof interaction.fetchReply === 'function') {
try {
const m = await interaction.fetchReply()
return discordUnwrapMessage(m) || m
} catch {
return null
}
}
return null
}
function discordIdleArm(msg, payload, userId) {
if (!msg) return null
const hasUi =
(payload && payload.embeds && payload.embeds.length) ||
(payload && payload.components && payload.components.length)
if (!hasUi) return null
const id = String(msg.id || '') || 'anon:' + ++BARE_OS_DISCORD_IDLE_SEQ
const prev = BARE_OS_DISCORD_LIVE[id]
if (prev) discordIdleClearTimer(prev.timer)
const rec = {
id: id,
message: msg,
userId: userId || '',
embeds: payload && payload.embeds ? payload.embeds : null,
atMs: discordIdleNow(),
timer: null
}
BARE_OS_DISCORD_LIVE[id] = rec
rec.timer = discordIdleSchedule(function () {
Promise.resolve(discordIdleExpire(id)).catch(function () {})
}, BARE_OS_DISCORD_IDLE_MS)
return rec
}
async function discordIdleExpire(id) {
const rec = BARE_OS_DISCORD_LIVE[id]
if (!rec) return false
delete BARE_OS_DISCORD_LIVE[id]
discordIdleClearTimer(rec.timer)
rec.timer = null
const msg = rec.message
let cleaned = false
if (msg && typeof msg.delete === 'function') {
try {
await msg.delete()
cleaned = true
} catch {
cleaned = false
}
}
if (!cleaned && msg && typeof msg.edit === 'function') {
try {
const body = { components: [] }
if (rec.embeds && rec.embeds.length) body.embeds = discordIdleExpiredEmbeds(rec.embeds)
await msg.edit(body)
cleaned = true
} catch {
cleaned = false
}
}
discordIdleForgetSessions(rec.userId)
return cleaned
}
function discordIdleSweep() {
const now = discordIdleNow()
const due = []
for (const k in BARE_OS_DISCORD_LIVE) {
const r = BARE_OS_DISCORD_LIVE[k]
if (!r || now - r.atMs >= BARE_OS_DISCORD_IDLE_MS) due.push(k)
}
for (let i = 0; i < due.length; i++) {
Promise.resolve(discordIdleExpire(due[i])).catch(function () {})
}
}
function discordIdleTouch(interaction) {
const mid = interaction && interaction.message && interaction.message.id
if (!mid) return
const rec = BARE_OS_DISCORD_LIVE[String(mid)]
if (!rec) return
if (discordIdleNow() - rec.atMs >= BARE_OS_DISCORD_IDLE_MS) return
rec.atMs = discordIdleNow()
}
async function discordIdleWatch(interaction, payload, sent) {
if (!payload) return
if (
payload.flags === BARE_OS_DISCORD_FLAG_EPHEMERAL &&
!(payload.embeds && payload.embeds.length) &&
!(payload.components && payload.components.length)
) {
return
}
const msg = await discordResolvePostedMessage(interaction, sent)
discordIdleArm(msg, payload, discordInteractionUserId(interaction))
}
async function discordSendResult(ctx, interaction, result) {
if (result && result.editPath) {
const prev = discordEditGet(interaction)
@@ -3461,15 +3621,16 @@ async function discordSendResult(ctx, interaction, result) {
(typeof interaction.isButton === 'function' && interaction.isButton()) ||
(typeof interaction.isStringSelectMenu === 'function' &&
interaction.isStringSelectMenu())
let sent = null
try {
if (isComp && typeof interaction.update === 'function' && !interaction.replied && !interaction.deferred) {
await interaction.update(payload)
sent = await interaction.update(payload)
} else if (interaction.deferred && typeof interaction.editReply === 'function') {
await interaction.editReply(payload)
sent = await interaction.editReply(payload)
} else if (interaction.replied && typeof interaction.followUp === 'function') {
await interaction.followUp(payload)
sent = await interaction.followUp(Object.assign({ fetchReply: true }, payload))
} else {
await interaction.reply(payload)
sent = await interaction.reply(Object.assign({ fetchReply: true }, payload))
}
} catch (err) {
if (ctx && ctx.console && typeof ctx.console.error === 'function') {
@@ -3477,6 +3638,12 @@ async function discordSendResult(ctx, interaction, result) {
'discord-bot: reply failed: ' + ((err && err.message) || err)
)
}
return
}
try {
await discordIdleWatch(interaction, payload, sent)
} catch {
/* tracking must not break the bot */
}
}
@@ -4052,6 +4219,8 @@ async function discordDispatchModal(ctx, interaction) {
async function discordDispatchInteraction(ctx, interaction) {
if (!interaction) return false
discordSyncSessionIdentity(ctx)
discordIdleTouch(interaction)
discordIdleSweep()
if (typeof interaction.isAutocomplete === 'function' && interaction.isAutocomplete()) {
if (!discordUserAllowed(ctx, discordInteractionUserId(interaction))) {
try {
@@ -4151,7 +4320,14 @@ var bareOsDiscordCommands = {
settingsCurrent: discordSettingsCurrent,
uniqueComponents: discordUniqueComponents,
navRows: discordNavRows,
result: discordResult
result: discordResult,
idleMs: BARE_OS_DISCORD_IDLE_MS,
idleExpire: discordIdleExpire,
idleSweep: discordIdleSweep,
idleArm: discordIdleArm,
idleLive: function () {
return BARE_OS_DISCORD_LIVE
}
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = bareOsDiscordCommands