@@ -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). 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. Requesting that privileged intent without enabling it closes the gateway (4014) and used to hang after 'logging in'. Token comes from --token, guest DISCORD_TOKEN, a VFS .env file (--env, DISCORD_ENV_FILE, ~/.discord.env), or a host DISCORD_ENV_FILE / DISCORD_TOKEN copied into the session by the booter. Custom bots should use ctx.bare.discordJS.Client the same way (see examples/discord-ping-pong).",
|
||||
"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).",
|
||||
"options": [
|
||||
{
|
||||
"flag": "--env PATH",
|
||||
@@ -39,7 +39,8 @@
|
||||
"keywords": ["discord", "bot", "ping", "pong", "ctx.bare"],
|
||||
"environment": [
|
||||
"DISCORD_TOKEN — bot token in the guest session (also copied from the host when set).",
|
||||
"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.",
|
||||
"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_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.",
|
||||
@@ -57,11 +58,11 @@
|
||||
"examples": [
|
||||
{
|
||||
"caption": "check token resolution without connecting",
|
||||
"code": "discord-bot --check --env ~/.discord.env"
|
||||
"code": "discord-bot --check --env ~/.discord/.env"
|
||||
},
|
||||
{
|
||||
"caption": "run the ping-pong bot",
|
||||
"code": "discord-bot --env ~/.discord.env --guild 123456789012345678"
|
||||
"code": "discord-bot --env ~/.discord/.env --guild 123456789012345678"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -110,6 +110,49 @@ function discordErrText(err) {
|
||||
return (err && err.message) || String(err)
|
||||
}
|
||||
|
||||
function discordAttachInterrupt(ctx, onStop) {
|
||||
const proc = typeof globalThis.process !== 'undefined' ? globalThis.process : null
|
||||
if (!proc || typeof proc.on !== 'function') {
|
||||
return function () {}
|
||||
}
|
||||
let fired = false
|
||||
function fire() {
|
||||
if (fired) return
|
||||
fired = true
|
||||
ctx.exitCode = 130
|
||||
try {
|
||||
ctx.console.log('discord-bot: stopped (^C)')
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
try {
|
||||
onStop()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
proc.on('SIGINT', fire)
|
||||
proc.on('SIGTERM', fire)
|
||||
proc.on('bare-os:host-sigint', fire)
|
||||
return function detach() {
|
||||
try {
|
||||
proc.off('SIGINT', fire)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
try {
|
||||
proc.off('SIGTERM', fire)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
try {
|
||||
proc.off('bare-os:host-sigint', fire)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function discordUsage(argv0) {
|
||||
return (
|
||||
'usage: ' +
|
||||
@@ -118,7 +161,9 @@ function discordUsage(argv0) {
|
||||
' [--debug] [--message-content] [--login-timeout MS]\n' +
|
||||
'Ping-pong bot via ctx.bare.discordJS.\n' +
|
||||
'Token from --token, DISCORD_TOKEN, or a .env file (--env, DISCORD_ENV_FILE,\n' +
|
||||
'host DISCORD_ENV_FILE, ~/.discord.env).\n' +
|
||||
'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).'
|
||||
)
|
||||
@@ -151,7 +196,13 @@ async function discordLoadToken(ctx, argv) {
|
||||
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', './.env', '~/.env')
|
||||
envPaths.push(
|
||||
'~/.discord/.env',
|
||||
'~/.discord.env',
|
||||
'~/discord.env',
|
||||
'./.env',
|
||||
'~/.env'
|
||||
)
|
||||
|
||||
const seen = {}
|
||||
for (let i = 0; i < envPaths.length; i++) {
|
||||
@@ -401,6 +452,11 @@ async function run(ctx, argv) {
|
||||
let loggingIn = true
|
||||
let loginTimer = null
|
||||
let loginTick = null
|
||||
let interruptStop = null
|
||||
const detachInterrupt = discordAttachInterrupt(ctx, function () {
|
||||
if (typeof interruptStop === 'function') interruptStop()
|
||||
Promise.resolve(client.destroy()).catch(function () {})
|
||||
})
|
||||
|
||||
if (Events.Warn) {
|
||||
client.on(Events.Warn, function (message) {
|
||||
@@ -484,6 +540,11 @@ async function run(ctx, argv) {
|
||||
resolve('ready')
|
||||
})
|
||||
}),
|
||||
new Promise(function (resolve, reject) {
|
||||
interruptStop = function () {
|
||||
resolve('interrupt')
|
||||
}
|
||||
}),
|
||||
new Promise(function (_, reject) {
|
||||
let waited = 0
|
||||
loginTick = setInterval(function () {
|
||||
@@ -539,19 +600,27 @@ async function run(ctx, argv) {
|
||||
loggingIn = false
|
||||
if (loginTimer) clearTimeout(loginTimer)
|
||||
if (loginTick) clearInterval(loginTick)
|
||||
detachInterrupt()
|
||||
await Promise.resolve(client.destroy()).catch(function () {})
|
||||
return
|
||||
}
|
||||
loggingIn = false
|
||||
if (loginTimer) clearTimeout(loginTimer)
|
||||
if (loginTick) clearInterval(loginTick)
|
||||
if (ctx.exitCode === 130) {
|
||||
detachInterrupt()
|
||||
await Promise.resolve(client.destroy()).catch(function () {})
|
||||
return
|
||||
}
|
||||
|
||||
await new Promise(function (resolve) {
|
||||
function shutdown() {
|
||||
detachInterrupt()
|
||||
Promise.resolve(client.destroy())
|
||||
.catch(function () {})
|
||||
.then(resolve)
|
||||
}
|
||||
interruptStop = shutdown
|
||||
if (typeof ctx.registerKernelShutdownHook === 'function') {
|
||||
ctx.registerKernelShutdownHook(shutdown)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user