Finished adding rest of database access classes

This commit is contained in:
WatDuhHekBro 2021-12-09 01:06:55 -06:00
parent 658348d993
commit 3b0b7bd559
No known key found for this signature in database
GPG Key ID: E128514902DF8A05
13 changed files with 198 additions and 95 deletions

View File

@ -9,6 +9,7 @@ LICENSE
# Specific to this repository # Specific to this repository
dist/ dist/
data/ data/
public/
docs/ docs/
*.md *.md
tmp/ tmp/

View File

@ -44,7 +44,8 @@ Certain variables are set via `.env` at the project root. These are for system c
- `ADMINS`: A comma-separated (with a space) list of bot admin IDs - `ADMINS`: A comma-separated (with a space) list of bot admin IDs
- `SUPPORT`: A comma-separated (with a space) list of bot support IDs - `SUPPORT`: A comma-separated (with a space) list of bot support IDs
- `WOLFRAM_API_KEY`: Used for `commands/utility/calc` - `WOLFRAM_API_KEY`: Used for `commands/utility/calc`
- `DEV`: Enables dev mode as long as it isn't a falsy value (`DEV=1` works for example) - `DEV`: Enables dev mode as long as it isn't a falsy value (`DEV=1` works for example), specific values can be checked to test certain features
- `DEV_DATABASE`: Specifies the file to use for trying out changes on a database (`DEV_DATABASE=test` writes to `data/test.db`)
# Utility Functions # Utility Functions

View File

@ -88,17 +88,16 @@ export const LeaderboardCommand = new NamedCommand({
async run({send, guild, channel, client}) { async run({send, guild, channel, client}) {
if (isAuthorized(guild, channel)) { if (isAuthorized(guild, channel)) {
const users = User.all(); const users = User.all();
const ids = Object.keys(users); users.sort((a, b) => b.money - a.money);
ids.sort((a, b) => users[b].money - users[a].money);
const fields = []; const fields = [];
for (let i = 0, limit = Math.min(10, ids.length); i < limit; i++) { for (let i = 0, limit = Math.min(10, users.length); i < limit; i++) {
const id = ids[i]; const id = users[i].id;
const user = await client.users.fetch(id); const user = await client.users.fetch(id);
fields.push({ fields.push({
name: `#${i + 1}. ${user.tag}`, name: `#${i + 1}. ${user.tag}`,
value: pluralise(users[id].money, "Mon", "s") value: pluralise(users[i].money, "Mon", "s")
}); });
} }

View File

@ -214,7 +214,7 @@ export default new NamedCommand({
any: new RestCommand({ any: new RestCommand({
async run({send, guild, args, combined}) { async run({send, guild, args, combined}) {
const role = args[0] as Role; const role = args[0] as Role;
new Guild(guild!.id).streamingRoles.set(role.id, combined); new Guild(guild!.id).setStreamingRole(role.id, combined);
send( send(
`Successfully set the category \`${combined}\` to notify \`${role.name}\`.` `Successfully set the category \`${combined}\` to notify \`${role.name}\`.`
); );
@ -229,11 +229,14 @@ export default new NamedCommand({
async run({send, guild, args}) { async run({send, guild, args}) {
const role = args[0] as Role; const role = args[0] as Role;
const guildStorage = new Guild(guild!.id); const guildStorage = new Guild(guild!.id);
const category = guildStorage.streamingRoles.get(role.id); const category = guildStorage.getStreamingRole(role.id);
delete guildStorage.streamingRoles[role.id]; if (guildStorage.removeStreamingRole(role.id)) {
send( send(
`Successfully removed the category \`${category}\` to notify \`${role.name}\`.` `Successfully removed the category \`${category}\` to notify \`${role.name}\`.`
); );
} else {
send(`Failed to remove streaming role \`${role.id}\` (\`${category}\`).`);
}
} }
}) })
}) })
@ -248,8 +251,12 @@ export default new NamedCommand({
const voiceChannel = message.member?.voice.channel; const voiceChannel = message.member?.voice.channel;
if (!voiceChannel) return send("You are not in a voice channel."); if (!voiceChannel) return send("You are not in a voice channel.");
const guildStorage = new Guild(guild!.id); const guildStorage = new Guild(guild!.id);
delete guildStorage.defaultChannelNames[voiceChannel.id];
return send(`Successfully removed the default channel name for ${voiceChannel}.`); if (guildStorage.removeDefaultChannelName(voiceChannel.id)) {
send(`Successfully removed the default channel name for ${voiceChannel}.`);
} else {
send(`Failed to remove the default channel name for ${voiceChannel}`);
}
}, },
any: new RestCommand({ any: new RestCommand({
async run({send, guild, message, combined}) { async run({send, guild, message, combined}) {
@ -262,7 +269,7 @@ export default new NamedCommand({
if (!guild!.me?.permissions.has(Permissions.FLAGS.MANAGE_CHANNELS)) if (!guild!.me?.permissions.has(Permissions.FLAGS.MANAGE_CHANNELS))
return send("I can't change channel names without the `Manage Channels` permission."); return send("I can't change channel names without the `Manage Channels` permission.");
guildStorage.defaultChannelNames.set(voiceChannel.id, newName); guildStorage.setDefaultChannelName(voiceChannel.id, newName);
return await send(`Set default channel name to "${newName}".`); return await send(`Set default channel name to "${newName}".`);
} }
}) })

View File

@ -115,7 +115,7 @@ export default new NamedCommand({
let found = false; let found = false;
// Check if it's a valid category // Check if it's a valid category
for (const [roleID, categoryName] of Object.entries(guildStorage.streamingRoles)) { for (const [roleID, categoryName] of guildStorage.getStreamingRoleEntries()) {
if (combined === categoryName) { if (combined === categoryName) {
found = true; found = true;
memberStorage.streamCategory = roleID; memberStorage.streamCategory = roleID;
@ -133,10 +133,16 @@ export default new NamedCommand({
} }
if (!found) { if (!found) {
const categories = [];
for (const [_, category] of guildStorage.getStreamingRoleEntries()) {
categories.push(category);
}
send( send(
`No valid category found by \`${combined}\`! The available categories are: \`${Object.values( `No valid category found by \`${combined}\`! The available categories are: \`${categories.join(
guildStorage.streamingRoles ", "
).join(", ")}\`` )}\``
); );
} }
} }

View File

@ -1,4 +1,4 @@
import {NamedCommand, RestCommand} from "onion-lasers"; import {Command, NamedCommand, RestCommand} from "onion-lasers";
import moment from "moment"; import moment from "moment";
import {User} from "../../lib"; import {User} from "../../lib";
import {MessageEmbed} from "discord.js"; import {MessageEmbed} from "discord.js";
@ -9,11 +9,12 @@ export default new NamedCommand({
const user = new User(author.id); const user = new User(author.id);
const embed = new MessageEmbed().setTitle(`Todo list for ${author.tag}`).setColor("BLUE"); const embed = new MessageEmbed().setTitle(`Todo list for ${author.tag}`).setColor("BLUE");
for (const timestamp in user.todoList) { for (const [id, {entry, lastModified}] of user.getTodoEntries()) {
const date = new Date(Number(timestamp));
embed.addField( embed.addField(
`${moment(date).format("LT")} ${moment(date).format("LL")} (${moment(date).fromNow()})`, `\`${id}\`: ${moment(lastModified).format("LT")} ${moment(lastModified).format("LL")} (${moment(
user.todoList[timestamp] lastModified
).fromNow()})`,
entry
); );
} }
@ -24,40 +25,29 @@ export default new NamedCommand({
run: "You need to specify a note to add.", run: "You need to specify a note to add.",
any: new RestCommand({ any: new RestCommand({
async run({send, author, combined}) { async run({send, author, combined}) {
const user = new User(author.id); new User(author.id).addTodoEntry(combined);
user.todoList[Date.now().toString()] = combined;
Storage.save();
send(`Successfully added \`${combined}\` to your todo list.`); send(`Successfully added \`${combined}\` to your todo list.`);
} }
}) })
}), }),
remove: new NamedCommand({ remove: new NamedCommand({
run: "You need to specify a note to remove.", run: "You need to specify a note to remove.",
any: new RestCommand({ number: new Command({
async run({send, author, combined}) { async run({send, author, args}) {
const user = new User(author.id); const user = new User(author.id);
let isFound = false; const success = user.removeTodoEntry(args[0]);
for (const timestamp in user.todoList) { if (success) {
const selectedNote = user.todoList[timestamp]; send(`Removed Note \`${args[0]}\` from your todo list.`);
} else {
if (selectedNote === combined) { send("That item couldn't be found.");
delete user.todoList[timestamp];
Storage.save();
isFound = true;
send(`Removed \`${combined}\` from your todo list.`);
}
} }
if (!isFound) send("That item couldn't be found.");
} }
}) })
}), }),
clear: new NamedCommand({ clear: new NamedCommand({
async run({send, author}) { async run({send, author}) {
const user = new User(author.id); new User(author.id).clearTodoEntries();
user.todoList = {};
Storage.save();
send("Cleared todo list."); send("Cleared todo list.");
} }
}) })

View File

@ -23,15 +23,23 @@ class Config {
this._systemLogsChannel = systemLogsChannel; this._systemLogsChannel = systemLogsChannel;
db.prepare("UPDATE Settings SET SystemLogsChannel = ? WHERE Tag = 'Main'").run(systemLogsChannel); db.prepare("UPDATE Settings SET SystemLogsChannel = ? WHERE Tag = 'Main'").run(systemLogsChannel);
} }
get webhooks() {
return this._webhooks; getWebhook(id: string) {
return this._webhooks.get(id);
}
getWebhookEntries() {
return this._webhooks.entries();
}
hasWebhook(id: string) {
return this._webhooks.has(id);
} }
// getWebhook, setWebhook, removeWebhook, hasWebhook, getWebhookEntries
setWebhook(id: string, token: string) { setWebhook(id: string, token: string) {
db.prepare("INSERT INTO Webhooks VALUES (?, ?)").run(id, token);
this._webhooks.set(id, token); this._webhooks.set(id, token);
db.prepare( }
"INSERT INTO Webhooks VALUES (:id, :token) ON CONFLICT (ID) DO UPDATE SET Token = :token WHERE ID = :id" removeWebhook(id: string) {
).run({id, token}); db.prepare("DELETE FROM Webhooks WHERE ID = ?").run(id);
return this._webhooks.delete(id);
} }
} }

View File

@ -109,7 +109,7 @@ export class Guild {
break; break;
} }
db.prepare(upsert("WelcomeType", "welcomeType")).run({ db.prepare(upsert("WelcomeType", "welcomeTypeInt")).run({
id: this.id, id: this.id,
welcomeTypeInt welcomeTypeInt
}); });
@ -154,13 +154,53 @@ export class Guild {
hasMessageEmbeds: +hasMessageEmbeds hasMessageEmbeds: +hasMessageEmbeds
}); });
} }
get streamingRoles() {
return this._streamingRoles; // Role ID: Category Name getStreamingRole(id: string) {
return this._streamingRoles.get(id);
} }
get defaultChannelNames() { getStreamingRoleEntries() {
return this._defaultChannelNames; // Channel ID: Channel Name return this._streamingRoles.entries();
} }
hasStreamingRole(id: string) {
return this._streamingRoles.has(id);
}
setStreamingRole(id: string, category: string) {
db.prepare("INSERT INTO StreamingRoles VALUES (?, ?, ?)").run(this.id, id, category);
this._streamingRoles.set(id, category);
}
removeStreamingRole(id: string) {
db.prepare("DELETE FROM StreamingRoles WHERE GuildID = ? AND RoleID = ?").run(this.id, id);
return this._streamingRoles.delete(id);
}
getDefaultChannelName(id: string) {
return this._defaultChannelNames.get(id);
}
getDefaultChannelNameEntries() {
return this._defaultChannelNames.entries();
}
hasDefaultChannelName(id: string) {
return this._defaultChannelNames.has(id);
}
setDefaultChannelName(id: string, name: string) {
db.prepare("INSERT INTO DefaultChannelNames VALUES (?, ?, ?)").run(this.id, id, name);
this._defaultChannelNames.set(id, name);
}
removeDefaultChannelName(id: string) {
db.prepare("DELETE FROM DefaultChannelNames WHERE GuildID = ? AND ChannelID = ?").run(this.id, id);
return this._defaultChannelNames.delete(id);
}
get autoRoles() { get autoRoles() {
return this._autoRoles; // string array of role IDs return this._autoRoles;
}
set autoRoles(autoRoles) {
this._autoRoles = autoRoles;
db.prepare("DELETE FROM AutoRoles WHERE GuildID = ?").run(this.id);
const addAutoRoles = db.prepare("INSERT INTO AutoRoles VALUES (?, ?)");
for (const roleID of autoRoles) {
addAutoRoles.run(this.id, roleID);
}
} }
} }

View File

@ -12,12 +12,12 @@ export class User {
private _timezoneOffset: number | null; // This is for the standard timezone only, not the daylight savings timezone private _timezoneOffset: number | null; // This is for the standard timezone only, not the daylight savings timezone
private _daylightSavingsRegion: "na" | "eu" | "sh" | "none"; private _daylightSavingsRegion: "na" | "eu" | "sh" | "none";
private _ecoBetInsurance: number; private _ecoBetInsurance: number;
private _todoList: Collection<number, string>; private _todoList: Collection<number, {lastModified: Date; entry: string}>;
constructor(id: string) { constructor(id: string) {
this.id = id; this.id = id;
const data = db.prepare("SELECT * FROM Users WHERE ID = ?").get(id); const data = db.prepare("SELECT * FROM Users WHERE ID = ?").get(id);
const todoList = db.prepare("SELECT Timestamp, Entry FROM TodoLists WHERE UserID = ?").all(id) ?? []; const todoList = db.prepare("SELECT ID, LastModified, Entry FROM TodoLists WHERE UserID = ?").all(id) ?? [];
if (data) { if (data) {
const {Money, LastReceived, LastMonday, TimezoneOffset, DaylightSavingsRegion, EcoBetInsurance} = data; const {Money, LastReceived, LastMonday, TimezoneOffset, DaylightSavingsRegion, EcoBetInsurance} = data;
@ -51,8 +51,11 @@ export class User {
this._todoList = new Collection(); this._todoList = new Collection();
for (const {Timestamp, Entry} of todoList) { for (const {ID, LastModified, Entry} of todoList) {
this._todoList.set(Timestamp, Entry); this._todoList.set(ID, {
entry: Entry,
lastModified: new Date(LastModified)
});
} }
} }
@ -147,8 +150,52 @@ export class User {
get todoList() { get todoList() {
return this._todoList; return this._todoList;
} }
// NOTE: Need to figure out an actual ID system
setTodoEntry(timestamp: number, entry: string) { getTodoEntry(id: number) {
db.prepare("INSERT INTO TodoLists VALUES (?, ?, ?)").run(this.id, timestamp, entry); return this._todoList.get(id);
}
getTodoEntries() {
return this._todoList.entries();
}
hasTodoEntry(id: number) {
return this._todoList.has(id);
}
addTodoEntry(entry: string) {
const lastModified = Date.now();
db.prepare("INSERT INTO TodoLists (UserID, Entry, LastModified) VALUES (?, ?, ?)").run(
this.id,
entry,
lastModified
);
const {ID} = db
.prepare("SELECT ID FROM TodoLists WHERE UserID = ? AND Entry = ? AND LastModified = ?")
.get(this.id, entry, lastModified);
this._todoList.set(ID, {
entry,
lastModified: new Date(lastModified)
});
}
setTodoEntry(id: number, entry: string): boolean {
const lastModified = Date.now();
const exists = !!db.prepare("SELECT * FROM TodoLists WHERE UserID = ? AND ID = ?").get(this.id, id);
if (exists) {
db.prepare("INSERT INTO TodoLists VALUES (?, ?, ?, ?)").run(id, this.id, entry, lastModified);
this._todoList.set(id, {
entry,
lastModified: new Date(lastModified)
});
return true;
} else {
return false;
}
}
removeTodoEntry(id: number) {
db.prepare("DELETE FROM TodoLists WHERE UserID = ? AND ID = ?").run(this.id, id);
return this._todoList.delete(id);
}
clearTodoEntries() {
db.prepare("DELETE FROM TodoLists WHERE UserID = ?").run(this.id);
this._todoList.clear();
} }
} }

View File

@ -4,14 +4,14 @@ import {Permissions} from "discord.js";
client.on("voiceStateUpdate", async (before, after) => { client.on("voiceStateUpdate", async (before, after) => {
const channel = before.channel; const channel = before.channel;
const {defaultChannelNames} = new Guild(after.guild.id); const guild = new Guild(after.guild.id);
if ( if (
channel && channel &&
channel.members.size === 0 && channel.members.size === 0 &&
defaultChannelNames.has(channel.id) && guild.hasDefaultChannelName(channel.id) &&
before.guild.me?.permissions.has(Permissions.FLAGS.MANAGE_CHANNELS) before.guild.me?.permissions.has(Permissions.FLAGS.MANAGE_CHANNELS)
) { ) {
channel.setName(defaultChannelNames.get(channel.id)!); channel.setName(guild.getDefaultChannelName(channel.id)!);
} }
}); });

View File

@ -17,7 +17,7 @@ import {join} from "path";
// Guilds: ID, Prefix (TEXT NULLABLE), WelcomeType (INT), WelcomeChannel (TEXT NULLABLE), WelcomeMessage (TEXT NULLABLE), StreamingChannel (TEXT NULLABLE), HasMessageEmbeds (BOOL) // Guilds: ID, Prefix (TEXT NULLABLE), WelcomeType (INT), WelcomeChannel (TEXT NULLABLE), WelcomeMessage (TEXT NULLABLE), StreamingChannel (TEXT NULLABLE), HasMessageEmbeds (BOOL)
// Members: UserID, GuildID, StreamCategory (TEXT NULLABLE) // Members: UserID, GuildID, StreamCategory (TEXT NULLABLE)
// Webhooks: ID, Token (TEXT) // Webhooks: ID, Token (TEXT)
// TodoLists: UserID, Timestamp (TIME), Entry (TEXT) // TodoLists: ID (INT PRIMARY KEY), UserID, Entry (TEXT), LastModified (TIME)
// StreamingRoles: GuildID, RoleID, Category (TEXT) // StreamingRoles: GuildID, RoleID, Category (TEXT)
// DefaultChannelNames: GuildID, ChannelID, Name (TEXT) // DefaultChannelNames: GuildID, ChannelID, Name (TEXT)
// AutoRoles: GuildID, RoleID // AutoRoles: GuildID, RoleID
@ -33,7 +33,7 @@ import {join} from "path";
// - Booleans (marked as BOOL) will be stored as an integer, either 0 or 1 (though it just checks for 0). // - Booleans (marked as BOOL) will be stored as an integer, either 0 or 1 (though it just checks for 0).
const DATA_FOLDER = "data"; const DATA_FOLDER = "data";
const DATABASE_FILE = join(DATA_FOLDER, "main.db"); const DATABASE_FILE = join(DATA_FOLDER, `${process.env.DEV_DATABASE ?? "main"}.db`);
// Calling migrations[2]() migrates the database from version 2 to version 3. // Calling migrations[2]() migrates the database from version 2 to version 3.
// NOTE: Once a migration is written, DO NOT change that migration or it'll break all future migrations. // NOTE: Once a migration is written, DO NOT change that migration or it'll break all future migrations.
@ -84,9 +84,10 @@ const migrations: (() => void)[] = [
Token TEXT NOT NULL Token TEXT NOT NULL
)`, )`,
`CREATE TABLE TodoLists ( `CREATE TABLE TodoLists (
ID INTEGER NOT NULL PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT,
UserID TEXT NOT NULL, UserID TEXT NOT NULL,
Timestamp INT NOT NULL, Entry TEXT NOT NULL,
Entry TEXT NOT NULL LastModified INT NOT NULL
)`, )`,
`CREATE TABLE StreamingRoles ( `CREATE TABLE StreamingRoles (
GuildID TEXT NOT NULL, GuildID TEXT NOT NULL,
@ -108,11 +109,18 @@ const migrations: (() => void)[] = [
if (hasLegacyData) { if (hasLegacyData) {
const config = JSON.parse(readFileSync(CONFIG_FILE, "utf-8")); const config = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
const {users, guilds} = JSON.parse(readFileSync(STORAGE_FILE, "utf-8")); const {users, guilds} = JSON.parse(readFileSync(STORAGE_FILE, "utf-8"));
db.prepare("INSERT INTO Settings VALUES ('Main', ?)").run(config.systemLogsChannel); db.prepare("INSERT INTO Settings VALUES ('Main', ?)").run(config.systemLogsChannel);
const addWebhooks = db.prepare("INSERT INTO Webhooks VALUES (?, ?)");
const addUsers = db.prepare("INSERT INTO Users VALUES (?, ?, ?, ?, ?, ?, ?)");
const addTodoLists = db.prepare("INSERT INTO TodoLists (UserID, Entry, LastModified) VALUES (?, ?, ?)");
const addGuilds = db.prepare("INSERT INTO Guilds VALUES (?, ?, ?, ?, ?, ?, ?)");
const addMembers = db.prepare("INSERT INTO Members VALUES (?, ?, ?)");
const addStreamingRoles = db.prepare("INSERT INTO StreamingRoles VALUES (?, ?, ?)");
const addDefaultChannelNames = db.prepare("INSERT INTO DefaultChannelNames VALUES (?, ?, ?)");
const addAutoRoles = db.prepare("INSERT INTO AutoRoles VALUES (?, ?)");
for (const [id, token] of Object.entries(config.webhooks)) { for (const [id, token] of Object.entries(config.webhooks)) {
db.prepare("INSERT INTO Webhooks VALUES (?, ?)").run(id, token); addWebhooks.run(id, token);
} }
for (const id in users) { for (const id in users) {
@ -134,19 +142,11 @@ const migrations: (() => void)[] = [
} }
} }
db.prepare("INSERT INTO Users VALUES (?, ?, ?, ?, ?, ?, ?)").run( addUsers.run(id, money, lastReceived, lastMonday, timezone, dstInfo, ecoBetInsurance);
id,
money,
lastReceived,
lastMonday,
timezone,
dstInfo,
ecoBetInsurance
);
for (const timestamp in todoList) { for (const timestamp in todoList) {
const entry = todoList[timestamp]; const entry = todoList[timestamp];
db.prepare("INSERT INTO TodoLists VALUES (?, ?, ?)").run(id, Number(timestamp), entry); addTodoLists.run(id, entry, Number(timestamp));
} }
} }
@ -174,7 +174,7 @@ const migrations: (() => void)[] = [
break; break;
} }
db.prepare("INSERT INTO Guilds VALUES (?, ?, ?, ?, ?, ?, ?)").run( addGuilds.run(
id, id,
prefix, prefix,
welcomeTypeInt, welcomeTypeInt,
@ -186,28 +186,28 @@ const migrations: (() => void)[] = [
for (const userID in members) { for (const userID in members) {
const {streamCategory} = members[userID]; const {streamCategory} = members[userID];
db.prepare("INSERT INTO Members VALUES (?, ?, ?)").run(userID, id, streamCategory); addMembers.run(userID, id, streamCategory);
} }
for (const roleID in streamingRoles) { for (const roleID in streamingRoles) {
const category = streamingRoles[roleID]; const category = streamingRoles[roleID];
db.prepare("INSERT INTO StreamingRoles VALUES (?, ?, ?)").run(id, roleID, category); addStreamingRoles.run(id, roleID, category);
} }
for (const channelID in channelNames) { for (const channelID in channelNames) {
const channelName = channelNames[channelID]; const channelName = channelNames[channelID];
db.prepare("INSERT INTO DefaultChannelNames VALUES (?, ?, ?)").run(id, channelID, channelName); addDefaultChannelNames.run(id, channelID, channelName);
} }
if (autoRoles) { if (autoRoles) {
for (const roleID of autoRoles) { for (const roleID of autoRoles) {
db.prepare("INSERT INTO AutoRoles VALUES (?, ?)").run(id, roleID); addAutoRoles.run(id, roleID);
} }
} }
} }
} }
} }
// generateSQLMigration(["UPDATE System SET Version = 2"]) // generateSQLMigration([])
]; ];
const isExistingDatabase = existsSync(DATABASE_FILE); const isExistingDatabase = existsSync(DATABASE_FILE);
@ -244,6 +244,10 @@ if (isExistingDatabase) {
if (version !== -1) { if (version !== -1) {
for (let v = version; v < migrations.length; v++) { for (let v = version; v < migrations.length; v++) {
migrations[v](); migrations[v]();
if (v >= 1) {
db.prepare("UPDATE System SET Version = ?").run(v + 1);
}
} }
} }

View File

@ -61,7 +61,8 @@ client.on("voiceStateUpdate", async (before, after) => {
// Note: isStopStreamEvent can be called twice in a row - If Discord crashes/quits while you're streaming, it'll call once with a null channel and a second time with a channel. // Note: isStopStreamEvent can be called twice in a row - If Discord crashes/quits while you're streaming, it'll call once with a null channel and a second time with a channel.
if (isStartStreamEvent || isStopStreamEvent) { if (isStartStreamEvent || isStopStreamEvent) {
const {streamingChannel, streamingRoles} = new Guild(after.guild.id); const guild = new Guild(after.guild.id);
const {streamingChannel} = guild;
if (streamingChannel) { if (streamingChannel) {
const member = after.member!; const member = after.member!;
@ -79,9 +80,9 @@ client.on("voiceStateUpdate", async (before, after) => {
const roleID = new Member(member.id, after.guild.id).streamCategory; const roleID = new Member(member.id, after.guild.id).streamCategory;
// Only continue if they set a valid category. // Only continue if they set a valid category.
if (roleID && streamingRoles.has(roleID)) { if (roleID && guild.hasStreamingRole(roleID)) {
streamNotificationPing = `<@&${roleID}>`; streamNotificationPing = `<@&${roleID}>`;
category = streamingRoles.get(roleID)!; category = guild.getStreamingRole(roleID)!;
} }
streamList.set(member.id, { streamList.set(member.id, {

View File

@ -38,7 +38,7 @@ export function deleteWebhook(urlOrID: string): boolean {
else if (ID_PATTERN.test(urlOrID)) id = ID_PATTERN.exec(urlOrID)![1]; else if (ID_PATTERN.test(urlOrID)) id = ID_PATTERN.exec(urlOrID)![1];
if (id) { if (id) {
delete config.webhooks[id]; config.removeWebhook(id);
refreshWebhookCache(); refreshWebhookCache();
} }
@ -55,14 +55,13 @@ client.on("ready", refreshWebhookCache);
export async function refreshWebhookCache(): Promise<void> { export async function refreshWebhookCache(): Promise<void> {
webhookStorage.clear(); webhookStorage.clear();
for (const [id, token] of Object.entries(Config.webhooks)) { for (const [id, token] of config.getWebhookEntries()) {
// If there are stored webhook IDs/tokens that don't work, delete those webhooks from storage. // If there are stored webhook IDs/tokens that don't work, delete those webhooks from storage.
try { try {
const webhook = await client.fetchWebhook(id, token); const webhook = await client.fetchWebhook(id, token);
webhookStorage.set(webhook.channelId, webhook); webhookStorage.set(webhook.channelId, webhook);
} catch { } catch {
delete Config.webhooks[id]; config.removeWebhook(id);
Config.save();
} }
} }
} }