More error handling, better time parsing, basic feature set fully flushed out.
This commit is contained in:
Ean Milligan (Bastion) 2021-06-02 12:27:05 -04:00
parent d60d1ff829
commit af92920ec9
6 changed files with 402 additions and 290 deletions

View File

@ -2,4 +2,4 @@
Group Up is a Discord bot built for scheduling events in your Guild. Group Up is a Discord bot built for scheduling events in your Guild.
# Invite Link # Invite Link
https://discord.com/api/oauth2/authorize?client_id=847256159123013722&permissions=75776&scope=bot https://discord.com/api/oauth2/authorize?client_id=847256159123013722&permissions=92160&scope=bot

View File

@ -1,6 +1,6 @@
export const config = { export const config = {
"name": "Group Up", // Name of the bot "name": "Group Up", // Name of the bot
"version": "0.0.0", // Version of the bot "version": "0.2.1", // 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": "[[", // Prefix for all commands "prefix": "[[", // Prefix for all commands

454
mod.ts
View File

@ -19,7 +19,7 @@ import intervals from "./src/intervals.ts";
import { LFGActivities } from "./src/games.ts"; import { LFGActivities } from "./src/games.ts";
import { JoinLeaveType } from "./src/lfgHandlers.d.ts"; import { JoinLeaveType } from "./src/lfgHandlers.d.ts";
import { handleLFGStep, handleMemberJoin, handleMemberLeave } from "./src/lfgHandlers.ts"; import { handleLFGStep, handleMemberJoin, handleMemberLeave } from "./src/lfgHandlers.ts";
import { constantCmds, editBtns } from "./src/constantCmds.ts"; import { constantCmds, editBtns, lfgStepQuestions } from "./src/constantCmds.ts";
import { DEBUG, LOCALMODE } from "./flags.ts"; import { DEBUG, LOCALMODE } from "./flags.ts";
import config from "./config.ts"; import config from "./config.ts";
@ -35,7 +35,7 @@ const dbClient = await new Client().connect({
// Initialize logging client with folder to use for logs, needs --allow-write set on Deno startup // Initialize logging client with folder to use for logs, needs --allow-write set on Deno startup
initLog("logs", DEBUG); initLog("logs", DEBUG);
log(LT.INFO, `${config.name} Starting up . . .`) log(LT.INFO, `${config.name} Starting up . . .`);
// Handle idling out the active builders // Handle idling out the active builders
const activeBuilders: Array<BuildingLFG> = []; const activeBuilders: Array<BuildingLFG> = [];
@ -158,10 +158,12 @@ startBot({
} }
] ]
} }
}).catch(e =>{
log(LT.WARN, `Failed to send message | ${JSON.stringify(e)}`);
}); });
} }
else if (hasGuildPermissions(message.guildId, message.authorId, ["ADMINISTRATOR"])) { else if (await hasGuildPermissions(message.guildId, message.authorId, ["ADMINISTRATOR"])) {
const newPrefix = message.content.replace(`<@!${botId}>`, "").trim(); const newPrefix = message.content.replace(`<@!${botId}>`, "").trim();
if (newPrefix.length <= 10) { if (newPrefix.length <= 10) {
@ -191,6 +193,8 @@ startBot({
} }
] ]
} }
}).catch(e =>{
log(LT.WARN, `Failed to send message | ${JSON.stringify(e)}`);
}); });
} else { } else {
message.send({ message.send({
@ -202,6 +206,8 @@ startBot({
} }
] ]
} }
}).catch(e =>{
log(LT.WARN, `Failed to send message | ${JSON.stringify(e)}`);
}); });
} }
} else { } else {
@ -214,6 +220,8 @@ startBot({
} }
] ]
} }
}).catch(e =>{
log(LT.WARN, `Failed to send message | ${JSON.stringify(e)}`);
}); });
} }
} }
@ -282,6 +290,8 @@ startBot({
] ]
} }
] ]
}).catch(e =>{
log(LT.WARN, `Failed to edit message | ${JSON.stringify(e)}`);
}); });
activeLFGPosts.push({ activeLFGPosts.push({
@ -298,10 +308,14 @@ startBot({
)); ));
} }
await activeBuilders[activeIdx].questionMsg.delete() await activeBuilders[activeIdx].questionMsg.delete().catch(e =>{
log(LT.WARN, `Failed to delete message | ${JSON.stringify(e)}`);
});
activeBuilders.splice(activeIdx, 1); activeBuilders.splice(activeIdx, 1);
} }
await message.delete(); await message.delete().catch(e =>{
log(LT.WARN, `Failed to delete message | ${JSON.stringify(e)}`);
});
} }
return; return;
} }
@ -359,242 +373,257 @@ startBot({
// Create a new LFG // Create a new LFG
else if (subcmd === "create" || subcmd === "c") { else if (subcmd === "create" || subcmd === "c") {
const lfgMsg = await message.send(`Creating new LFG post for <@${message.authorId}>. Please reply with the requested information and watch as your LFG post gets created!`); try {
const lfgMsg = await message.send(`Creating new LFG post for <@${message.authorId}>. Please reply with the requested information and watch as your LFG post gets created!`);
const gameButtons: Array<ButtonComponent> = Object.keys(LFGActivities).map(game => { const gameButtons: Array<ButtonComponent> = Object.keys(LFGActivities).map(game => {
return { return {
type: 2, type: 2,
label: game, label: game,
customId: `building@set_game#${game}`, customId: `building@set_game#${game}`,
style: DiscordButtonStyles.Primary style: DiscordButtonStyles.Primary
}; };
}); });
const buttonComps: Array<ActionRow> = []; const buttonComps: Array<ActionRow> = [];
const temp: Array<ActionRow["components"]> = []; const temp: Array<ActionRow["components"]> = [];
gameButtons.forEach((btn, idx) => { gameButtons.forEach((btn, idx) => {
if (!temp[Math.floor(idx/5)]) { if (!temp[Math.floor(idx/5)]) {
temp[Math.floor(idx/5)] = [btn]; temp[Math.floor(idx/5)] = [btn];
} else { } else {
temp[Math.floor(idx/5)].push(btn); temp[Math.floor(idx/5)].push(btn);
} }
}); });
temp.forEach(btns => { temp.forEach(btns => {
if (btns.length && btns.length <= 5) { if (btns.length && btns.length <= 5) {
buttonComps.push({ buttonComps.push({
type: 1, type: 1,
components: btns components: btns
}); });
} }
}); });
const question = await message.send({ const question = await message.send({
content: "Please select a game from the list below. If your game is not listed, please type it out:", content: lfgStepQuestions.set_game,
components: buttonComps components: buttonComps
}); });
activeBuilders.push({ activeBuilders.push({
userId: message.authorId, userId: message.authorId,
channelId: message.channelId, channelId: message.channelId,
step: "set_game", step: "set_game",
lfgMsg: lfgMsg, lfgMsg: lfgMsg,
questionMsg: question, questionMsg: question,
lastTouch: new Date(), lastTouch: new Date(),
maxIdle: 60, maxIdle: 60,
editing: false editing: false
}); });
message.delete(); message.delete();
}
catch (e) {
log(LT.WARN, `LFG failed at step | create | ${JSON.stringify(e)}`);
}
} }
// Delete an existing LFG // Delete an existing LFG
else if (subcmd === "delete" || subcmd === "d") { else if (subcmd === "delete" || subcmd === "d") {
// User provided a Uid, use it try {
if (lfgUid) { // User provided a Uid, use it
const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId && lfgUid === lfg.lfgUid)); if (lfgUid) {
const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId && lfgUid === lfg.lfgUid));
// Found one, delete // Found one, delete
if (matches.length) { if (matches.length) {
await deleteMessage(matches[0].channelId, matches[0].messageId, "User requested LFG to be deleted."); await deleteMessage(matches[0].channelId, matches[0].messageId, "User requested LFG to be deleted.");
const lfgIdx = activeLFGPosts.findIndex(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId && lfgUid === lfg.lfgUid)); const lfgIdx = activeLFGPosts.findIndex(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId && lfgUid === lfg.lfgUid));
activeLFGPosts.splice(lfgIdx, 1); activeLFGPosts.splice(lfgIdx, 1);
localStorage.setItem("activeLFGPosts", JSON.stringify(activeLFGPosts, (_key, value) => localStorage.setItem("activeLFGPosts", JSON.stringify(activeLFGPosts, (_key, value) =>
typeof value === "bigint" ? value.toString() + "n" : value typeof value === "bigint" ? value.toString() + "n" : value
)); ));
const m = await message.send(constantCmds.lfgDelete3); const m = await message.send(constantCmds.lfgDelete3);
setTimeout(() => { setTimeout(() => {
m.delete(); m.delete();
message.delete(); message.delete();
}, 5000); }, 5000);
}
// Did not find one
else {
const m = await message.send(constantCmds.lfgDelete1);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
} }
// Did not find one // User did not provide a Uid, find it automatically
else { else {
const m = await message.send(constantCmds.lfgDelete1); const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId));
setTimeout(() => {
m.delete(); // Found one, delete
message.delete(); if (matches.length === 1) {
}, 5000); await deleteMessage(matches[0].channelId, matches[0].messageId, "User requested LFG to be deleted.");
} const lfgIdx = activeLFGPosts.findIndex(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId));
activeLFGPosts.splice(lfgIdx, 1);
localStorage.setItem("activeLFGPosts", JSON.stringify(activeLFGPosts, (_key, value) =>
typeof value === "bigint" ? value.toString() + "n" : value
));
const m = await message.send(constantCmds.lfgDelete3);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
// Found multiple, notify user
else if (matches.length) {
const deleteMsg = constantCmds.lfgDelete2;
const deepCloningFailedSoThisIsTheSolution = constantCmds.lfgDelete2.embed.fields[0].value;
matches.forEach(mt => {
deleteMsg.embed.fields[0].value += `[${mt.lfgUid}](https://discord.com/channels/${message.guildId}/${mt.channelId}/${mt.messageId})\n`
});
deleteMsg.embed.fields[0].value += "\nThis message will self descruct in 30 seconds."
const m = await message.send(deleteMsg);
constantCmds.lfgDelete2.embed.fields[0].value = deepCloningFailedSoThisIsTheSolution;
setTimeout(() => {
m.delete();
message.delete();
}, 30000);
}
// Found none, notify user you cannot delete other's lfgs
else {
const m = await message.send(constantCmds.lfgDelete1);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
}
} }
catch (e) {
// User did not provide a Uid, find it automatically log(LT.WARN, `LFG failed at step | delete | ${JSON.stringify(e)}`);
else {
const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId));
// Found one, delete
if (matches.length === 1) {
await deleteMessage(matches[0].channelId, matches[0].messageId, "User requested LFG to be deleted.");
const lfgIdx = activeLFGPosts.findIndex(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId));
activeLFGPosts.splice(lfgIdx, 1);
localStorage.setItem("activeLFGPosts", JSON.stringify(activeLFGPosts, (_key, value) =>
typeof value === "bigint" ? value.toString() + "n" : value
));
const m = await message.send(constantCmds.lfgDelete3);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
// Found multiple, notify user
else if (matches.length) {
const deleteMsg = constantCmds.lfgDelete2;
const deepCloningFailedSoThisIsTheSolution = constantCmds.lfgDelete2.embed.fields[0].value;
matches.forEach(mt => {
deleteMsg.embed.fields[0].value += `[${mt.lfgUid}](https://discord.com/channels/${message.guildId}/${mt.channelId}/${mt.messageId})\n`
});
deleteMsg.embed.fields[0].value += "\nThis message will self descruct in 30 seconds."
const m = await message.send(deleteMsg);
constantCmds.lfgDelete2.embed.fields[0].value = deepCloningFailedSoThisIsTheSolution;
setTimeout(() => {
m.delete();
message.delete();
}, 30000);
}
// Found none, notify user you cannot delete other's lfgs
else {
const m = await message.send(constantCmds.lfgDelete1);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
} }
} }
// Edit an existing LFG // Edit an existing LFG
else if (subcmd === "edit" || subcmd === "e") { else if (subcmd === "edit" || subcmd === "e") {
// User provided a Uid, use it try {
if (lfgUid) { // User provided a Uid, use it
const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId && lfgUid === lfg.lfgUid)); if (lfgUid) {
const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId && lfgUid === lfg.lfgUid));
// Found one, edit // Found one, edit
if (matches.length) { if (matches.length) {
const lfgMessage = await (await getMessage(matches[0].channelId, matches[0].messageId)).edit({ const lfgMessage = await (await getMessage(matches[0].channelId, matches[0].messageId)).edit({
content: `Editing new LFG post for <@${matches[0].ownerId}>. Please reply with the requested information and watch as your LFG post gets edited!` content: `Editing new LFG post for <@${matches[0].ownerId}>. Please reply with the requested information and watch as your LFG post gets edited!`
}); });
const question = await message.send({ const question = await message.send({
content: "Please select an item to edit from the buttons below:", content: "Please select an item to edit from the buttons below:",
components: [{ components: [{
type: 1, type: 1,
components: editBtns components: editBtns
}] }]
}); });
activeBuilders.push({ activeBuilders.push({
userId: matches[0].ownerId, userId: matches[0].ownerId,
channelId: matches[0].channelId, channelId: matches[0].channelId,
step: "edit_btn", step: "edit_btn",
lfgMsg: lfgMessage, lfgMsg: lfgMessage,
questionMsg: question, questionMsg: question,
lastTouch: new Date(), lastTouch: new Date(),
maxIdle: 60, maxIdle: 60,
editing: true editing: true
}); });
message.delete(); message.delete();
}
// Did not find one
else {
const m = await message.send(constantCmds.lfgEdit1);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
} }
// Did not find one // User did not provide a Uid, find it automatically
else { else {
const m = await message.send(constantCmds.lfgEdit1); const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId));
setTimeout(() => {
m.delete(); // Found one, edit
if (matches.length === 1) {
const lfgMessage = await (await getMessage(matches[0].channelId, matches[0].messageId)).edit({
content: `Editing new LFG post for <@${matches[0].ownerId}>. Please reply with the requested information and watch as your LFG post gets edited!`
});
const question = await message.send({
content: "Please select an item to edit from the buttons below:",
components: [{
type: 1,
components: editBtns
}]
});
activeBuilders.push({
userId: matches[0].ownerId,
channelId: matches[0].channelId,
step: "edit_btn",
lfgMsg: lfgMessage,
questionMsg: question,
lastTouch: new Date(),
maxIdle: 60,
editing: true
});
message.delete(); message.delete();
}, 5000); }
// Found multiple, notify user
else if (matches.length) {
const deleteMsg = constantCmds.lfgEdit2;
const deepCloningFailedSoThisIsTheSolution = constantCmds.lfgEdit2.embed.fields[0].value;
matches.forEach(mt => {
deleteMsg.embed.fields[0].value += `[${mt.lfgUid}](https://discord.com/channels/${message.guildId}/${mt.channelId}/${mt.messageId})\n`
});
deleteMsg.embed.fields[0].value += "\nThis message will self descruct in 30 seconds."
const m = await message.send(deleteMsg);
constantCmds.lfgEdit2.embed.fields[0].value = deepCloningFailedSoThisIsTheSolution;
setTimeout(() => {
m.delete();
message.delete();
}, 30000);
}
// Found none, notify user you cannot edit other's lfgs
else {
const m = await message.send(constantCmds.lfgEdit1);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
} }
} }
catch (e) {
// User did not provide a Uid, find it automatically log(LT.WARN, `LFG failed at step | edit | ${JSON.stringify(e)}`);
else {
const matches = activeLFGPosts.filter(lfg => (message.authorId === lfg.ownerId && message.channelId === lfg.channelId));
// Found one, edit
if (matches.length === 1) {
const lfgMessage = await (await getMessage(matches[0].channelId, matches[0].messageId)).edit({
content: `Editing new LFG post for <@${matches[0].ownerId}>. Please reply with the requested information and watch as your LFG post gets edited!`
});
const question = await message.send({
content: "Please select an item to edit from the buttons below:",
components: [{
type: 1,
components: editBtns
}]
});
activeBuilders.push({
userId: matches[0].ownerId,
channelId: matches[0].channelId,
step: "edit_btn",
lfgMsg: lfgMessage,
questionMsg: question,
lastTouch: new Date(),
maxIdle: 60,
editing: true
});
message.delete();
}
// Found multiple, notify user
else if (matches.length) {
const deleteMsg = constantCmds.lfgEdit2;
const deepCloningFailedSoThisIsTheSolution = constantCmds.lfgEdit2.embed.fields[0].value;
matches.forEach(mt => {
deleteMsg.embed.fields[0].value += `[${mt.lfgUid}](https://discord.com/channels/${message.guildId}/${mt.channelId}/${mt.messageId})\n`
});
deleteMsg.embed.fields[0].value += "\nThis message will self descruct in 30 seconds."
const m = await message.send(deleteMsg);
constantCmds.lfgEdit2.embed.fields[0].value = deepCloningFailedSoThisIsTheSolution;
setTimeout(() => {
m.delete();
message.delete();
}, 30000);
}
// Found none, notify user you cannot edit other's lfgs
else {
const m = await message.send(constantCmds.lfgEdit1);
setTimeout(() => {
m.delete();
message.delete();
}, 5000);
}
} }
} }
} }
@ -657,6 +686,9 @@ startBot({
interactionCreate: async (interact: Interaction) => { interactionCreate: async (interact: Interaction) => {
if (interact.type === DiscordInteractionTypes.MessageComponent) { if (interact.type === DiscordInteractionTypes.MessageComponent) {
if (interact.message && interact.data && (interact.data as ButtonData).customId && interact.member) { if (interact.message && interact.data && (interact.data as ButtonData).customId && interact.member) {
log(LT.INFO, `Handling Button ${(interact.data as ButtonData).customId}`);
console.log(LT.LOG, `Button Data | ${JSON.stringify(interact)}`);
sendInteractionResponse(BigInt(interact.id), interact.token, { sendInteractionResponse(BigInt(interact.id), interact.token, {
type: 6 type: 6
}); });
@ -720,7 +752,7 @@ startBot({
} }
case "active": { case "active": {
const member = await structures.createDiscordenoMember(interact.member, BigInt(interact.guildId)); const member = await structures.createDiscordenoMember(interact.member, BigInt(interact.guildId));
const message = await structures.createDiscordenoMessage(interact.message); const message = await getMessage(BigInt(interact.channelId), BigInt(interact.message.id));
const embeds = message.embeds[0].fields || []; const embeds = message.embeds[0].fields || [];
let results: JoinLeaveType = { let results: JoinLeaveType = {
@ -766,7 +798,7 @@ startBot({
const nextComponents: Array<ActionRow> = []; const nextComponents: Array<ActionRow> = [];
switch (action) { switch (action) {
case "set_game": { case "set_game": {
nextQuestion = "Please select a game from the list below. If your game is not listed, please type it out:"; nextQuestion = lfgStepQuestions.set_game;
const gameButtons: Array<ButtonComponent> = Object.keys(LFGActivities).map(game => { const gameButtons: Array<ButtonComponent> = Object.keys(LFGActivities).map(game => {
return { return {

View File

@ -170,3 +170,14 @@ export const editBtns: ActionRow["components"] = [
style: DiscordButtonStyles.Primary style: DiscordButtonStyles.Primary
} }
]; ];
export const lfgStepQuestions = {
"set_game": "Please select a game from the list below. If your game is not listed, please type it out:",
"set_activity_with_button": "Please select an Activity from the list below. Depending on the game selected, these may be categories you can use to drill down to a specific activity.\n\nIf your activity is not listed, please type it out:",
"set_activity_with_text": "Please type the activity name out:",
"set_activity_from_category": "Please select an Activity from the list below.\n\nIf your activity is not listed, please type it out:",
"set_player_cnt": "Please enter the max number of members for this activity:",
"set_time": "Please enter the time of the activity:\nRecommended format: `h:mm am/pm tz month/day`",
"set_desc": "Please enter a description for the activity. Enter `none` to skip:",
"set_done": "Finalizing, please wait. . ."
};

View File

@ -41,17 +41,22 @@ const getRandomStatus = (): string => {
const updateListStatistics = (botID: BigInt, serverCount: number): void => { const updateListStatistics = (botID: BigInt, serverCount: number): void => {
config.botLists.forEach(async e => { config.botLists.forEach(async e => {
if (e.enabled) { if (e.enabled) {
log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`) log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`);
const tempHeaders = new Headers(); try {
tempHeaders.append(e.headers[0].header, e.headers[0].value); const tempHeaders = new Headers();
tempHeaders.append("Content-Type", "application/json"); tempHeaders.append(e.headers[0].header, e.headers[0].value);
// ?{} is a template used in config, just need to replace it with the real value tempHeaders.append("Content-Type", "application/json");
const response = await fetch(e.apiUrl.replace("?{bot_id}", botID.toString()), { // ?{} is a template used in config, just need to replace it with the real value
"method": 'POST', const response = await fetch(e.apiUrl.replace("?{bot_id}", botID.toString()), {
"headers": tempHeaders, "method": 'POST',
"body": JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()) // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number "headers": tempHeaders,
}); "body": JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()) // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number
log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`); });
log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`);
}
catch (e) {
log(LT.WARN, `Failed to post statistics to ${e.name} | ${JSON.stringify(e)}`);
}
} }
}); });
}; };
@ -64,16 +69,27 @@ const buildingTimeout = async (activeBuilders: Array<BuildingLFG>): Promise<void
if (activeBuilders[i].editing) { if (activeBuilders[i].editing) {
activeBuilders[i].lfgMsg.edit({ activeBuilders[i].lfgMsg.edit({
content: "" content: ""
}).catch(e => {
log(LT.WARN, `Failed to clean up active builder | edit | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${JSON.stringify(e)}`);
}); });
} else { } else {
activeBuilders[i].lfgMsg.delete(); activeBuilders[i].lfgMsg.delete().catch(e => {
log(LT.WARN, `Failed to clean up active builder | delete | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${JSON.stringify(e)}`);
});
}
try {
const m = await sendMessage(activeBuilders[i].channelId, `<@${activeBuilders[i].userId}>, your LFG ${activeBuilders[i].editing ? "editing" : "creation"} has timed out. Please try again.`);
setTimeout(() => {
m.delete();
}, 30000);
}
catch (e) {
log(LT.WARN, `Failed to clean up active builder | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${JSON.stringify(e)}`);
}
finally {
activeBuilders.splice(i, 1);
i--;
} }
const m = await sendMessage(activeBuilders[i].channelId, `<@${activeBuilders[i].userId}>, your LFG ${activeBuilders[i].editing ? "editing" : "creation"} has timed out. Please try again.`);
activeBuilders.splice(i, 1);
setTimeout(() => {
m.delete();
}, 30000);
i--;
} }
} }
} }
@ -86,45 +102,66 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
// Send notifications // Send notifications
if (!activeLFGPosts[i].notified && activeLFGPosts[i].lfgTime < (now + tenMin)) { if (!activeLFGPosts[i].notified && activeLFGPosts[i].lfgTime < (now + tenMin)) {
log(LT.INFO, `Notifying LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid}`); log(LT.INFO, `Notifying LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid}`);
const message = await getMessage(activeLFGPosts[i].channelId, activeLFGPosts[i].messageId); try {
const lfg = message.embeds[0].fields || []; const message = await getMessage(activeLFGPosts[i].channelId, activeLFGPosts[i].messageId);
const lfgActivity = `${lfg[0].name.substr(0, lfg[0].name.length - 1)} - ${lfg[0].value}`; const lfg = message.embeds[0].fields || [];
const guildName = message.guild?.name || "unknown"; const lfgActivity = `${lfg[0].name.substr(0, lfg[0].name.length - 1)} - ${lfg[0].value}`;
const members = lfg[4].value; const guildName = message.guild?.name || "unknown";
let editMsg = ""; const members = lfg[4].value;
await members.split("\n").forEach(async m => { let editMsg = "";
const [name, tmpId] = m.split(" - <@"); members.split("\n").forEach(async m => {
const userId = BigInt(tmpId.substr(0, tmpId.length - 1)); if (m !== "None") {
editMsg += `<@${userId}>, `; const [name, tmpId] = m.split(" - <@");
await sendDirectMessage(userId, { const userId = BigInt(tmpId.substr(0, tmpId.length - 1));
embed: { editMsg += `<@${userId}>, `;
title: `Hello ${name}! You ${lfgActivity} in ${guildName} starts in less than 10 minutes.`, await sendDirectMessage(userId, {
fields: [{ embed: {
name: "Please start grouping up with the other members of this activity:", title: `Hello ${name}! You event in ${guildName} starts in less than 10 minutes.`,
value: members fields: [
}] lfg[0],
{
name: "Please start grouping up with the other members of this activity:",
value: members
}
]
}
});
} }
}); });
}); editMsg += `your ${lfgActivity} starts in less than 10 minutes.`;
editMsg += `your ${lfgActivity} starts in less than 10 minutes.`;
await message.edit({ await message.edit({
content: editMsg content: editMsg
}); });
activeLFGPosts[i].notified = true; activeLFGPosts[i].notified = true;
}
catch (err) {
log(LT.WARN, `Failed to find LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${JSON.stringify(err)}`);
activeLFGPosts.splice(i, 1);
i--;
}
} }
// Lock LFG from editing/Joining/Leaving // Lock LFG from editing/Joining/Leaving
if (!activeLFGPosts[i].locked && activeLFGPosts[i].lfgTime < now) { if (!activeLFGPosts[i].locked && activeLFGPosts[i].lfgTime < now) {
log(LT.INFO, `Locking LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid}`); log(LT.INFO, `Locking LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid}`);
const message = await getMessage(activeLFGPosts[i].channelId, activeLFGPosts[i].messageId); try {
const message = await getMessage(activeLFGPosts[i].channelId, activeLFGPosts[i].messageId);
await message.edit({ await message.edit({
components: [] components: []
}); });
activeLFGPosts[i].locked = true; activeLFGPosts[i].locked = true;
}
catch (err) {
log(LT.WARN, `Failed to find LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${JSON.stringify(err)}`);
activeLFGPosts.splice(i, 1);
i--;
}
} }
// Delete old LFG post // Delete old LFG post

View File

@ -1,11 +1,14 @@
import { import {
ActionRow, ButtonComponent, DiscordButtonStyles, EmbedField, DiscordenoMember ActionRow, ButtonComponent, DiscordButtonStyles, EmbedField, DiscordenoMember,
LT, log
} from "../deps.ts"; } from "../deps.ts";
import { JoinLeaveType } from "./lfgHandlers.d.ts"; import { JoinLeaveType } from "./lfgHandlers.d.ts";
import { BuildingLFG } from "./mod.d.ts"; import { BuildingLFG } from "./mod.d.ts";
import { LFGActivities } from "./games.ts"; import { LFGActivities } from "./games.ts";
import { determineTZ } from "./timeUtils.ts" import { determineTZ } from "./timeUtils.ts";
import { lfgStepQuestions } from "./constantCmds.ts";
export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise<BuildingLFG> => { export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise<BuildingLFG> => {
const currentLFG = (wipLFG.lfgMsg.embeds[0] || { fields: undefined }).fields || [ const currentLFG = (wipLFG.lfgMsg.embeds[0] || { fields: undefined }).fields || [
@ -47,10 +50,10 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
switch (wipLFG.step) { switch (wipLFG.step) {
case "set_game": { case "set_game": {
currentLFG[0].name = input; currentLFG[0].name = input.substr(0, 254);
if (Object.prototype.hasOwnProperty.call(LFGActivities, input)) { if (Object.prototype.hasOwnProperty.call(LFGActivities, input)) {
nextQuestion = "Please select an Activity from the list below. Depending on the game selected, these may be categories you can use to drill down to a specific activity.\n\nIf your activity is not listed, please type it out:"; nextQuestion = lfgStepQuestions.set_activity_with_button;
let tempObj = {}; let tempObj = {};
Object.entries(LFGActivities).some(e => { Object.entries(LFGActivities).some(e => {
@ -88,7 +91,7 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
} }
}); });
} else { } else {
nextQuestion = "Please type the activity name out:"; nextQuestion = lfgStepQuestions.set_activity_with_text;
} }
wipLFG.step = "set_activity"; wipLFG.step = "set_activity";
@ -111,23 +114,23 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
}); });
currentLFG[0].name = `${game}:`; currentLFG[0].name = `${game}:`;
currentLFG[0].value = input; currentLFG[0].value = input.substr(0, 1023);
if (typeof tempObj === "number") { if (typeof tempObj === "number") {
// Activity // Activity
currentLFG[4].name = `Members Joined: ${currentLFG[4].value === "None" ? 0 : currentLFG[4].value.split("\n").length}/${tempObj}`; currentLFG[4].name = `Members Joined: ${currentLFG[4].value === "None" ? 0 : currentLFG[4].value.split("\n").length}/${tempObj}`;
nextQuestion = wipLFG.editing ? "Finalizing, please wait. . ." : "Please enter the time of the activity:"; nextQuestion = wipLFG.editing ? lfgStepQuestions.set_done : lfgStepQuestions.set_time;
wipLFG.step = wipLFG.editing ? "done" : "set_time"; wipLFG.step = wipLFG.editing ? "done" : "set_time";
} else if (!tempObj) { } else if (!tempObj) {
// Custom // Custom
nextQuestion = "Please enter the max number of members for this activity:"; nextQuestion = lfgStepQuestions.set_player_cnt;
wipLFG.step = "set_player_cnt"; wipLFG.step = "set_player_cnt";
} else { } else {
// Category // Category
nextQuestion = "Please select an Activity from the list below.\n\nIf your activity is not listed, please type it out:"; nextQuestion = lfgStepQuestions.set_activity_from_category;
currentLFG[0].name = game; currentLFG[0].name = game;
@ -187,16 +190,16 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
}); });
currentLFG[0].name = `${game}:`; currentLFG[0].name = `${game}:`;
currentLFG[0].value = input; currentLFG[0].value = input.substr(0, 1023);
if (tempObj) { if (tempObj) {
currentLFG[4].name = `Members Joined: ${currentLFG[4].value === "None" ? 0 : currentLFG[4].value.split("\n").length}/${tempObj}`; currentLFG[4].name = `Members Joined: ${currentLFG[4].value === "None" ? 0 : currentLFG[4].value.split("\n").length}/${tempObj}`;
nextQuestion = wipLFG.editing ? "Finalizing, please wait. . ." : "Please enter the time of the activity:"; nextQuestion = wipLFG.editing ? lfgStepQuestions.set_done : lfgStepQuestions.set_time;
wipLFG.step = wipLFG.editing ? "done" : "set_time"; wipLFG.step = wipLFG.editing ? "done" : "set_time";
} else { } else {
nextQuestion = "Please enter the max number of members for this activity:"; nextQuestion = lfgStepQuestions.set_player_cnt;
wipLFG.step = "set_player_cnt"; wipLFG.step = "set_player_cnt";
} }
@ -206,7 +209,7 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
if (parseInt(input)) { if (parseInt(input)) {
currentLFG[4].name = `Members Joined: ${currentLFG[4].value === "None" ? 0 : currentLFG[4].value.split("\n").length}/${parseInt(input)}`; currentLFG[4].name = `Members Joined: ${currentLFG[4].value === "None" ? 0 : currentLFG[4].value.split("\n").length}/${parseInt(input)}`;
nextQuestion = wipLFG.editing ? "Finalizing, please wait. . ." : "Please enter the time of the activity:"; nextQuestion = wipLFG.editing ? lfgStepQuestions.set_done : lfgStepQuestions.set_time;
wipLFG.step = wipLFG.editing ? "done" : "set_time"; wipLFG.step = wipLFG.editing ? "done" : "set_time";
} else { } else {
@ -227,13 +230,31 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
input.split(" ").forEach(c => { input.split(" ").forEach(c => {
if (c.includes("/")) { if (c.includes("/")) {
lfgDate = c; lfgDate = c;
} else if (c.includes(":") || parseInt(c).toString() === c) {
lfgTime = c;
} else if (c.toLowerCase() === "am" || c.toLowerCase() === "pm") { } else if (c.toLowerCase() === "am" || c.toLowerCase() === "pm") {
lfgPeriod = c.toLowerCase(); lfgPeriod = c.toLowerCase();
} else if (c.toLowerCase().includes("am") || c.toLowerCase().includes("pm")) { } else if (c.toLowerCase().includes("am") || c.toLowerCase().includes("pm")) {
lfgTime = c.substr(0, c.length - 2); lfgTime = c.substr(0, c.length - 2);
lfgPeriod = c.toLowerCase().includes("am") ? "am" : "pm"; lfgPeriod = c.toLowerCase().includes("am") ? "am" : "pm";
} else if (c.includes(":")) {
lfgTime = c;
} else if (parseInt(c).toString() === (c.replace(/^0+/, '') || "0")) {
if (c.length === 4) {
if (parseInt(c) >= 1300) {
lfgTime = (parseInt(c) - 1200).toString();
lfgPeriod = "pm";
} else if (parseInt(c) >= 1200) {
lfgTime = c;
lfgPeriod = "pm";
} else {
lfgTime = c.startsWith("00") ? `12${c.substr(2)}` : c;
lfgPeriod = "am"
}
const hourLen = lfgTime.length === 4 ? 2 : 1;
lfgTime = `${lfgTime.substr(0, hourLen)}:${lfgTime.substr(hourLen)}`;
} else {
lfgTime = c;
}
} else { } else {
lfgTZ = determineTZ(c); lfgTZ = determineTZ(c);
} }
@ -254,20 +275,26 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
lfgTZ = lfgTZ.toUpperCase(); lfgTZ = lfgTZ.toUpperCase();
lfgDate = `${lfgDate.split("/")[0]}/${lfgDate.split("/")[1]}/${today.getFullYear()}`; lfgDate = `${lfgDate.split("/")[0]}/${lfgDate.split("/")[1]}/${today.getFullYear()}`;
const lfgDateTime = new Date(`${lfgTime} ${lfgPeriod} ${lfgTZ} ${lfgDate}`);
console.log(`${lfgTime} ${lfgPeriod} ${lfgTZ} ${lfgDate}`);
const lfgDateTime = new Date(`${lfgTime} ${lfgPeriod} ${lfgTZ} ${lfgDate}`);
lfgDate = `${lfgDate.split("/")[0]}/${lfgDate.split("/")[1]}`; lfgDate = `${lfgDate.split("/")[0]}/${lfgDate.split("/")[1]}`;
const lfgDateStr = `[${lfgTime} ${lfgPeriod} ${lfgTZ} ${lfgDate}](https://groupup.eanm.dev/tz#${lfgDateTime.getTime()})`; const lfgDateStr = `[${lfgTime} ${lfgPeriod} ${lfgTZ} ${lfgDate}](https://groupup.eanm.dev/tz#${lfgDateTime.getTime()})`;
currentLFG[1].name = "Start Time (Click for Conversion):"; currentLFG[1].name = "Start Time (Click for Conversion):";
currentLFG[1].value = lfgDateStr; currentLFG[1].value = lfgDateStr.substr(0, 1023);
if (isNaN(lfgDateTime.getTime())) { if (isNaN(lfgDateTime.getTime())) {
nextQuestion = `Input time "${input}" is invalid, please make sure you have the timezone set correctly.\n\nPlease enter the time of the activity:`; nextQuestion = `Input time "${input}" is invalid, please make sure you have the timezone set correctly.\n\n${lfgStepQuestions.set_time}`;
editFlag = false;
} else if (lfgDateTime.getTime() <= today.getTime()) {
nextQuestion = `Input time "${input}" is in the past, please make sure you are setting up the event to be in the future.\n\n${lfgStepQuestions.set_time}`;
editFlag = false; editFlag = false;
} else { } else {
nextQuestion = wipLFG.editing ? "Finalizing, please wait. . ." : "Please enter a description for the activity. Enter `none` to skip:"; nextQuestion = wipLFG.editing ? lfgStepQuestions.set_done : lfgStepQuestions.set_desc;
wipLFG.step = wipLFG.editing ? "done" : "set_desc"; wipLFG.step = wipLFG.editing ? "done" : "set_desc";
} }
@ -278,9 +305,9 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
input = currentLFG[0].value; input = currentLFG[0].value;
} }
currentLFG[3].value = input; currentLFG[3].value = input.substr(0, 1023);
nextQuestion = "Finalizing, please wait. . ."; nextQuestion = lfgStepQuestions.set_done;
wipLFG.step = "done"; wipLFG.step = "done";
break; break;
@ -289,18 +316,23 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
break; break;
} }
if (editFlag) { try {
wipLFG.lfgMsg = await wipLFG.lfgMsg.edit({ if (editFlag) {
embed: { wipLFG.lfgMsg = await wipLFG.lfgMsg.edit({
fields: currentLFG embed: {
} fields: currentLFG
}
});
}
wipLFG.questionMsg = await wipLFG.questionMsg.edit({
content: nextQuestion,
components: nextComponents
}); });
} }
catch (e) {
wipLFG.questionMsg = await wipLFG.questionMsg.edit({ log(LT.WARN, `Failed to edit active builder | ${wipLFG.userId}-${wipLFG.channelId} | ${JSON.stringify(e)}`);
content: nextQuestion, }
components: nextComponents
});
return wipLFG; return wipLFG;
}; };