@@ -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)'
|
||||
|
||||
Reference in New Issue
Block a user