deno fmt + l!p now sends available presets

This commit is contained in:
Ean Milligan 2025-04-25 22:08:07 -04:00
parent 5b87cfda75
commit 30547e9d6a
3 changed files with 252 additions and 213 deletions

View File

@ -1,4 +1,4 @@
# Logogram Discord Bot - A FFXIV Eureka Utility Bot | V1.1.6 - 2025/09/21 # Logogram Discord Bot - A FFXIV Eureka Utility Bot | V1.2.0 - 2025/09/21
A Discord bot for Eureka Logos Actions and their recipes. A Discord bot for Eureka Logos Actions and their recipes.
## Commands ## Commands

View File

@ -1,29 +1,31 @@
export const config = { export const config = {
'name': 'Logogram Bot', // Name of the bot name: 'Logogram Bot', // Name of the bot
'version': '1.1.6', // Version of the bot version: '1.2.0', // Version of the bot
'token': 'the_bot_token', // Discord API Token for this bot token: 'the_bot_token', // Discord API Token for this bot
'localToken': 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token" localToken: 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
'prefix': 'l!', // Prefix for all commands prefix: 'l!', // Prefix for all commands
'logChannel': 0n, // Discord channel ID where the bot should put startup messages and other error messages needed logChannel: 0n, // Discord channel ID where the bot should put startup messages and other error messages needed
'reportChannel': 0n, // Discord channel ID where reports will be sent when using the built-in report command reportChannel: 0n, // Discord channel ID where reports will be sent when using the built-in report command
'devServer': 0n, // Discord guild ID where testing of indev features/commands will be handled, used in conjunction with the DEVMODE bool in mod.ts devServer: 0n, // Discord guild ID where testing of indev features/commands will be handled, used in conjunction with the DEVMODE bool in mod.ts
'owner': 0n, // Discord user ID of the bot admin owner: 0n, // Discord user ID of the bot admin
'imageUrl': '', // Base URL for logos action images imageUrl: '', // Base URL for logos action images
'resultsPerPage': 5, // Results to show on each page resultsPerPage: 5, // Results to show on each page
'mneme': { // Emojis for the mnemes mneme: {
'blue': '', // Emojis for the mnemes
'cyan': '', blue: '',
'green': '', cyan: '',
'purple': '', green: '',
'red': '', purple: '',
'yellow': '', red: '',
yellow: '',
}, },
'jobType': { // Emojis for job type icons jobType: {
'tank': '', // Emojis for job type icons
'healer': '', tank: '',
'melee': '', healer: '',
'ranged': '', melee: '',
'magic': '', ranged: '',
magic: '',
}, },
}; };

413
mod.ts
View File

@ -1,17 +1,17 @@
import { import {
DiscordActivityTypes, DiscordActivityTypes,
DiscordenoGuild, DiscordenoGuild,
DiscordenoMessage, DiscordenoMessage,
editBotNickname, editBotNickname,
editBotStatus, editBotStatus,
// Log4Deno deps // Log4Deno deps
initLog, initLog,
Intents, Intents,
log, log,
LT, LT,
sendMessage, sendMessage,
// Discordeno deps // Discordeno deps
startBot, startBot,
} from './deps.ts'; } from './deps.ts';
import { constantCmds } from './src/constantCmds.ts'; import { constantCmds } from './src/constantCmds.ts';
@ -26,189 +26,226 @@ import { classToType } from './src/utils.ts';
initLog('logs', DEBUG); initLog('logs', DEBUG);
startBot({ startBot({
token: LOCALMODE ? config.localToken : config.token, token: LOCALMODE ? config.localToken : config.token,
intents: [Intents.GuildMessages, Intents.DirectMessages, Intents.Guilds], intents: [Intents.GuildMessages, Intents.DirectMessages, Intents.Guilds],
eventHandlers: { eventHandlers: {
ready: () => { ready: () => {
editBotStatus({ editBotStatus({
activities: [{ activities: [
name: 'Booting up . . .', {
type: DiscordActivityTypes.Game, name: 'Booting up . . .',
createdAt: new Date().getTime(), type: DiscordActivityTypes.Game,
}], createdAt: new Date().getTime(),
status: 'online', },
}); ],
status: 'online',
});
// setTimeout added to make sure the startup message does not error out // setTimeout added to make sure the startup message does not error out
setTimeout(() => { setTimeout(() => {
LOCALMODE && editBotNickname(config.devServer, `LOCAL - ${config.name}`); LOCALMODE && editBotNickname(config.devServer, `LOCAL - ${config.name}`);
editBotStatus({ editBotStatus({
activities: [{ activities: [
name: 'Booting Complete', {
type: DiscordActivityTypes.Game, name: 'Booting Complete',
createdAt: new Date().getTime(), type: DiscordActivityTypes.Game,
}], createdAt: new Date().getTime(),
status: 'online', },
}); ],
sendMessage(config.logChannel, `${config.name} has started, running version ${config.version}.`).catch((e) => { status: 'online',
log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); });
}); sendMessage(config.logChannel, `${config.name} has started, running version ${config.version}.`).catch((e) => {
}, 1000); log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`);
}, });
guildCreate: (guild: DiscordenoGuild) => { }, 1000);
sendMessage(config.logChannel, `New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`).catch((e) => { },
log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); guildCreate: (guild: DiscordenoGuild) => {
}); sendMessage(config.logChannel, `New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`).catch((e) => {
}, log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`);
guildDelete: (guild: DiscordenoGuild) => { });
sendMessage(config.logChannel, `I have been removed from: ${guild.name} (id: ${guild.id}).`).catch((e) => { },
log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); guildDelete: (guild: DiscordenoGuild) => {
}); sendMessage(config.logChannel, `I have been removed from: ${guild.name} (id: ${guild.id}).`).catch((e) => {
}, log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`);
messageCreate: (message: DiscordenoMessage) => { });
// Ignore all other bots },
if (message.isBot) return; messageCreate: (message: DiscordenoMessage) => {
// Ignore all other bots
if (message.isBot) return;
// Ignore all messages that are not commands // Ignore all messages that are not commands
if (message.content.indexOf(config.prefix) !== 0) return; if (message.content.indexOf(config.prefix) !== 0) return;
// Split into standard command + args format // Split into standard command + args format
const args = message.content.slice(config.prefix.length).trim().split(/[ \n]+/g); const args = message.content
const command = args.shift()?.toLowerCase(); .slice(config.prefix.length)
.trim()
.split(/[ \n]+/g);
const command = args.shift()?.toLowerCase();
if (command === 'help' || command === 'h' || command === '?') { if (command === 'help' || command === 'h' || command === '?') {
// l!help or l!h or l!? // l!help or l!h or l!?
// Help command, prints from help file // Help command, prints from help file
message.send(constantCmds.help).catch((e) => { message.send(constantCmds.help).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} else if (command === 'info' || command === 'i') { } else if (command === 'info' || command === 'i') {
// l!info or l!i // l!info or l!i
// Info command, prints short desc on bot and some links // Info command, prints short desc on bot and some links
message.send(constantCmds.info).catch((e) => { message.send(constantCmds.info).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} else if (command === 'version' || command === 'v') { } else if (command === 'version' || command === 'v') {
// l!version or l!v // l!version or l!v
// Returns version of the bot // Returns version of the bot
message.send(constantCmds.version).catch((e) => { message.send(constantCmds.version).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} else if (command === 'flowchart' || command === 'flow' || command === 'f') { } else if (command === 'flowchart' || command === 'flow' || command === 'f') {
// l!flowchart or l!flow or l!logos or l!f // l!flowchart or l!flow or l!logos or l!f
// Returns logos actions stuff // Returns logos actions stuff
message.send(constantCmds.flowchart).catch((e) => { message.send(constantCmds.flowchart).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} else if (command === 'logograms' || command === 'logogram' || command === 'logos' || command === 'logo' || command === 'l') { } else if (command === 'logograms' || command === 'logogram' || command === 'logos' || command === 'logo' || command === 'l') {
// l!logograms or l!logograms or l!logos or l!logo or l!l // l!logograms or l!logograms or l!logos or l!logo or l!l
// Returns logos actions stuff // Returns logos actions stuff
if (!args.length) { if (!args.length) {
message.send(constantCmds.logogramsNoQuery).catch((e) => { message.send(constantCmds.logogramsNoQuery).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} else { } else {
const params = { const params = {
class: '', class: '',
rawClass: '', rawClass: '',
isNin: false, isNin: false,
page: 1, page: 1,
debug: false, debug: false,
}; };
const classPrefixes = ['-class=', 'class=', '-c=', 'c=']; const classPrefixes = ['-class=', 'class=', '-c=', 'c='];
const pagePrefixes = ['-page=', 'page=', '-p=', 'p=']; const pagePrefixes = ['-page=', 'page=', '-p=', 'p='];
const debugPrefixes = ['-debug=', 'debug=', '-d=', 'd=']; const debugPrefixes = ['-debug=', 'debug=', '-d=', 'd='];
const allPrefixes = classPrefixes.concat(pagePrefixes).concat(debugPrefixes); const allPrefixes = classPrefixes.concat(pagePrefixes).concat(debugPrefixes);
args.forEach((arg) => { args.forEach((arg) => {
if (classPrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx))) { // if (classPrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx))) {
params.rawClass = arg.split('=')[1]; //
params.class = classToType(params.rawClass); params.rawClass = arg.split('=')[1];
params.isNin = params.rawClass.toLowerCase() === 'nin'; params.class = classToType(params.rawClass);
} else if (pagePrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx))) { params.isNin = params.rawClass.toLowerCase() === 'nin';
params.page = parseInt(arg.split('=')[1]); } else if (pagePrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx))) {
} else if (debugPrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx))) { params.page = parseInt(arg.split('=')[1]);
params.debug = true; } else if (debugPrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx))) {
} params.debug = true;
}); }
});
const cleanArgs = args.filter((arg) => !(allPrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx)))); const cleanArgs = args.filter((arg) => !allPrefixes.some((pfx) => arg.toLowerCase().startsWith(pfx)));
const rawQuery = cleanArgs.join(' '); const rawQuery = cleanArgs.join(' ');
const query = rawQuery.toLowerCase(); const query = rawQuery.toLowerCase();
if (data.ActionNames.includes(query)) { if (data.ActionNames.includes(query)) {
log(LT.LOG, `in name matched '${query}'`); log(LT.LOG, `in name matched '${query}'`);
const singleAction: Array<number> = [data.ActionNames.indexOf(query)]; const singleAction: Array<number> = [data.ActionNames.indexOf(query)];
message.send({ message
content: 'Showing single action:', .send({
embeds: generateEmbeds(singleAction, params.debug), content: 'Showing single action:',
}).catch((e) => { embeds: generateEmbeds(singleAction, params.debug),
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); })
}); .catch((e) => {
} else if (query && data.ActionShortNames.includes(query)) { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
log(LT.LOG, `in shorthand matched '${query}'`); });
const searchResults: Array<number> = data.Actions.filter((action) => action.shorthand === query).map((action) => data.ActionNames.indexOf(action.name.toLowerCase())); } else if (query && data.ActionShortNames.includes(query)) {
message.send({ log(LT.LOG, `in shorthand matched '${query}'`);
content: searchResults.length > 1 ? `Showing ${searchResults.length} actions:` : 'Showing single action:', const searchResults: Array<number> = data.Actions.filter((action) => action.shorthand === query).map((action) => data.ActionNames.indexOf(action.name.toLowerCase()));
embeds: generateEmbeds(searchResults, params.debug), message
}).catch((e) => { .send({
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); content: searchResults.length > 1 ? `Showing ${searchResults.length} actions:` : 'Showing single action:',
}); embeds: generateEmbeds(searchResults, params.debug),
} else { })
log(LT.LOG, `in general search '${query}'`); .catch((e) => {
const initialSearchResults: Array<number> = data.ActionNames.filter((action) => action.includes(query)).map((action) => data.ActionNames.indexOf(action)); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
const searchResults: Array<number> = initialSearchResults.filter((actionIdx) => });
params.class ? (data.Actions[actionIdx].jobs.includes('all-nin') && !params.isNin) || data.Actions[actionIdx].jobs.includes('all') || data.Actions[actionIdx].jobs.includes(params.class) : true } else {
); log(LT.LOG, `in general search '${query}'`);
const initialSearchResults: Array<number> = data.ActionNames.filter((action) => action.includes(query)).map((action) => data.ActionNames.indexOf(action));
const searchResults: Array<number> = initialSearchResults.filter((actionIdx) =>
params.class
? (data.Actions[actionIdx].jobs.includes('all-nin') && !params.isNin) ||
data.Actions[actionIdx].jobs.includes('all') ||
data.Actions[actionIdx].jobs.includes(params.class)
: true
);
if (searchResults.length) { if (searchResults.length) {
const totalPages = Math.ceil(searchResults.length / config.resultsPerPage); const totalPages = Math.ceil(searchResults.length / config.resultsPerPage);
if (params.page > totalPages) { if (params.page > totalPages) {
params.page = totalPages; params.page = totalPages;
} }
const classMessage = params.class ? ` -class=${params.rawClass}` : ''; const classMessage = params.class ? ` -class=${params.rawClass}` : '';
const userQuery = `${rawQuery}${classMessage}`.trim(); const userQuery = `${rawQuery}${classMessage}`.trim();
const paginationMessage = searchResults.length > config.resultsPerPage const paginationMessage = searchResults.length > config.resultsPerPage
? `\nShowing page ${params.page} of ${totalPages}\n\nTo see more results, please run \`${config.prefix}logos ${userQuery} -page=#\`, where # is the page number you wish to see.` ? `\nShowing page ${params.page} of ${totalPages}\n\nTo see more results, please run \`${config.prefix}logos ${userQuery} -page=#\`, where # is the page number you wish to see.`
: ''; : '';
message.send({ message
content: `${searchResults.length} result${searchResults.length > 1 ? 's' : ''} matching query: \`${userQuery}\`${paginationMessage}`, .send({
embeds: generateEmbeds(searchResults.slice((params.page - 1) * config.resultsPerPage, config.resultsPerPage * params.page), params.debug), content: `${searchResults.length} result${searchResults.length > 1 ? 's' : ''} matching query: \`${userQuery}\`${paginationMessage}`,
}).catch((e) => { embeds: generateEmbeds(searchResults.slice((params.page - 1) * config.resultsPerPage, config.resultsPerPage * params.page), params.debug),
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); })
}); .catch((e) => {
} else { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
message.send({ });
content: `No results found matching query: \`${rawQuery}\``, } else {
}).catch((e) => { message
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); .send({
}); content: `No results found matching query: \`${rawQuery}\``,
} })
} .catch((e) => {
} log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
} else if (command === 'preset' || command === 'p') { });
// l!preset or l!p }
// Returns logos actions stuff }
const rawQuery = args.join(' '); }
const query = rawQuery.toLowerCase(); } else if (command === 'preset' || command === 'p') {
// l!preset or l!p
// Returns logos actions stuff
const rawQuery = args.join(' ');
const query = rawQuery.toLowerCase();
if (data.Presets.has(query)) { if (data.Presets.has(query)) {
const preset: Array<number> = data.Presets.get(query) ?? []; const preset: Array<number> = data.Presets.get(query) ?? [];
message.send({ message
content: `Showing ${rawQuery} Preset:`, .send({
embeds: generateEmbeds(preset, false), content: `Showing ${rawQuery} Preset:`,
}).catch((e) => { embeds: generateEmbeds(preset, false),
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); })
}); .catch((e) => {
} else { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
message.send({ });
content: `No preset found matching query: \`${rawQuery}\``, } else if (!query) {
}).catch((e) => { message
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); .send({
}); content: `Available presets: ${
} data.Presets.keys()
} .toArray()
}, .map((p) => `\`${p}\``)
}, .join(', ')
}`,
})
.catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
});
} else {
message
.send({
content: `No preset found matching query: \`${rawQuery}\``,
})
.catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
});
}
}
},
},
}); });