Updates
Release rolling / release (push) Failing after 5m54s

This commit is contained in:
Raven Scott
2026-08-13 11:57:15 -04:00
parent 232a75eca2
commit 2d0563984e
36 changed files with 3312 additions and 77 deletions
+7 -1
View File
@@ -23,6 +23,7 @@ const preamble = {
awk: ['awk-engine.js'],
jq: ['jq-engine.js'],
man: ['man-render.js'],
'discord-bot': ['bare-os-discord-commands-guest.js'],
ls: ['bare-os-lscolors.js', 'ls-colors.js'],
dircolors: ['bare-os-lscolors.js'],
edit: [
@@ -199,7 +200,12 @@ export async function build() {
const chunkPath =
f === 'bare-os-lscolors.js'
? join(repoRoot, 'packages/bare-os-lscolors/bare-os-lscolors.js')
: join(__dirname, 'lib', f)
: f === 'bare-os-discord-commands-guest.js'
? join(
repoRoot,
'packages/bare-os-booter/lib/bare-os-discord-commands-guest.js'
)
: join(__dirname, 'lib', f)
let chunk = await readFile(chunkPath, 'utf8')
if (f === 'bare-os-lscolors.js')
chunk = stripLscolorsBundleExport(chunk)
@@ -5,7 +5,7 @@
"synopsis": [
"discord-bot [--env PATH] [--token TOKEN] [--guild ID] [--check] [--debug] [--message-content] [--login-timeout MS]"
],
"description": "Foreground Discord ping-pong bot using ctx.bare.discordJS (vendored bare-discord-js / official discord.js). Ctrl+C (or host SIGINT) stops the process. Replies pong to the /ping slash command. Channel message ping/pong is opt-in: pass --message-content (or DISCORD_MESSAGE_CONTENT=1) and enable MESSAGE CONTENT INTENT in the Discord Developer Portal. Token comes from --token, guest DISCORD_TOKEN, or a VFS .env file (--env, DISCORD_ENV_FILE, ~/.discord/.env, ~/.discord.env). The initd unit bare-os-discord is registered and listed by systemctl only when ~/.discord/.env exists with DISCORD_TOKEN=. Disable the unit with BARE_OS_DISCORD_INITD=0. Custom bots should use ctx.bare.discordJS.Client the same way (see examples/discord-ping-pong).",
"description": "Bare OS Discord bot via ctx.bare.discordJS. Foreground Ctrl+C stops the process. Slash commands: /bare (status, uname, whoami, …), /sys (df, mem, ps, env, doctor), /svc (systemctl), /fs (ls/cat/stat), /net (swarm), /man, /say, /run (allowlisted utilities), /journal, /ping. Channel message ping still replies pong when Message Content Intent is enabled. Token from --token, DISCORD_TOKEN, or ~/.discord/.env. Optional DISCORD_ID_WHITELIST=id,id in that .env restricts slash commands and channel ping replies to those Discord user ids (anyone else is denied). The initd unit bare-os-discord is listed by systemctl only when ~/.discord/.env exists. Disable with BARE_OS_DISCORD_INITD=0.",
"options": [
{
"flag": "--env PATH",
@@ -42,6 +42,7 @@
"DISCORD_ENV_FILE / BARE_OS_DISCORD_ENV_FILE — path to a .env file. On the host this may be a host filesystem path (booter reads it). In the guest it is a VFS path. Preferred guest path: ~/.discord/.env.",
"BARE_OS_DISCORD_INITD — set 0 / false to never register the bare-os-discord systemctl unit, even when ~/.discord/.env exists.",
"DISCORD_GUILD_ID — optional guild for slash-command registration.",
"DISCORD_ID_WHITELIST — comma-separated Discord user ids allowed to use the bot. Unset or empty allows everyone. A user not on a non-empty list is denied (ephemeral for slash commands).",
"DISCORD_MESSAGE_CONTENT / BARE_OS_DISCORD_MESSAGE_CONTENT — set 1 to request Message Content Intent (same as --message-content).",
"DISCORD_DEBUG / BARE_OS_DISCORD_DEBUG — set 1 to print discord.js debug lines.",
"DISCORD_LOGIN_TIMEOUT_MS — login deadline in milliseconds (default 45000).",
@@ -52,6 +53,10 @@
{
"name": "bare-os-ctx-bare",
"section": 7
},
{
"name": "devguide-21-discord-bots",
"section": 7
}
],
"bareOsNotes": "Requires a Bare/Pear host so vendored bare-discord-js can remap Node builtins. Guest scripts have no import/require; use ctx.bare.discordJS.",
+121 -16
View File
@@ -1,6 +1,7 @@
/**
* Stock Discord ping-pong bot via ctx.bare.discordJS (vendored bare-discord-js).
* Token: --token, DISCORD_TOKEN, --env PATH, DISCORD_ENV_FILE, ~/.discord.env.
* Optional DISCORD_ID_WHITELIST=id,id in .env restricts who may use the bot.
*/
function discordNormalizeToken(raw) {
@@ -164,8 +165,9 @@ function discordUsage(argv0) {
'host DISCORD_ENV_FILE, ~/.discord/.env, ~/.discord.env).\n' +
'Ctrl+C stops the foreground bot. Initd unit bare-os-discord appears in\n' +
'systemctl only when ~/.discord/.env exists with DISCORD_TOKEN=.\n' +
'Replies pong to /ping. Message "ping" needs --message-content and the\n' +
'Message Content Intent in the Developer Portal (privileged).'
'Slash commands: /bare /sys /svc /fs /net /man /say /run /journal /ping.\n' +
'Channel "ping" still replies pong when Message Content Intent is enabled.\n' +
'DISCORD_ID_WHITELIST=id,id in .env restricts the bot to those Discord user ids.'
)
}
@@ -214,15 +216,54 @@ async function discordLoadToken(ctx, argv) {
const parsed = discordParseDotEnvText(text)
const token = discordNormalizeToken(parsed.DISCORD_TOKEN || '')
if (token) {
if (parsed.DISCORD_GUILD_ID && ctx.env && !ctx.env.DISCORD_GUILD_ID) {
ctx.env.DISCORD_GUILD_ID = String(parsed.DISCORD_GUILD_ID).trim()
}
discordApplyDotEnvExtras(ctx, parsed)
return { token: token, source: p }
}
}
return { token: '', source: '' }
}
function discordApplyDotEnvExtras(ctx, parsed) {
if (!parsed || !ctx) return
if (!ctx.env) ctx.env = {}
const extras = ['DISCORD_GUILD_ID', 'DISCORD_ID_WHITELIST']
for (let i = 0; i < extras.length; i++) {
const k = extras[i]
const v = parsed[k]
if (v == null || String(v).trim() === '') continue
if (!ctx.env[k] || String(ctx.env[k]).trim() === '') {
ctx.env[k] = String(v).trim()
}
}
}
async function discordHydrateEnvFromFiles(ctx, argv) {
const envPaths = []
const flagPath = discordArgValue(argv, ['--env', '--env-file'])
if (flagPath) envPaths.push(flagPath)
if (ctx.env && ctx.env.DISCORD_ENV_FILE)
envPaths.push(ctx.env.DISCORD_ENV_FILE)
if (ctx.env && ctx.env.BARE_OS_DISCORD_ENV_FILE) {
envPaths.push(ctx.env.BARE_OS_DISCORD_ENV_FILE)
}
envPaths.push(
'~/.discord/.env',
'~/.discord.env',
'~/discord.env',
'./.env',
'~/.env'
)
const seen = {}
for (let i = 0; i < envPaths.length; i++) {
const p = String(envPaths[i] || '').trim()
if (!p || seen[p]) continue
seen[p] = true
const text = await discordReadVfsText(ctx, p)
if (!text) continue
discordApplyDotEnvExtras(ctx, discordParseDotEnvText(text))
}
}
async function run(ctx, argv) {
if (discordHasFlag(argv, ['-h', '--help'])) {
ctx.console.log(discordUsage(argv[0]))
@@ -256,6 +297,7 @@ async function run(ctx, argv) {
}
const loaded = await discordLoadToken(ctx, argv)
await discordHydrateEnvFromFiles(ctx, argv)
const checkOnly =
discordHasFlag(argv, ['--check', '--dry-run']) ||
(argv[1] && String(argv[1]) === 'check')
@@ -319,10 +361,15 @@ async function run(ctx, argv) {
}
}
})
const pingCommand = new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with pong.')
.toJSON()
const slashBody =
typeof discordBuildSlashCommands === 'function'
? discordBuildSlashCommands(dj)
: [
new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with pong.')
.toJSON()
]
client.once(Events.ClientReady, async function (readyClient) {
ctx.console.log('Logged in as ' + readyClient.user.tag)
@@ -332,15 +379,19 @@ async function run(ctx, argv) {
const appId = readyClient.user.id
if (guildId) {
await rest.put(Routes.applicationGuildCommands(appId, guildId), {
body: [pingCommand]
})
ctx.console.log('Registered /ping for guild ' + guildId)
} else {
await rest.put(Routes.applicationCommands(appId), {
body: [pingCommand]
body: slashBody
})
ctx.console.log(
'Registered global /ping (may take up to ~1 hour). Set DISCORD_GUILD_ID or --guild for instant updates.'
'Registered ' + slashBody.length + ' slash commands for guild ' + guildId
)
} else {
await rest.put(Routes.applicationCommands(appId), {
body: slashBody
})
ctx.console.log(
'Registered ' +
slashBody.length +
' global slash commands (may take up to ~1 hour). Set DISCORD_GUILD_ID or --guild for instant updates.'
)
}
} catch (err) {
@@ -352,9 +403,32 @@ async function run(ctx, argv) {
})
client.on(Events.InteractionCreate, async function (interaction) {
if (typeof discordDispatchInteraction === 'function') {
await discordDispatchInteraction(ctx, interaction)
return
}
if (!interaction.isChatInputCommand || !interaction.isChatInputCommand())
return
if (interaction.commandName !== 'ping') return
const uid =
interaction.user && interaction.user.id ? interaction.user.id : ''
if (typeof discordUserAllowed === 'function' && !discordUserAllowed(ctx, uid)) {
try {
await interaction.reply({
content:
typeof BARE_OS_DISCORD_WHITELIST_DENY === 'string'
? BARE_OS_DISCORD_WHITELIST_DENY
: 'Access denied. Your Discord user id is not on DISCORD_ID_WHITELIST.',
ephemeral: true
})
} catch (err) {
ctx.console.error(
'discord-bot: deny reply failed: ' +
((err && err.message) || String(err))
)
}
return
}
try {
await interaction.reply({ content: 'pong' })
} catch (err) {
@@ -372,6 +446,21 @@ async function run(ctx, argv) {
.trim()
.toLowerCase()
if (text !== 'ping') return
const uid = message.author && message.author.id ? message.author.id : ''
if (typeof discordUserAllowed === 'function' && !discordUserAllowed(ctx, uid)) {
try {
await message.reply(
typeof BARE_OS_DISCORD_WHITELIST_DENY === 'string'
? BARE_OS_DISCORD_WHITELIST_DENY
: 'Access denied. Your Discord user id is not on DISCORD_ID_WHITELIST.'
)
} catch (err) {
ctx.console.error(
'discord-bot: deny reply failed: ' + discordErrText(err)
)
}
return
}
try {
await message.reply('pong')
} catch (err) {
@@ -475,6 +564,22 @@ async function run(ctx, argv) {
ctx.console.log(
'discord-bot: logging in (token from ' + loaded.source + ')...'
)
if (typeof discordParseIdWhitelist === 'function') {
const wl = discordParseIdWhitelist(
(ctx.env &&
(ctx.env.DISCORD_ID_WHITELIST || ctx.env.BARE_OS_DISCORD_ID_WHITELIST)) ||
''
)
let wlN = 0
for (const k in wl) {
if (Object.prototype.hasOwnProperty.call(wl, k)) wlN++
}
if (wlN) {
ctx.console.log(
'discord-bot: DISCORD_ID_WHITELIST active (' + wlN + ' user id(s))'
)
}
}
if (!wantsMessageContent) {
ctx.console.log(
'discord-bot: /ping only (pass --message-content after enabling Message Content Intent for channel ping/pong)'