Change tab size
This commit is contained in:
parent
7912b455e9
commit
ece60db183
116
src/constants.js
116
src/constants.js
@ -1,65 +1,65 @@
|
||||
export default {
|
||||
rollRegex: /^(\d+)?([dhlfb])(\d+)(\s*([+\-*x\/])\s*(\d+))?/,
|
||||
optionRollRegex: /^(\d+)?(([dhlfb])(\d+))?(\s*([+\-*x\/])\s*(\d+))?/,
|
||||
descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g,
|
||||
macroNameRegex: /^[a-z0-9]+$/,
|
||||
rollRegex: /^(\d+)?([dhlfb])(\d+)(\s*([+\-*x\/])\s*(\d+))?/,
|
||||
optionRollRegex: /^(\d+)?(([dhlfb])(\d+))?(\s*([+\-*x\/])\s*(\d+))?/,
|
||||
descriptionRegex: /\s*((\d*-\d*)|(\d+))?([^;\n]+)/g,
|
||||
macroNameRegex: /^[a-z0-9]+$/,
|
||||
|
||||
commands: {
|
||||
about: {
|
||||
name: 'about',
|
||||
description: "Get information about dicedicedice"
|
||||
},
|
||||
commands: {
|
||||
about: {
|
||||
name: 'about',
|
||||
description: "Get information about dicedicedice"
|
||||
},
|
||||
|
||||
macro: {
|
||||
name: 'macro',
|
||||
description: "Manage macros",
|
||||
'dm_permission': false,
|
||||
options: [
|
||||
{
|
||||
name: 'add',
|
||||
description: "Define a dice macro",
|
||||
type: 1, // Sub command
|
||||
options: [
|
||||
{
|
||||
name: "name",
|
||||
description: "Name of the macro",
|
||||
type: 3, // String
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "dice",
|
||||
description: "The dice expression to save as a macro",
|
||||
type: 3, // String
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'remove',
|
||||
description: "Remove a macro",
|
||||
type: 1, // Sub command
|
||||
options: [
|
||||
{
|
||||
name: "name",
|
||||
description: "Name of the macro",
|
||||
type: 3, // String
|
||||
required: true,
|
||||
autocomplete: true,
|
||||
getAutocomplete: interaction => {
|
||||
let macros = globalThis.macroCache.get(interaction.guild.id)
|
||||
|
||||
return macros ? Object.keys(macros) : []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
macro: {
|
||||
name: 'macro',
|
||||
description: "Manage macros",
|
||||
'dm_permission': false,
|
||||
options: [
|
||||
{
|
||||
name: 'add',
|
||||
description: "Define a dice macro",
|
||||
type: 1, // Sub command
|
||||
options: [
|
||||
{
|
||||
name: "name",
|
||||
description: "Name of the macro",
|
||||
type: 3, // String
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "dice",
|
||||
description: "The dice expression to save as a macro",
|
||||
type: 3, // String
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'remove',
|
||||
description: "Remove a macro",
|
||||
type: 1, // Sub command
|
||||
options: [
|
||||
{
|
||||
name: "name",
|
||||
description: "Name of the macro",
|
||||
type: 3, // String
|
||||
required: true,
|
||||
autocomplete: true,
|
||||
getAutocomplete: interaction => {
|
||||
let macros = globalThis.macroCache.get(interaction.guild.id)
|
||||
|
||||
return macros ? Object.keys(macros) : []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
iconUrl: 'https://github.com/Dakedres/dicedicedice/raw/main/assets/eater-transparent.png',
|
||||
iconUrl: 'https://github.com/Dakedres/dicedicedice/raw/main/assets/eater-transparent.png',
|
||||
|
||||
errorMessage: error => `\
|
||||
errorMessage: error => `\
|
||||
Something went wrong trying to execute that command.
|
||||
\`\`\`fix
|
||||
${error.toString()}
|
||||
@ -67,7 +67,7 @@ ${error.toString()}
|
||||
If this issue persists please report it here: <https://github.com/Dakedres/dicedicedice/issues>\
|
||||
`,
|
||||
|
||||
aboutMessage: (guildCount) => `\
|
||||
aboutMessage: (guildCount) => `\
|
||||
A discord bot for metaphorically "rolling dice"/generating random values. Made for use with Weaverdice systems.
|
||||
|
||||
Present in ~${guildCount} guilds!
|
||||
|
698
src/index.js
698
src/index.js
@ -11,508 +11,508 @@ globalThis.macroCache = new Map()
|
||||
const db = new ClassicLevel('./db')
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent,
|
||||
GatewayIntentBits.DirectMessages
|
||||
],
|
||||
partials: [
|
||||
Partials.Channel
|
||||
]
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent,
|
||||
GatewayIntentBits.DirectMessages
|
||||
],
|
||||
partials: [
|
||||
Partials.Channel
|
||||
]
|
||||
})
|
||||
const rest = new REST().setToken(process.env.DISCORD_TOKEN)
|
||||
|
||||
const ParseRollInt = (value, defaultValue) =>
|
||||
value ? parseInt(value) : defaultValue
|
||||
value ? parseInt(value) : defaultValue
|
||||
|
||||
const ParseOptionRoll = expression => {
|
||||
let match = constants.optionRollRegex.exec(expression.trim())
|
||||
let match = constants.optionRollRegex.exec(expression.trim())
|
||||
|
||||
let [
|
||||
count,
|
||||
modeSize,
|
||||
mode,
|
||||
size,
|
||||
operationModifier,
|
||||
operation,
|
||||
modifier
|
||||
] = match
|
||||
.slice(1)
|
||||
|
||||
return {
|
||||
count: ParseRollInt(count),
|
||||
mode,
|
||||
size: ParseRollInt(size),
|
||||
operation,
|
||||
modifier: ParseRollInt(modifier),
|
||||
descriptionConditions: PullDescription(expression, match)
|
||||
}
|
||||
let [
|
||||
count,
|
||||
modeSize,
|
||||
mode,
|
||||
size,
|
||||
operationModifier,
|
||||
operation,
|
||||
modifier
|
||||
] = match
|
||||
.slice(1)
|
||||
|
||||
return {
|
||||
count: ParseRollInt(count),
|
||||
mode,
|
||||
size: ParseRollInt(size),
|
||||
operation,
|
||||
modifier: ParseRollInt(modifier),
|
||||
descriptionConditions: PullDescription(expression, match)
|
||||
}
|
||||
}
|
||||
|
||||
const ParseRoll = expression => {
|
||||
let match = constants.rollRegex.exec(expression.trim())
|
||||
let match = constants.rollRegex.exec(expression.trim())
|
||||
|
||||
if(match == null)
|
||||
return
|
||||
if(match == null)
|
||||
return
|
||||
|
||||
let [
|
||||
count,
|
||||
mode,
|
||||
size,
|
||||
modifierString,
|
||||
operation,
|
||||
modifier
|
||||
] = match.slice(1)
|
||||
let [
|
||||
count,
|
||||
mode,
|
||||
size,
|
||||
modifierString,
|
||||
operation,
|
||||
modifier
|
||||
] = match.slice(1)
|
||||
|
||||
return {
|
||||
count: ParseRollInt(count, 1),
|
||||
mode,
|
||||
size: ParseRollInt(size),
|
||||
operation,
|
||||
modifier: ParseRollInt(modifier),
|
||||
descriptionConditions: PullDescription(expression, match)
|
||||
}
|
||||
return {
|
||||
count: ParseRollInt(count, 1),
|
||||
mode,
|
||||
size: ParseRollInt(size),
|
||||
operation,
|
||||
modifier: ParseRollInt(modifier),
|
||||
descriptionConditions: PullDescription(expression, match)
|
||||
}
|
||||
}
|
||||
|
||||
const PullDescription = (expression, match) => {
|
||||
if(match[0].length == expression.length)
|
||||
return
|
||||
if(match[0].length == expression.length)
|
||||
return
|
||||
|
||||
return ParseDescription(expression.slice(match[0].length))
|
||||
return ParseDescription(expression.slice(match[0].length))
|
||||
}
|
||||
|
||||
const ParseDescription = description => {
|
||||
let conditions = []
|
||||
let match
|
||||
let conditions = []
|
||||
let match
|
||||
|
||||
while((match = constants.descriptionRegex.exec(description)) !== null) {
|
||||
let range
|
||||
let [
|
||||
rangeExp,
|
||||
valueExp,
|
||||
content
|
||||
] = match.slice(2)
|
||||
while((match = constants.descriptionRegex.exec(description)) !== null) {
|
||||
let range
|
||||
let [
|
||||
rangeExp,
|
||||
valueExp,
|
||||
content
|
||||
] = match.slice(2)
|
||||
|
||||
if(rangeExp) {
|
||||
let split = rangeExp.split('-')
|
||||
if(rangeExp) {
|
||||
let split = rangeExp.split('-')
|
||||
|
||||
range = {
|
||||
lower: ParseRollInt(split[0], -Infinity),
|
||||
upper: ParseRollInt(split[1], Infinity)
|
||||
}
|
||||
} else if(valueExp) {
|
||||
range = {
|
||||
upper: valueExp,
|
||||
lower: valueExp
|
||||
}
|
||||
}
|
||||
range = {
|
||||
lower: ParseRollInt(split[0], -Infinity),
|
||||
upper: ParseRollInt(split[1], Infinity)
|
||||
}
|
||||
} else if(valueExp) {
|
||||
range = {
|
||||
upper: valueExp,
|
||||
lower: valueExp
|
||||
}
|
||||
}
|
||||
|
||||
conditions.push({
|
||||
range,
|
||||
content: content.trim()
|
||||
})
|
||||
}
|
||||
conditions.push({
|
||||
range,
|
||||
content: content.trim()
|
||||
})
|
||||
}
|
||||
|
||||
return conditions
|
||||
return conditions
|
||||
}
|
||||
|
||||
const OnMessage = (message, respond) => {
|
||||
let dice = ParseRoll(message.content)
|
||||
let dice = ParseRoll(message.content)
|
||||
|
||||
if(dice == undefined)
|
||||
return // No dice
|
||||
if(dice == undefined)
|
||||
return // No dice
|
||||
|
||||
RollDice(dice, respond)
|
||||
RollDice(dice, respond)
|
||||
}
|
||||
|
||||
const RollDice = (dice, respond) => {
|
||||
if(dice.size > 255) {
|
||||
respond('That die is way too big... .-.')
|
||||
return
|
||||
} else if(dice.size < 2) {
|
||||
respond('I cannot even fathom a die with that geometry ;-;')
|
||||
return
|
||||
}
|
||||
if(dice.size > 255) {
|
||||
respond('That die is way too big... .-.')
|
||||
return
|
||||
} else if(dice.size < 2) {
|
||||
respond('I cannot even fathom a die with that geometry ;-;')
|
||||
return
|
||||
}
|
||||
|
||||
if(dice.count > 100) {
|
||||
respond('I don\'t have that many dice O_O')
|
||||
return
|
||||
}
|
||||
if(dice.count > 100) {
|
||||
respond('I don\'t have that many dice O_O')
|
||||
return
|
||||
}
|
||||
|
||||
let rolls = [ ...crypto.getRandomValues(new Uint8Array(dice.count) ) ]
|
||||
.map(n => Math.ceil((n / 256) * dice.size))
|
||||
let result = 0
|
||||
let operationSymbol = dice.operation
|
||||
let response = ''
|
||||
let rolls = [ ...crypto.getRandomValues(new Uint8Array(dice.count) ) ]
|
||||
.map(n => Math.ceil((n / 256) * dice.size))
|
||||
let result = 0
|
||||
let operationSymbol = dice.operation
|
||||
let response = ''
|
||||
|
||||
switch(dice.mode.toLowerCase()) {
|
||||
case 'd':
|
||||
result = rolls.reduce((a, v) => a + v, 0)
|
||||
break
|
||||
switch(dice.mode.toLowerCase()) {
|
||||
case 'd':
|
||||
result = rolls.reduce((a, v) => a + v, 0)
|
||||
break
|
||||
|
||||
case 'h':
|
||||
result = rolls.reduce((a, v) => v > a ? v : a, 0)
|
||||
break
|
||||
case 'h':
|
||||
result = rolls.reduce((a, v) => v > a ? v : a, 0)
|
||||
break
|
||||
|
||||
case 'l':
|
||||
result = rolls.reduce((a, v) => v < a ? v : a, Infinity)
|
||||
break
|
||||
case 'l':
|
||||
result = rolls.reduce((a, v) => v < a ? v : a, Infinity)
|
||||
break
|
||||
|
||||
case 'f':
|
||||
let pseudoMedian = Math.floor(dice.size / 2)
|
||||
let resultDistance = -1
|
||||
case 'f':
|
||||
let pseudoMedian = Math.floor(dice.size / 2)
|
||||
let resultDistance = -1
|
||||
|
||||
for(let roll of rolls) {
|
||||
let distance = Math.abs(roll - pseudoMedian)
|
||||
for(let roll of rolls) {
|
||||
let distance = Math.abs(roll - pseudoMedian)
|
||||
|
||||
if(distance > resultDistance) {
|
||||
result = roll
|
||||
resultDistance = distance
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
if(distance > resultDistance) {
|
||||
result = roll
|
||||
resultDistance = distance
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
switch(dice.operation) {
|
||||
case '+':
|
||||
result += dice.modifier
|
||||
break
|
||||
switch(dice.operation) {
|
||||
case '+':
|
||||
result += dice.modifier
|
||||
break
|
||||
|
||||
case '-':
|
||||
result -= dice.modifier
|
||||
break
|
||||
case '-':
|
||||
result -= dice.modifier
|
||||
break
|
||||
|
||||
case 'x':
|
||||
operationSymbol = '*'
|
||||
case '*':
|
||||
result = result * dice.modifier
|
||||
break
|
||||
case 'x':
|
||||
operationSymbol = '*'
|
||||
case '*':
|
||||
result = result * dice.modifier
|
||||
break
|
||||
|
||||
case '/':
|
||||
result = result / dice.modifier
|
||||
break
|
||||
}
|
||||
case '/':
|
||||
result = result / dice.modifier
|
||||
break
|
||||
}
|
||||
|
||||
if(dice.descriptionConditions) {
|
||||
for(let { range, content } of dice.descriptionConditions) {
|
||||
if(!range || result >= range.lower && result <= range.upper)
|
||||
response += `'${content}', `
|
||||
}
|
||||
}
|
||||
if(dice.descriptionConditions) {
|
||||
for(let { range, content } of dice.descriptionConditions) {
|
||||
if(!range || result >= range.lower && result <= range.upper)
|
||||
response += `'${content}', `
|
||||
}
|
||||
}
|
||||
|
||||
response += `\` ${result} \` \u27F5 [${rolls.join(', ')}] ${dice.count + dice.mode + dice.size}`
|
||||
response += `\` ${result} \` \u27F5 [${rolls.join(', ')}] ${dice.count + dice.mode + dice.size}`
|
||||
|
||||
if(dice.operation) {
|
||||
response += ' ' + operationSymbol + ' ' + dice.modifier
|
||||
}
|
||||
if(dice.operation) {
|
||||
response += ' ' + operationSymbol + ' ' + dice.modifier
|
||||
}
|
||||
|
||||
respond(response)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
const SaveReply = (message, reply) => {
|
||||
globalThis.replies.set(message.id, {
|
||||
id: reply.id,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
globalThis.replies.set(message.id, {
|
||||
id: reply.id,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
}
|
||||
|
||||
const MessageCycle = async message => {
|
||||
OnMessage(message, async content => {
|
||||
SaveReply(message, await message.reply(content) )
|
||||
})
|
||||
OnMessage(message, async content => {
|
||||
SaveReply(message, await message.reply(content) )
|
||||
})
|
||||
}
|
||||
|
||||
const RehandleMessage = async (message, reply) => {
|
||||
OnMessage(message, async content => {
|
||||
SaveReply(message, await reply.edit(content) )
|
||||
})
|
||||
OnMessage(message, async content => {
|
||||
SaveReply(message, await reply.edit(content) )
|
||||
})
|
||||
}
|
||||
|
||||
const PruneReplies = () => {
|
||||
for(let [ id, entry ] of globalThis.replies.entries()) {
|
||||
let age = Date.now() - entry.timestamp
|
||||
for(let [ id, entry ] of globalThis.replies.entries()) {
|
||||
let age = Date.now() - entry.timestamp
|
||||
|
||||
if(age > 1000 * 60 * 3) {
|
||||
globalThis.replies.delete(id)
|
||||
}
|
||||
}
|
||||
if(age > 1000 * 60 * 3) {
|
||||
globalThis.replies.delete(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const InteractionRespond = (interaction, content) => {
|
||||
let reply = { content, ephemeral: true }
|
||||
let reply = { content, ephemeral: true }
|
||||
|
||||
if(interaction.replied || interaction.deferred) {
|
||||
return interaction.followUp(reply)
|
||||
} else {
|
||||
return interaction.reply(reply)
|
||||
}
|
||||
if(interaction.replied || interaction.deferred) {
|
||||
return interaction.followUp(reply)
|
||||
} else {
|
||||
return interaction.reply(reply)
|
||||
}
|
||||
}
|
||||
|
||||
const ErrorHandler = (interaction) => (error) =>
|
||||
InteractionRespond(interaction, constants.errorMessage(error) )
|
||||
.catch(reportingError => console.error('Could not display error message:\n ', reportingError) )
|
||||
InteractionRespond(interaction, constants.errorMessage(error) )
|
||||
.catch(reportingError => console.error('Could not display error message:\n ', reportingError) )
|
||||
|
||||
const Command = (data, callback) => {
|
||||
globalThis.commands.set(data.name, {
|
||||
data,
|
||||
execute: callback
|
||||
})
|
||||
globalThis.commands.set(data.name, {
|
||||
data,
|
||||
execute: callback
|
||||
})
|
||||
}
|
||||
|
||||
const Subcommands = (data, subcommandCallbacks) =>
|
||||
Command(data, interaction => {
|
||||
return subcommandCallbacks[interaction.options.getSubcommand()](interaction)
|
||||
})
|
||||
Command(data, interaction => {
|
||||
return subcommandCallbacks[interaction.options.getSubcommand()](interaction)
|
||||
})
|
||||
|
||||
const OpenMacros = guildId =>
|
||||
db.sublevel(guildId).sublevel('macros')
|
||||
db.sublevel(guildId).sublevel('macros')
|
||||
|
||||
const ReloadMacros = async guildId => {
|
||||
let commands = []
|
||||
let macros = OpenMacros(guildId)
|
||||
let cacheEntry = {}
|
||||
let commands = []
|
||||
let macros = OpenMacros(guildId)
|
||||
let cacheEntry = {}
|
||||
|
||||
for await (let [ name, dice ] of macros.iterator() ) {
|
||||
cacheEntry[name] = dice
|
||||
for await (let [ name, dice ] of macros.iterator() ) {
|
||||
cacheEntry[name] = dice
|
||||
|
||||
commands.push({
|
||||
name,
|
||||
description: Elipsify("Roll " + dice.replaceAll('\n', ';'), 100),
|
||||
options: [
|
||||
{
|
||||
name: "options",
|
||||
description: "Dice, modifiers, or descriptions to apply over the macro",
|
||||
type: 3
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
commands.push({
|
||||
name,
|
||||
description: Elipsify("Roll " + dice.replaceAll('\n', ';'), 100),
|
||||
options: [
|
||||
{
|
||||
name: "options",
|
||||
description: "Dice, modifiers, or descriptions to apply over the macro",
|
||||
type: 3
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
globalThis.macroCache.set(guildId, cacheEntry)
|
||||
globalThis.macroCache.set(guildId, cacheEntry)
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(process.env.DISCORD_ID, guildId),
|
||||
{ body: globalThis.commands }
|
||||
)
|
||||
.catch(err => console.error('Failed to reload macros:', err) )
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(process.env.DISCORD_ID, guildId),
|
||||
{ body: globalThis.commands }
|
||||
)
|
||||
.catch(err => console.error('Failed to reload macros:', err) )
|
||||
}
|
||||
|
||||
const Elipsify = (string, maxLength) =>
|
||||
string.length > maxLength ? string.slice(0, maxLength - 3) + '...' : string
|
||||
string.length > maxLength ? string.slice(0, maxLength - 3) + '...' : string
|
||||
|
||||
const PruneDB = async () => {
|
||||
let validIds = []
|
||||
let validIds = []
|
||||
|
||||
for await(let key of db.keys()) {
|
||||
let [ guildId ] = key.split('!').slice(1)
|
||||
for await(let key of db.keys()) {
|
||||
let [ guildId ] = key.split('!').slice(1)
|
||||
|
||||
if(validIds.includes(guildId))
|
||||
continue
|
||||
if(validIds.includes(guildId))
|
||||
continue
|
||||
|
||||
if(client.guilds.cache.has(guildId)) {
|
||||
validIds.push(guildId)
|
||||
} else {
|
||||
await db.del(key)
|
||||
}
|
||||
}
|
||||
if(client.guilds.cache.has(guildId)) {
|
||||
validIds.push(guildId)
|
||||
} else {
|
||||
await db.del(key)
|
||||
}
|
||||
}
|
||||
|
||||
return validIds
|
||||
return validIds
|
||||
}
|
||||
|
||||
const Responses = (interaction, ephemeral) => async content =>
|
||||
interaction.reply({ content, ephemeral })
|
||||
interaction.reply({ content, ephemeral })
|
||||
|
||||
const Subscribe = (event, callback) => {
|
||||
client.on(event, (...args) => {
|
||||
return callback(...args)
|
||||
.catch(err => console.error(err))
|
||||
})
|
||||
client.on(event, (...args) => {
|
||||
return callback(...args)
|
||||
.catch(err => console.error(err))
|
||||
})
|
||||
}
|
||||
|
||||
const HandleCommand = async interaction => {
|
||||
if(globalThis.commands.has(interaction.commandName) ) {
|
||||
globalThis.commands.get(interaction.commandName).execute(interaction)
|
||||
.catch(ErrorHandler(interaction))
|
||||
return
|
||||
}
|
||||
if(globalThis.commands.has(interaction.commandName) ) {
|
||||
globalThis.commands.get(interaction.commandName).execute(interaction)
|
||||
.catch(ErrorHandler(interaction))
|
||||
return
|
||||
}
|
||||
|
||||
await interaction.deferReply()
|
||||
let roll = globalThis.macroCache.get(interaction.guild.id)[interaction.commandName]
|
||||
await interaction.deferReply()
|
||||
let roll = globalThis.macroCache.get(interaction.guild.id)[interaction.commandName]
|
||||
|
||||
if(roll) {
|
||||
let dice = ParseRoll(roll)
|
||||
let options = interaction.options.get('options')
|
||||
|
||||
if(options) {
|
||||
let optionDice = ParseOptionRoll(options.value)
|
||||
if(roll) {
|
||||
let dice = ParseRoll(roll)
|
||||
let options = interaction.options.get('options')
|
||||
|
||||
if(options) {
|
||||
let optionDice = ParseOptionRoll(options.value)
|
||||
|
||||
for(let [ key, value ] of Object.entries(optionDice)) {
|
||||
if(value)
|
||||
dice[key] = Array.isArray(value) ? value.concat(dice[key]) : value
|
||||
}
|
||||
}
|
||||
for(let [ key, value ] of Object.entries(optionDice)) {
|
||||
if(value)
|
||||
dice[key] = Array.isArray(value) ? value.concat(dice[key]) : value
|
||||
}
|
||||
}
|
||||
|
||||
RollDice(dice, content => interaction.followUp(content) )
|
||||
}
|
||||
RollDice(dice, content => interaction.followUp(content) )
|
||||
}
|
||||
}
|
||||
|
||||
const FindOption = (options, name) =>
|
||||
options.find(option => option.name == name)
|
||||
options.find(option => option.name == name)
|
||||
|
||||
const HandleAutocomplete = async interaction => {
|
||||
if(globalThis.commands.has(interaction.commandName) ) {
|
||||
let { data } = globalThis.commands.get(interaction.commandName)
|
||||
let subcommand = interaction.options.getSubcommand()
|
||||
let focusedOption = interaction.options.getFocused(true)
|
||||
if(globalThis.commands.has(interaction.commandName) ) {
|
||||
let { data } = globalThis.commands.get(interaction.commandName)
|
||||
let subcommand = interaction.options.getSubcommand()
|
||||
let focusedOption = interaction.options.getFocused(true)
|
||||
|
||||
if(subcommand !== undefined) {
|
||||
data = FindOption(data.options, subcommand)
|
||||
}
|
||||
if(subcommand !== undefined) {
|
||||
data = FindOption(data.options, subcommand)
|
||||
}
|
||||
|
||||
let option = FindOption(data.options, focusedOption.name)
|
||||
let option = FindOption(data.options, focusedOption.name)
|
||||
|
||||
if(!option) {
|
||||
console.error('Could not find option: ' + focusedOption)
|
||||
return
|
||||
}
|
||||
if(!option) {
|
||||
console.error('Could not find option: ' + focusedOption)
|
||||
return
|
||||
}
|
||||
|
||||
let filtered = option
|
||||
.getAutocomplete(interaction)
|
||||
.filter(choice => choice.startsWith(focusedOption.value) )
|
||||
.map(choice => ({ name: choice, value: choice }) )
|
||||
let filtered = option
|
||||
.getAutocomplete(interaction)
|
||||
.filter(choice => choice.startsWith(focusedOption.value) )
|
||||
.map(choice => ({ name: choice, value: choice }) )
|
||||
|
||||
await interaction.respond(filtered)
|
||||
}
|
||||
await interaction.respond(filtered)
|
||||
}
|
||||
}
|
||||
|
||||
const Interaction = interaction => {
|
||||
if(interaction.isChatInputCommand()) {
|
||||
return HandleCommand(interaction)
|
||||
} else if(interaction.isAutocomplete()) {
|
||||
return HandleAutocomplete(interaction)
|
||||
.catch(console.error)
|
||||
}
|
||||
if(interaction.isChatInputCommand()) {
|
||||
return HandleCommand(interaction)
|
||||
} else if(interaction.isAutocomplete()) {
|
||||
return HandleAutocomplete(interaction)
|
||||
.catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
const MessageUpdate = async (oldMessage, newMessage) => {
|
||||
if(globalThis.replies.has(newMessage.id) ) {
|
||||
let { id } = globalThis.replies.get(newMessage.id)
|
||||
if(globalThis.replies.has(newMessage.id) ) {
|
||||
let { id } = globalThis.replies.get(newMessage.id)
|
||||
|
||||
newMessage.channel.messages.fetch(id)
|
||||
.then(reply => RehandleMessage(newMessage, reply) )
|
||||
.catch(err => MessageCycle(newMessage) )
|
||||
} else {
|
||||
MessageCycle(newMessage)
|
||||
}
|
||||
newMessage.channel.messages.fetch(id)
|
||||
.then(reply => RehandleMessage(newMessage, reply) )
|
||||
.catch(err => MessageCycle(newMessage) )
|
||||
} else {
|
||||
MessageCycle(newMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const About = async (interaction) => {
|
||||
let embed = {
|
||||
title: 'dicedicedice',
|
||||
thumbnail: {
|
||||
url: constants.iconUrl
|
||||
},
|
||||
description: constants.aboutMessage(client.guilds.cache.size)
|
||||
}
|
||||
let embed = {
|
||||
title: 'dicedicedice',
|
||||
thumbnail: {
|
||||
url: constants.iconUrl
|
||||
},
|
||||
description: constants.aboutMessage(client.guilds.cache.size)
|
||||
}
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [ embed ],
|
||||
ephemeral: true
|
||||
})
|
||||
await interaction.reply({
|
||||
embeds: [ embed ],
|
||||
ephemeral: true
|
||||
})
|
||||
}
|
||||
|
||||
const AddMacro = async (interaction) => {
|
||||
let respond = Responses(interaction, true)
|
||||
let name = interaction.options.get('name').value.toLowerCase()
|
||||
let respond = Responses(interaction, true)
|
||||
let name = interaction.options.get('name').value.toLowerCase()
|
||||
|
||||
if(!constants.macroNameRegex.test(name))
|
||||
return respond("Please provide a macro name that consists of only alphanumeric characters.")
|
||||
if(!constants.macroNameRegex.test(name))
|
||||
return respond("Please provide a macro name that consists of only alphanumeric characters.")
|
||||
|
||||
if(commands.has(name))
|
||||
return respond("Uhh,, I think that macro name is already taken by my own commands, sorry.")
|
||||
if(commands.has(name))
|
||||
return respond("Uhh,, I think that macro name is already taken by my own commands, sorry.")
|
||||
|
||||
let macros = globalThis.macroCache.get(interaction.guild.id)
|
||||
let macros = globalThis.macroCache.get(interaction.guild.id)
|
||||
|
||||
if(macros && !macros[name] && Object.keys(macros).length >= 100)
|
||||
return respond("I can't keep track of that many macros,, ;-;")
|
||||
if(macros && !macros[name] && Object.keys(macros).length >= 100)
|
||||
return respond("I can't keep track of that many macros,, ;-;")
|
||||
|
||||
let dice = interaction.options.get('dice').value
|
||||
let dice = interaction.options.get('dice').value
|
||||
|
||||
if(!constants.rollRegex.test(dice) )
|
||||
return respond("Please provide a valid roll expression.")
|
||||
if(!constants.rollRegex.test(dice) )
|
||||
return respond("Please provide a valid roll expression.")
|
||||
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
|
||||
await Promise.all([
|
||||
OpenMacros(interaction.guild.id).put(name, dice),
|
||||
ReloadMacros(interaction.guild.id)
|
||||
])
|
||||
interaction.followUp(`Macro added! Try \`/${name}\`! You might need to switch to a different server and back or reopen Discord in order for it to recognize the new command.`)
|
||||
await Promise.all([
|
||||
OpenMacros(interaction.guild.id).put(name, dice),
|
||||
ReloadMacros(interaction.guild.id)
|
||||
])
|
||||
interaction.followUp(`Macro added! Try \`/${name}\`! You might need to switch to a different server and back or reopen Discord in order for it to recognize the new command.`)
|
||||
}
|
||||
|
||||
const RemoveMacro = async (interaction) => {
|
||||
let name = interaction.options.get('name').value.toLowerCase()
|
||||
let macros = macroCache.get(interaction.guild.id)
|
||||
let respond = openResponses(interaction, true)
|
||||
|
||||
if(!macros)
|
||||
return respond('There aren\'t even any macros in this guild!')
|
||||
let name = interaction.options.get('name').value.toLowerCase()
|
||||
let macros = macroCache.get(interaction.guild.id)
|
||||
let respond = openResponses(interaction, true)
|
||||
|
||||
if(!macros)
|
||||
return respond('There aren\'t even any macros in this guild!')
|
||||
|
||||
let dice = macros && macroCache.get(interaction.guild.id)[name]
|
||||
let dice = macros && macroCache.get(interaction.guild.id)[name]
|
||||
|
||||
if(!dice)
|
||||
return respond("There isn't a macro with that name .-.")
|
||||
if(!dice)
|
||||
return respond("There isn't a macro with that name .-.")
|
||||
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await Promise.all([
|
||||
openMacros(interaction.guild.id).del(name),
|
||||
reloadMacros(interaction.guild.id)
|
||||
])
|
||||
await interaction.deferReply({ ephemeral: true })
|
||||
await Promise.all([
|
||||
openMacros(interaction.guild.id).del(name),
|
||||
reloadMacros(interaction.guild.id)
|
||||
])
|
||||
|
||||
await interaction.followUp(`Removed \`${name}\`, its dice expression was: \`\`\`${dice}\`\`\``)
|
||||
await interaction.followUp(`Removed \`${name}\`, its dice expression was: \`\`\`${dice}\`\`\``)
|
||||
}
|
||||
|
||||
const Start = async () => {
|
||||
Command(
|
||||
constants.commands.about,
|
||||
About
|
||||
)
|
||||
Command(
|
||||
constants.commands.about,
|
||||
About
|
||||
)
|
||||
|
||||
Subcommands(constants.commands.macro, {
|
||||
add: AddMacro,
|
||||
remove: RemoveMacro
|
||||
})
|
||||
Subcommands(constants.commands.macro, {
|
||||
add: AddMacro,
|
||||
remove: RemoveMacro
|
||||
})
|
||||
|
||||
Subscribe('interactionCreate', Interaction)
|
||||
Subscribe('messageCreate', MessageCycle)
|
||||
Subscribe('messageUpdate', MessageUpdate)
|
||||
Subscribe('ready', async () => {
|
||||
console.log("Logged in!")
|
||||
Subscribe('interactionCreate', Interaction)
|
||||
Subscribe('messageCreate', MessageCycle)
|
||||
Subscribe('messageUpdate', MessageUpdate)
|
||||
Subscribe('ready', async () => {
|
||||
console.log("Logged in!")
|
||||
|
||||
let guildIds = await PruneDB()
|
||||
let guildIds = await PruneDB()
|
||||
|
||||
for(let guildId of guildIds)
|
||||
await ReloadMacros(guildId)
|
||||
for(let guildId of guildIds)
|
||||
await ReloadMacros(guildId)
|
||||
|
||||
console.log("Ready")
|
||||
})
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationCommands(process.env.DISCORD_ID),
|
||||
{
|
||||
body: [ ...globalThis.commands.values() ]
|
||||
.map(command => command.data )
|
||||
}
|
||||
)
|
||||
.catch(err => console.error('Command registration failed: ', err) )
|
||||
console.log("Ready")
|
||||
})
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationCommands(process.env.DISCORD_ID),
|
||||
{
|
||||
body: [ ...globalThis.commands.values() ]
|
||||
.map(command => command.data )
|
||||
}
|
||||
)
|
||||
.catch(err => console.error('Command registration failed: ', err) )
|
||||
|
||||
console.log(process.env)
|
||||
console.log(process.env)
|
||||
|
||||
await client.login(process.env.DISCORD_TOKEN)
|
||||
.catch(err => console.error('Login failed: ', err) )
|
||||
await client.login(process.env.DISCORD_TOKEN)
|
||||
.catch(err => console.error('Login failed: ', err) )
|
||||
|
||||
setInterval(PruneReplies, 1000 * 60)
|
||||
setInterval(PruneReplies, 1000 * 60)
|
||||
}
|
||||
|
||||
Start()
|
||||
|
Loading…
x
Reference in New Issue
Block a user