Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 3 additions & 17 deletions api/db/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export async function initTables() {
name VARCHAR(255),
icon VARCHAR(255),
members INT,
cooldown INT DEFAULT 30000
cooldown INT DEFAULT 30000,
updates_enabled BOOLEAN DEFAULT FALSE,
updates_channel_id VARCHAR(255) DEFAULT NULL
)
`;
const createUsersTable = `
Expand All @@ -34,14 +36,6 @@ export async function initTables() {
)
`;
// FOREIGN KEY (guild_id) REFERENCES guilds(id)
const createUpdatesTable = `
CREATE TABLE IF NOT EXISTS updates (
guild_id VARCHAR(255) NOT NULL PRIMARY KEY,
channel_id VARCHAR(255) NOT NULL,
enabled BOOLEAN DEFAULT FALSE
)
`;
// FOREIGN KEY (guild_id) REFERENCES guilds(id)

pool.query(createGuildsTable, (err) => {
if (err) {
Expand All @@ -66,12 +60,4 @@ export async function initTables() {
console.log("Roles table created");
}
});

pool.query(createUpdatesTable, (err) => {
if (err) {
console.error("Error creating updates table:", err);
} else {
console.log("Updates table created");
}
});
}
11 changes: 6 additions & 5 deletions api/db/queries/guilds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ export interface Guild {
icon: string;
members: number;
cooldown: number;
updates_enabled: 0 | 1;
updates_channel_id: string | null;
}


export async function getGuild(guildId: string): Promise<[QueryError, null] | [null, Guild | null]> {
return new Promise((resolve, reject) => {
pool.query("SELECT * FROM guilds WHERE id = ?", [guildId], (err, results) => {
Expand All @@ -21,24 +24,22 @@ export async function getGuild(guildId: string): Promise<[QueryError, null] | [n
});
}

export async function updateGuild(guild: Guild): Promise<[QueryError | null, null] | [null, Guild[]]> {
export async function updateGuild(guild: Omit<Guild, "cooldown" | "updates_enabled" | "updates_channel_id">): Promise<[QueryError | null, null] | [null, Guild[]]> {
return new Promise((resolve, reject) => {
pool.query(
`
INSERT INTO guilds (id, name, icon, members, cooldown)
INSERT INTO guilds (id, name, icon, members)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
icon = VALUES(icon),
members = VALUES(members),
cooldown = VALUES(cooldown)
members = VALUES(members)
`,
[
guild.id,
guild.name,
guild.icon,
guild.members,
guild.cooldown
],
(err, results) => {
console.dir(results, { depth: null });
Expand Down
46 changes: 23 additions & 23 deletions api/db/queries/updates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,38 @@ export interface Updates {
enabled: boolean;
}

export async function getUpdates(guildId: string): Promise<[QueryError | null, Updates[] | null]> {
export async function enableUpdates(guildId: string): Promise<[QueryError | null, boolean]> {
return new Promise((resolve, reject) => {
pool.query("SELECT * FROM updates WHERE guild_id = ?", [guildId], (err, results) => {
if (err) {
reject([err, null]);
} else {
resolve([null, results as Updates[]]);
}
});
pool.query(
`
UPDATE guilds SET updates_enabled = TRUE WHERE id = ?
`,
[
guildId,
],
(err) => {
if (err) {
reject([err, false]);
} else {
resolve([null, true]);
}
},
);
});
}

export async function enableUpdates(guildId: string, channelId: string): Promise<[QueryError | null, true | null]> {
export async function disableUpdates(guildId: string): Promise<[QueryError | null, boolean]> {
return new Promise((resolve, reject) => {
pool.query(
`
INSERT INTO updates (guild_id, channel_id, enabled)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE
channel_id = VALUES(channel_id),
enabled = VALUES(enabled)
UPDATE guilds SET updates_enabled = FALSE WHERE id = ?
`,
[
guildId,
channelId,
true,
],
(err) => {
if (err) {
reject([err, null]);
reject([err, false]);
} else {
resolve([null, true]);
}
Expand All @@ -45,21 +47,19 @@ export async function enableUpdates(guildId: string, channelId: string): Promise
});
}

export async function disableUpdates(guildId: string): Promise<[QueryError | null, true | null]> {
export async function setUpdatesChannel(guildId: string, channelId: string | null): Promise<[QueryError | null, boolean]> {
return new Promise((resolve, reject) => {
pool.query(
`
UPDATE updates
SET enabled = ?
WHERE guild_id = ?
UPDATE guilds SET updates_channel_id = ? WHERE id = ?
`,
[
false,
channelId,
guildId,
],
(err) => {
if (err) {
reject([err, null]);
reject([err, false]);
} else {
resolve([null, true]);
}
Expand Down
31 changes: 23 additions & 8 deletions api/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express, { type NextFunction, type Request, type Response } from "express";
import cors from "cors";
import path from "path";
import { getBotInfo, getGuild, getUser, getUsers, initTables, pool, updateGuild, getUpdates, enableUpdates, disableUpdates, setCooldown } from "./db";
import { getBotInfo, getGuild, getUser, getUsers, initTables, pool, updateGuild, enableUpdates, disableUpdates, setCooldown, setUpdatesChannel } from "./db";

const app = express();
const PORT = 18103;
Expand Down Expand Up @@ -34,7 +34,6 @@ app.post("/post/:guild", authMiddleware, async (req, res) => {
name,
icon,
members,
cooldown: 30_000,
});

if (err) {
Expand All @@ -57,6 +56,7 @@ app.post("/post/:guild/:user", authMiddleware, async (req, res) => {
return res.status(500).json({ message: "Internal server error" });
}


const currentXp = result?.xp ?? 0;
const currentLevelSaved = result?.level ?? 0;
const newXp = currentXp + xpValue;
Expand Down Expand Up @@ -168,15 +168,12 @@ app.post("/admin/:action/:guild/:target", authMiddleware, async (req, res) => {
// run function to exclude target from guild
break;
case "updates":
if (target !== "enable" && target !== "disable" && target !== "get") {
if (target !== "enable" && target !== "disable" && target !== "set" && target !== "get") {
return res.status(400).json({ message: "Illegal request" });
}

switch (target) {
case "enable":
if (!extraData || !extraData.channelId) {
return res.status(400).json({ message: "Illegal request" });
}
try {
const [err, success] = await enableUpdates(guild, extraData.channelId);
if (err) {
Expand All @@ -198,13 +195,31 @@ app.post("/admin/:action/:guild/:target", authMiddleware, async (req, res) => {
} catch (err) {
return res.status(500).json({ message: "Internal server error", err });
}
case 'set':
if (!extraData || typeof extraData.channelId === "undefined") {
return res.status(400).json({ message: "Illegal request" });
}

try {
const [err, success] = await setUpdatesChannel(guild, extraData.channelId);
if (err) {
return res.status(500).json({ message: 'Internal server error', err });
} else {
return res.status(200).json(success);
}
} catch (err) {
return res.status(500).json({ message: 'Internal server error', err });
}
default:
try {
const [err, data] = await getUpdates(guild);
const [err, data] = await getGuild(guild);
if (err) {
return res.status(500).json({ message: "Internal server error", err });
}
return res.status(200).json(data);
return res.status(200).json({
enabled: ((data?.updates_enabled ?? 1) === 1),
channel: data?.updates_channel_id ?? null,
});
} catch (error) {
return res.status(500).json({ message: "Internal server error" });
}
Expand Down
83 changes: 68 additions & 15 deletions bot/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import client from '.';
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type CommandInteraction, ChannelType, type APIApplicationCommandOption, GuildMember, AttachmentBuilder, ComponentType } from 'discord.js';
import { heapStats } from 'bun:jsc';
import { getGuildLeaderboard, makeGETRequest, getRoles, removeRole, addRole, enableUpdates, disableUpdates, getCooldown, setCooldown, checkIfGuildHasUpdatesEnabled } from './utils/requestAPI';
import { getGuildLeaderboard, makeGETRequest, getRoles, removeRole, addRole, enableUpdates, disableUpdates, getCooldown, setCooldown, getUpdatesChannel, setUpdatesChannel } from './utils/requestAPI';
import convertToLevels from './utils/convertToLevels';
import quickEmbed from './utils/quickEmbed';
import { Font, RankCardBuilder } from 'canvacord';
Expand Down Expand Up @@ -161,7 +161,7 @@ const commands: Record<string, Command> = {
.setDisplayName(member.displayName)
.setAvatar(member.displayAvatarURL({ forceStatic: true, size: 4096 })) // user avatar
.setCurrentXP(xp.xp) // current xp
.setRequiredXP(xp.xp_needed_next_level) // required xp
.setRequiredXP(xp.xp + xp.xp_needed_next_level) // required xp
.setLevel(xp.level) // user level
.setRank(rank) // user rank
.setOverlay(member.user.banner ? 95 : 90) // overlay percentage. Overlay is a semi-transparent layer on top of the background
Expand Down Expand Up @@ -450,24 +450,37 @@ const commands: Record<string, Command> = {
data: {
options: [{
name: 'action',
description: 'Note that enabling is in THIS channel and will override the current updates channel!',
description: 'Select an action',
type: 3,
required: true,
choices: [
{
name: 'check',
name: 'Check',
value: 'check',
},
{
name: 'enable',
name: 'Enable',
value: 'enable',
},
{
name: 'disable',
name: 'Disable',
value: 'disable',
}
},
{
name: 'Set',
value: 'set',
},
{
name: 'Reset to Default',
value: 'reset',
},
]
},],
},{
name: 'channel',
description: 'Enter the channel ID. Required for set action.',
type: 7,
required: false,
}],
name: 'updates',
description: 'Get the latest updates on the bot!',
integration_types: [0],
Expand All @@ -494,6 +507,14 @@ const commands: Record<string, Command> = {
let data

switch (action) {
case 'enable':
success = await enableUpdates(interaction.guildId as string);
if (!success) {
await interaction.reply({ ephemeral: true, content: 'Error enabling updates for this server' }).catch(console.error);
return;
}
await interaction.reply({ ephemeral: true, content: `Updates are now enabled for this server` }).catch(console.error);
return;
case 'disable':
success = await disableUpdates(interaction.guildId as string);
if (!success) {
Expand All @@ -502,22 +523,54 @@ const commands: Record<string, Command> = {
}
await interaction.reply({ ephemeral: true, content: 'Updates are now disabled for this server' }).catch(console.error);
return;
case 'enable':
success = await enableUpdates(interaction.guildId as string, channelId as string);
case 'set':
if(!channelId) {
await interaction.reply({ ephemeral: true, content: 'ERROR: Channel was not specified!' });
return;
}
success = await setUpdatesChannel(interaction.guildId as string, channelId);
if (!success) {
await interaction.reply({ ephemeral: true, content: 'Error enabling updates for this server' }).catch(console.error);
await interaction.reply({ ephemeral: true, content: 'Error setting updates channel for this server' }).catch(console.error);
return;
}
await interaction.reply({ ephemeral: true, content: `Updates are now enabled for this server in <#${channelId}>` }).catch(console.error);
await interaction.reply({ ephemeral: true, content: `Updates channel has been set to <#${channelId}>` }).catch(console.error);
return;
case 'reset':
success = await setUpdatesChannel(interaction.guildId as string, null);
if (!success) {
await interaction.reply({ ephemeral: true, content: 'Error resetting updates channel for this server' }).catch(console.error);
return;
}
await interaction.reply({ ephemeral: true, content: `Updates channel has been reset to default` }).catch(console.error);
return
default:
data = await checkIfGuildHasUpdatesEnabled(interaction.guildId as string);
data = await getUpdatesChannel(interaction.guildId as string);
if (!data || Object.keys(data).length === 0) {
await interaction.reply({ ephemeral: true, content: 'No data found' }).catch(console.error);
return;
}
// TODO: Format in embed
await interaction.reply({ ephemeral: true, content: JSON.stringify(data, null, 2) }).catch(console.error);
await interaction.reply({
embeds: [
quickEmbed({
color: 'Blurple',
title: 'Updates',
description: 'Updates for this server',
}, interaction)
.addFields(
{
name: 'Enabled',
value: data.enabled ? 'Yes' : 'No',
inline: true,
},
{
name: 'Channel',
value: data.channel ? `<#${data.channel}>` : 'N/A',
inline: true,
},
)
],
ephemeral: true
}).catch(console.error);
return;
}
},
Expand Down
4 changes: 2 additions & 2 deletions bot/events/messageCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ client.on('messageCreate', async (message: Message) => {
if (message.author.bot) return;

const cooldownTime = (await getCooldown(message.guildId as string))?.cooldown ?? 30_000;

const cooldown = cooldowns.get(message.author.id);
if (cooldown && Date.now() - cooldown < cooldownTime) return;

const xpToGive: number = message.content.length;
const pfp: string = message.member?.displayAvatarURL() ?? message.author.displayAvatarURL()
const name: string = message.author.username;
const nickname: string = message.member?.nickname ?? message.author.globalName ?? message.author.username;
await makePOSTRequest(message.guildId as string, message.author.id, xpToGive, pfp, name, nickname);
await makePOSTRequest(message.guildId as string, message.author.id, message.channel.id, xpToGive, pfp, name, nickname);
cooldowns.set(message.author.id, Date.now());

const guildName = message.guild?.name;
Expand Down
Loading