chore: merge branch dev
to main
(#200)
Some checks failed
Publish latest version / build (push) Has been cancelled
Some checks failed
Publish latest version / build (push) Has been cancelled
Close #199 Close #198 Close #145 Close #196 Close #184 Close #187 Close #57 Reviewed-on: #200 Co-authored-by: Mylloon <kennel.anri@tutanota.com> Co-committed-by: Mylloon <kennel.anri@tutanota.com>
This commit is contained in:
parent
82e2f5a209
commit
10f5bf65b3
27 changed files with 729 additions and 509 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -11,7 +11,7 @@ docker-compose.yml
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
# Databse
|
# Databse
|
||||||
*.sqlite3
|
*.sqlite3*
|
||||||
|
|
||||||
# Debug file
|
# Debug file
|
||||||
src/events/player/debug.ts
|
src/events/player/debug.ts
|
||||||
|
|
609
package-lock.json
generated
609
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
@ -21,21 +21,19 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discord-player/extractor": "^4.5.1",
|
"@discord-player/extractor": "^4.5.1",
|
||||||
"@discordjs/rest": "^2.4.0",
|
"@discordjs/rest": "^2.4.0",
|
||||||
"@types/sqlite3": "^3.1.11",
|
|
||||||
"@types/uuid": "^10.0.0",
|
|
||||||
"discord-player": "^6.7.1",
|
"discord-player": "^6.7.1",
|
||||||
"discord-player-youtubei": "^1.3.2",
|
"discord-player-youtubei": "^1.3.4",
|
||||||
"discord.js": "^14.16.3",
|
"discord.js": "^14.16.3",
|
||||||
"mediaplex": "^0.0.9",
|
"mediaplex": "^0.0.9",
|
||||||
"moment-timezone": "^0.5.46",
|
"moment-timezone": "^0.5.46",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^11.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "~29.5.13",
|
"@types/jest": "~29.5.14",
|
||||||
"@typescript-eslint/eslint-plugin": "~8.8.1",
|
"@typescript-eslint/eslint-plugin": "~8.12.2",
|
||||||
"@typescript-eslint/parser": "~8.8.1",
|
"@typescript-eslint/parser": "~8.12.2",
|
||||||
"dotenv": "~16.4.5",
|
"dotenv": "~16.4.5",
|
||||||
"jest": "~29.7.0",
|
"jest": "~29.7.0",
|
||||||
"prettier-eslint": "~16.3.0",
|
"prettier-eslint": "~16.3.0",
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
import { ChatInputCommandInteraction, Client, Colors, EmbedBuilder } from "discord.js";
|
import {
|
||||||
|
ApplicationCommandOptionType,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
Client,
|
||||||
|
Colors,
|
||||||
|
EmbedBuilder,
|
||||||
|
} from "discord.js";
|
||||||
import "../../modules/string";
|
import "../../modules/string";
|
||||||
import { getLocale, getLocalizations } from "../../utils/locales";
|
import { getLocale, getLocalizations } from "../../utils/locales";
|
||||||
import { getFilename } from "../../utils/misc";
|
import { getFilename } from "../../utils/misc";
|
||||||
|
import {
|
||||||
|
goodDescription,
|
||||||
|
goodName,
|
||||||
|
NameNotLocalized,
|
||||||
|
SubnameNotLocalized,
|
||||||
|
} from "../../utils/commands/help";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
scope: () => [],
|
scope: () => [],
|
||||||
|
@ -47,16 +59,35 @@ export default {
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
// Load all the command per categories
|
// Load all the command per categories
|
||||||
// TODO: Check if the command exist in the context (guild)
|
|
||||||
// TODO: List subcommands too
|
|
||||||
// https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/issues/47
|
|
||||||
client.commands.categories.forEach((commands_name, category) => {
|
client.commands.categories.forEach((commands_name, category) => {
|
||||||
const commands = commands_name.reduce((data, command_name) => {
|
// Check if the command exist in the context (guild)
|
||||||
return data + `\`/${command_name}\`, `;
|
commands_name = commands_name.filter((command) => {
|
||||||
}, "");
|
const scope = client.commands.list.get(command)?.scope();
|
||||||
|
return scope!.length === 0 || scope?.find((v) => v === interaction.guildId) !== undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add subcommands
|
||||||
|
const all_commands: string[] = [];
|
||||||
|
commands_name.forEach((command) => {
|
||||||
|
const data = client.commands.list.get(command)?.data;
|
||||||
|
const name = goodName(data!, interaction.locale);
|
||||||
|
all_commands.push(name);
|
||||||
|
|
||||||
|
data
|
||||||
|
?.toJSON()
|
||||||
|
.options?.filter((option) => option.type === ApplicationCommandOptionType.Subcommand)
|
||||||
|
.forEach((subcommand) =>
|
||||||
|
all_commands.push(name + " " + goodName(subcommand, interaction.locale)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const commands = all_commands.reduce(
|
||||||
|
(data, command_name) => data + `\`/${command_name}\`, `,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
name: category.capitalize() + ` (${commands_name.length})`,
|
name: category.capitalize() + ` (${all_commands.length})`,
|
||||||
value: commands.slice(0, -2),
|
value: commands.slice(0, -2),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -73,30 +104,49 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a command is specified
|
const error = `${loc.get("c_help3")} \`${desired_command}\``;
|
||||||
// TODO: Check if the command exist in the context (guild)
|
|
||||||
// https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/issues/187
|
const [possible_command, possible_subcommand] = desired_command.split(" ");
|
||||||
const command = client.commands.list.get(desired_command);
|
|
||||||
|
const command = NameNotLocalized(client, possible_command);
|
||||||
if (!command) {
|
if (!command) {
|
||||||
// Command don't exist
|
return interaction.reply({ content: error, ephemeral: true });
|
||||||
return interaction.reply({
|
|
||||||
content: `${loc.get("c_help3")} \`${desired_command}\``,
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scope = client.commands.list.get(command.name)?.scope();
|
||||||
|
if (scope!.length > 0 && scope?.find((id) => id === interaction.guildId) === undefined) {
|
||||||
|
// Command not available for the current guild
|
||||||
|
return interaction.reply({ content: error, ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
let subcommand = undefined;
|
||||||
|
if (possible_subcommand) {
|
||||||
|
subcommand = SubnameNotLocalized(command, possible_subcommand);
|
||||||
|
} else {
|
||||||
|
subcommand = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!command || subcommand === undefined) {
|
||||||
|
// Sub/Command don't exist
|
||||||
|
return interaction.reply({ content: error, ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads the data according to the user's locals
|
||||||
|
const requestedName =
|
||||||
|
goodName(command, interaction.locale) +
|
||||||
|
(subcommand !== null ? " " + goodName(subcommand, interaction.locale) : "");
|
||||||
|
const requestedDesc = goodDescription(
|
||||||
|
subcommand !== null ? subcommand : command,
|
||||||
|
interaction.locale,
|
||||||
|
);
|
||||||
|
|
||||||
// Send information about the command
|
// Send information about the command
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
embeds: [
|
embeds: [
|
||||||
new EmbedBuilder()
|
new EmbedBuilder()
|
||||||
.setColor(Colors.Blurple)
|
.setColor(Colors.Blurple)
|
||||||
.setTitle("`/" + command.data.name + "`")
|
.setTitle("`/" + requestedName + "`")
|
||||||
.setDescription(
|
.setDescription(requestedDesc),
|
||||||
// Loads the description
|
|
||||||
// according to the user's locals
|
|
||||||
command.data.description_localizations?.[interaction.locale] ??
|
|
||||||
command.data.description,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
import { SlashCommandBuilder } from "@discordjs/builders";
|
||||||
import { useMainPlayer, useQueue } from "discord-player";
|
import { useMainPlayer, useQueue } from "discord-player";
|
||||||
import { ChatInputCommandInteraction, Client, EmbedBuilder } from "discord.js";
|
import { ChatInputCommandInteraction, Client, EmbedBuilder, Message } from "discord.js";
|
||||||
import { getLocale, getLocalizations } from "../../utils/locales";
|
import { getLocale, getLocalizations } from "../../utils/locales";
|
||||||
import { getFilename } from "../../utils/misc";
|
import { getFilename } from "../../utils/misc";
|
||||||
|
import { discord_limit_message } from "../../utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
scope: () => [],
|
scope: () => [],
|
||||||
|
@ -57,7 +58,7 @@ export default {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Synced
|
// Synced start
|
||||||
.addSubcommand((subcommand) =>
|
.addSubcommand((subcommand) =>
|
||||||
subcommand
|
subcommand
|
||||||
.setName(loc_default.get(`c_${filename}_sub3_name`)!.toLowerCase())
|
.setName(loc_default.get(`c_${filename}_sub3_name`)!.toLowerCase())
|
||||||
|
@ -65,6 +66,15 @@ export default {
|
||||||
.setNameLocalizations(getLocalizations(client, `c_${filename}_sub3_name`, true))
|
.setNameLocalizations(getLocalizations(client, `c_${filename}_sub3_name`, true))
|
||||||
.setDescriptionLocalizations(getLocalizations(client, `c_${filename}_sub3_desc`)),
|
.setDescriptionLocalizations(getLocalizations(client, `c_${filename}_sub3_desc`)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Synced stop
|
||||||
|
.addSubcommand((subcommand) =>
|
||||||
|
subcommand
|
||||||
|
.setName(loc_default.get(`c_${filename}_sub4_name`)!.toLowerCase())
|
||||||
|
.setDescription(loc_default.get(`c_${filename}_sub4_desc`)!)
|
||||||
|
.setNameLocalizations(getLocalizations(client, `c_${filename}_sub4_name`, true))
|
||||||
|
.setDescriptionLocalizations(getLocalizations(client, `c_${filename}_sub4_desc`)),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -123,30 +133,69 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load lyrics
|
// Load lyrics
|
||||||
const syncedLyrics = queue.syncedLyrics(data[0]);
|
if (queue.syncedLyricsMemory !== undefined) {
|
||||||
|
return await interaction.followUp(loc.get("c_lyrics9"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncedLyrics = queue.syncedLyrics(data[0]);
|
||||||
|
queue.syncedLyricsMemory = syncedLyrics;
|
||||||
|
|
||||||
|
let message: Message;
|
||||||
syncedLyrics?.onChange(async (lyrics) => {
|
syncedLyrics?.onChange(async (lyrics) => {
|
||||||
const content = `[${data[0].trackName}]: ${lyrics}`;
|
|
||||||
if (interaction.channel?.isSendable()) {
|
if (interaction.channel?.isSendable()) {
|
||||||
await interaction.channel?.send({
|
if (message) {
|
||||||
content,
|
const payload = message.cleanContent + "\n" + lyrics;
|
||||||
});
|
if (payload.length < discord_limit_message) {
|
||||||
|
message.edit(payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message = await interaction.channel?.send(
|
||||||
|
(message ? loc.get("c_lyrics6") + " " : "") +
|
||||||
|
`${data[0].artistName} : **${data[0].trackName}**\n\n` +
|
||||||
|
lyrics,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await interaction.followUp({
|
await interaction.followUp(loc.get("c_lyrics5"));
|
||||||
content,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Live update
|
// Live update
|
||||||
syncedLyrics.subscribe();
|
syncedLyrics.subscribe();
|
||||||
|
|
||||||
|
syncedLyrics.onUnsubscribe(() => {
|
||||||
|
queue.syncedLyricsMemory = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
return await interaction.followUp({
|
return await interaction.followUp({
|
||||||
content: `🎤 | ${loc.get("c_lyrics4")}`,
|
content: `🎤 | ${loc.get("c_lyrics4")}`,
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
interaction.options.getSubcommand() ===
|
||||||
|
loc_default?.get(`c_${filename}_sub4_name`)?.toLowerCase()
|
||||||
|
) {
|
||||||
|
if (queue === null) {
|
||||||
|
return await interaction.followUp(`❌ | ${loc.get("c_lyrics1")}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data === null || !data[0] || !data[0].syncedLyrics) {
|
||||||
|
return await interaction.followUp(
|
||||||
|
`❌ | ${loc.get("c_lyrics3")} \`${queue.currentTrack?.cleanTitle}\``,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load lyrics
|
||||||
|
if (queue.syncedLyricsMemory !== undefined && queue.syncedLyricsMemory.isSubscribed()) {
|
||||||
|
queue.syncedLyricsMemory.unsubscribe();
|
||||||
|
return await interaction.followUp(loc.get("c_lyrics7"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await interaction.followUp(loc.get("c_lyrics8"));
|
||||||
|
}
|
||||||
|
|
||||||
if (data && data.length > 0 && data[0].plainLyrics !== null) {
|
if (data && data.length > 0 && data[0].plainLyrics !== null) {
|
||||||
const title = data[0];
|
const title = data[0];
|
||||||
const limit_desc = 4096;
|
const limit_desc = 4096;
|
||||||
|
|
|
@ -9,6 +9,11 @@ import {
|
||||||
import { getLocale, getLocalizations } from "../../utils/locales";
|
import { getLocale, getLocalizations } from "../../utils/locales";
|
||||||
import { Metadata } from "../../utils/metadata";
|
import { Metadata } from "../../utils/metadata";
|
||||||
import { getFilename } from "../../utils/misc";
|
import { getFilename } from "../../utils/misc";
|
||||||
|
import {
|
||||||
|
discord_limit_autocompletion_list_length,
|
||||||
|
discord_limit_autocompletion_value_length,
|
||||||
|
} from "../../utils/constants";
|
||||||
|
import { timeToString } from "../../utils/time";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
scope: () => [],
|
scope: () => [],
|
||||||
|
@ -175,12 +180,29 @@ export default {
|
||||||
queue.node.play();
|
queue.node.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When added to an existing queue (size of queue > 0):
|
const positionEstimation = () => {
|
||||||
// - Add position in queue
|
const pos = queue.node.getTrackPosition(result.tracks[0]) + 1;
|
||||||
// - Add estimated time until playing
|
|
||||||
// https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/issues/184
|
if (pos === 0) {
|
||||||
|
return loc.get("c_play_sub2_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
const estimation = timeToString(
|
||||||
|
[queue.currentTrack, ...queue.tracks.toArray()]
|
||||||
|
.filter((t) => t !== null)
|
||||||
|
.slice(0, pos)
|
||||||
|
.reduce((total, t) => {
|
||||||
|
if (total === 0) {
|
||||||
|
return queue.dispatcher ? t.durationMS - queue.dispatcher.streamTime : t.durationMS;
|
||||||
|
}
|
||||||
|
return total + t.durationMS;
|
||||||
|
}, 0),
|
||||||
|
);
|
||||||
|
return `${loc.get("c_play10")} ${pos} (${loc.get("c_play11")} ≈${estimation})`;
|
||||||
|
};
|
||||||
|
|
||||||
return await interaction.followUp({
|
return await interaction.followUp({
|
||||||
content: `⏱️ | \`${title}\` ${loc.get("c_play5")}.`,
|
content: `⏱️ | \`${title}\` ${loc.get("c_play5")}, ${loc.get("c_play12")} ${positionEstimation()}.`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -192,7 +214,10 @@ export default {
|
||||||
const player = useMainPlayer();
|
const player = useMainPlayer();
|
||||||
const query = interaction.options.getString(loc_default!.get(`c_${filename}_opt1_name`)!, true);
|
const query = interaction.options.getString(loc_default!.get(`c_${filename}_opt1_name`)!, true);
|
||||||
|
|
||||||
const limit_value_discord = 100;
|
const limit_value_discord = discord_limit_autocompletion_value_length;
|
||||||
|
const limit_element_discord = discord_limit_autocompletion_list_length;
|
||||||
|
|
||||||
|
const query_discord = query.slice(0, limit_value_discord);
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
/* Since Discord wanna receive a response within 3 secs and results is async
|
/* Since Discord wanna receive a response within 3 secs and results is async
|
||||||
|
@ -208,7 +233,7 @@ export default {
|
||||||
|
|
||||||
/* Create a race between a timeout and the search
|
/* Create a race between a timeout and the search
|
||||||
* At the end, Discord will always receive a response */
|
* At the end, Discord will always receive a response */
|
||||||
let tracks = await Promise.race([
|
const tracks = await Promise.race([
|
||||||
delay,
|
delay,
|
||||||
player.search(query, {
|
player.search(query, {
|
||||||
requestedBy: interaction.user,
|
requestedBy: interaction.user,
|
||||||
|
@ -225,23 +250,18 @@ export default {
|
||||||
|
|
||||||
// If tracks found
|
// If tracks found
|
||||||
if (tracks.length > 0) {
|
if (tracks.length > 0) {
|
||||||
if (tracks.length > 25) {
|
const payload = tracks
|
||||||
tracks = tracks
|
// Assure that URL is under the limit of Discord
|
||||||
// Assure that URL is under the limit of Discord
|
.filter((v) => v.url.length < limit_value_discord)
|
||||||
.filter((v) => v.url.length < limit_value_discord)
|
// Slice the list to respect the limit of Discord
|
||||||
// Slice the list if needed to the 25 first results
|
.slice(0, limit_element_discord - 1)
|
||||||
.slice(0, 25);
|
.map((t) => {
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a list of songs with their title and author
|
|
||||||
return interaction.respond(
|
|
||||||
tracks.map((t) => {
|
|
||||||
let title = t.title;
|
let title = t.title;
|
||||||
let author = t.author;
|
let author = t.author;
|
||||||
let name = `${title} • ${author}`;
|
let name = `${title} • ${author}`;
|
||||||
|
|
||||||
// Slice returned data if needed to not exceed the length limit (100)
|
// Slice returned data if needed to not exceed the length limit
|
||||||
if (name.length > 100) {
|
if (name.length > limit_value_discord) {
|
||||||
const newTitle = title.substring(0, 40);
|
const newTitle = title.substring(0, 40);
|
||||||
if (title.length != newTitle.length) {
|
if (title.length != newTitle.length) {
|
||||||
title = `${newTitle}...`;
|
title = `${newTitle}...`;
|
||||||
|
@ -257,12 +277,18 @@ export default {
|
||||||
name,
|
name,
|
||||||
value: t.url,
|
value: t.url,
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
|
payload.unshift({
|
||||||
|
name: query_discord,
|
||||||
|
value: query_discord,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns a list of songs with their title and author
|
||||||
|
return interaction.respond(payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return interaction.respond([
|
|
||||||
{ name: loc.get("c_play9"), value: query.slice(0, limit_value_discord) },
|
return interaction.respond([{ name: loc.get("c_play9"), value: query_discord }]);
|
||||||
]);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
setTimeoutReminder,
|
setTimeoutReminder,
|
||||||
updateReminder,
|
updateReminder,
|
||||||
} from "../../utils/commands/reminder";
|
} from "../../utils/commands/reminder";
|
||||||
|
import { readSQL } from "../../utils/db";
|
||||||
|
|
||||||
export const once = true;
|
export const once = true;
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ export default async (client: Client) => {
|
||||||
// Restart all the timeout about reminders here
|
// Restart all the timeout about reminders here
|
||||||
new Promise((ok, ko) => {
|
new Promise((ok, ko) => {
|
||||||
// Fetch all reminders
|
// Fetch all reminders
|
||||||
client.db.all("SELECT * FROM reminder", [], (err, row) => {
|
client.db.all(readSQL("reminder/select"), [], (err, row) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
ko(err);
|
ko(err);
|
||||||
}
|
}
|
||||||
|
|
10
src/load.ts
10
src/load.ts
|
@ -61,11 +61,11 @@ export const run = async (isDev: boolean) => {
|
||||||
console.log(logStart(client_name, true));
|
console.log(logStart(client_name, true));
|
||||||
console.log(`Botanique "${client.user?.username}" v${client.config.version} started!`);
|
console.log(`Botanique "${client.user?.username}" v${client.config.version} started!`);
|
||||||
|
|
||||||
// ^C
|
// Handle quit
|
||||||
process.on("SIGINT", () => quit(client));
|
process.on("exit", () => quit(client));
|
||||||
|
process.on("SIGHUP", () => process.exit(128 + 1));
|
||||||
// Container force closed
|
process.on("SIGINT", () => process.exit(128 + 2));
|
||||||
process.on("SIGTERM", () => quit(client));
|
process.on("SIGTERM", () => process.exit(128 + 15));
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
|
@ -96,6 +96,9 @@
|
||||||
"c_play7": "Currently playing",
|
"c_play7": "Currently playing",
|
||||||
"c_play8": "Asked by",
|
"c_play8": "Asked by",
|
||||||
"c_play9": "No results were found",
|
"c_play9": "No results were found",
|
||||||
|
"c_play10": "in position",
|
||||||
|
"c_play11": "estimation",
|
||||||
|
"c_play12": "play",
|
||||||
|
|
||||||
"c_stop_name": "stop",
|
"c_stop_name": "stop",
|
||||||
"c_stop_desc": "Stop the music",
|
"c_stop_desc": "Stop the music",
|
||||||
|
@ -148,12 +151,19 @@
|
||||||
"c_lyrics_sub2_desc": "Search for romanized lyrics (e.g., hangul → Latin)",
|
"c_lyrics_sub2_desc": "Search for romanized lyrics (e.g., hangul → Latin)",
|
||||||
"c_lyrics_sub3_name": "synced",
|
"c_lyrics_sub3_name": "synced",
|
||||||
"c_lyrics_sub3_desc": "Synchronized lyrics search (updates in live)",
|
"c_lyrics_sub3_desc": "Synchronized lyrics search (updates in live)",
|
||||||
|
"c_lyrics_sub4_name": "stop-synced",
|
||||||
|
"c_lyrics_sub4_desc": "Stop Synchronized lyrics",
|
||||||
"c_lyrics_opt1_name": "song",
|
"c_lyrics_opt1_name": "song",
|
||||||
"c_lyrics_opt1_desc": "Wanted song",
|
"c_lyrics_opt1_desc": "Wanted song",
|
||||||
"c_lyrics1": "The bot is not playing anything at the moment, and no songs are specified.",
|
"c_lyrics1": "The bot is not playing anything at the moment, and no songs are specified.",
|
||||||
"c_lyrics2": "Unable to find the lyrics for",
|
"c_lyrics2": "Unable to find the lyrics for",
|
||||||
"c_lyrics3": "Unable to find synchronized lyrics for",
|
"c_lyrics3": "Unable to find synchronized lyrics for",
|
||||||
"c_lyrics4": "It's karaoke time!",
|
"c_lyrics4": "It's karaoke time!",
|
||||||
|
"c_lyrics5": "Unable to post the lyrics here.",
|
||||||
|
"c_lyrics6": "More of :",
|
||||||
|
"c_lyrics7": "Stop synchronized lyrics.",
|
||||||
|
"c_lyrics8": "No synchronized lyrics currently posted.",
|
||||||
|
"c_lyrics9": "Synchronized lyrics currently posted.",
|
||||||
|
|
||||||
"c_repeat_name": "repeat",
|
"c_repeat_name": "repeat",
|
||||||
"c_repeat_desc": "Command for the type of music repetition",
|
"c_repeat_desc": "Command for the type of music repetition",
|
||||||
|
|
|
@ -96,6 +96,9 @@
|
||||||
"c_play7": "Joue actuellement",
|
"c_play7": "Joue actuellement",
|
||||||
"c_play8": "Demandé par",
|
"c_play8": "Demandé par",
|
||||||
"c_play9": "Aucun résultat trouvé",
|
"c_play9": "Aucun résultat trouvé",
|
||||||
|
"c_play10": "en position",
|
||||||
|
"c_play11": "estimation",
|
||||||
|
"c_play12": "joue",
|
||||||
|
|
||||||
"c_stop_name": "stop",
|
"c_stop_name": "stop",
|
||||||
"c_stop_desc": "Stop la musique",
|
"c_stop_desc": "Stop la musique",
|
||||||
|
@ -148,12 +151,19 @@
|
||||||
"c_lyrics_sub2_desc": "Recherche de paroles romanisées (ex: hangul → latin)",
|
"c_lyrics_sub2_desc": "Recherche de paroles romanisées (ex: hangul → latin)",
|
||||||
"c_lyrics_sub3_name": "synced",
|
"c_lyrics_sub3_name": "synced",
|
||||||
"c_lyrics_sub3_desc": "Recherche de paroles synchronisées (se mettent à jour avec la chanson en direct)",
|
"c_lyrics_sub3_desc": "Recherche de paroles synchronisées (se mettent à jour avec la chanson en direct)",
|
||||||
|
"c_lyrics_sub4_name": "stop-synced",
|
||||||
|
"c_lyrics_sub4_desc": "Arrête les paroles synchronisées",
|
||||||
"c_lyrics_opt1_name": "chanson",
|
"c_lyrics_opt1_name": "chanson",
|
||||||
"c_lyrics_opt1_desc": "Chanson recherchée",
|
"c_lyrics_opt1_desc": "Chanson recherchée",
|
||||||
"c_lyrics1": "Le bot ne joue rien en ce moment et aucune chanson n'est renseignée.",
|
"c_lyrics1": "Le bot ne joue rien en ce moment et aucune chanson n'est renseignée.",
|
||||||
"c_lyrics2": "Impossible de trouver les paroles pour",
|
"c_lyrics2": "Impossible de trouver les paroles pour",
|
||||||
"c_lyrics3": "Impossible de trouver les paroles synchronisées pour",
|
"c_lyrics3": "Impossible de trouver les paroles synchronisées pour",
|
||||||
"c_lyrics4": "C'est parti !",
|
"c_lyrics4": "C'est parti !",
|
||||||
|
"c_lyrics5": "Impossible de poster les paroles ici.",
|
||||||
|
"c_lyrics6": "Suite de :",
|
||||||
|
"c_lyrics7": "Arrêt des paroles synchronisées.",
|
||||||
|
"c_lyrics8": "Pas de paroles synchronisées en cours.",
|
||||||
|
"c_lyrics9": "Paroles synchronisées déjà en cours.",
|
||||||
|
|
||||||
"c_repeat_name": "repeat",
|
"c_repeat_name": "repeat",
|
||||||
"c_repeat_desc": "Commande relative à la répétition des musiques",
|
"c_repeat_desc": "Commande relative à la répétition des musiques",
|
||||||
|
|
|
@ -75,6 +75,8 @@ declare module "discord.js" {
|
||||||
string,
|
string,
|
||||||
/** Command itself */
|
/** Command itself */
|
||||||
{
|
{
|
||||||
|
/** Guilds where the command is active */
|
||||||
|
scope: () => string[];
|
||||||
/** Data about the command */
|
/** Data about the command */
|
||||||
data: SlashCommandBuilder;
|
data: SlashCommandBuilder;
|
||||||
/** How the command interact */
|
/** How the command interact */
|
||||||
|
|
12
src/modules/player.ts
Normal file
12
src/modules/player.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare module "discord-player" {
|
||||||
|
export interface GuildQueue {
|
||||||
|
syncedLyricsMemory:
|
||||||
|
| {
|
||||||
|
isSubscribed: () => unknown;
|
||||||
|
unsubscribe: () => unknown;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
}
|
||||||
|
}
|
13
src/sql/init.sql
Normal file
13
src/sql/init.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS
|
||||||
|
reminder (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
data TEXT,
|
||||||
|
expiration_date TEXT,
|
||||||
|
option_id INTEGER,
|
||||||
|
channel_id TEXT,
|
||||||
|
creation_date TEXT,
|
||||||
|
user_id TEXT,
|
||||||
|
guild_id TEXT,
|
||||||
|
locale TEXT,
|
||||||
|
timeout_id TEXT
|
||||||
|
);
|
14
src/sql/reminder/add.sql
Normal file
14
src/sql/reminder/add.sql
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
INSERT INTO
|
||||||
|
reminder (
|
||||||
|
data,
|
||||||
|
expiration_date,
|
||||||
|
option_id,
|
||||||
|
channel_id,
|
||||||
|
creation_date,
|
||||||
|
user_id,
|
||||||
|
guild_id,
|
||||||
|
locale,
|
||||||
|
timeout_id
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, ?, ?, ?, ?, ?);
|
13
src/sql/reminder/find.sql
Normal file
13
src/sql/reminder/find.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
SELECT
|
||||||
|
data,
|
||||||
|
creation_date,
|
||||||
|
expiration_date,
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
reminder
|
||||||
|
WHERE
|
||||||
|
user_id = ?
|
||||||
|
AND (
|
||||||
|
guild_id = ?
|
||||||
|
OR guild_id = 0
|
||||||
|
)
|
6
src/sql/reminder/findById.sql
Normal file
6
src/sql/reminder/findById.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
reminder
|
||||||
|
WHERE
|
||||||
|
id = ?
|
14
src/sql/reminder/ownership_check.sql
Normal file
14
src/sql/reminder/ownership_check.sql
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
SELECT
|
||||||
|
EXISTS (
|
||||||
|
SELECT
|
||||||
|
1
|
||||||
|
FROM
|
||||||
|
reminder
|
||||||
|
WHERE
|
||||||
|
id = ?
|
||||||
|
AND user_id = ?
|
||||||
|
AND (
|
||||||
|
guild_id = ?
|
||||||
|
OR guild_id = 0
|
||||||
|
)
|
||||||
|
)
|
4
src/sql/reminder/remove.sql
Normal file
4
src/sql/reminder/remove.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
DELETE FROM reminder
|
||||||
|
WHERE
|
||||||
|
creation_date = ?
|
||||||
|
AND user_id = ?
|
4
src/sql/reminder/select.sql
Normal file
4
src/sql/reminder/select.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
reminder
|
13
src/sql/reminder/update.sql
Normal file
13
src/sql/reminder/update.sql
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
UPDATE reminder
|
||||||
|
SET
|
||||||
|
data = ?,
|
||||||
|
expiration_date = ?,
|
||||||
|
option_id = ?,
|
||||||
|
channel_id = ?,
|
||||||
|
creation_date = ?,
|
||||||
|
user_id = ?,
|
||||||
|
guild_id = ?,
|
||||||
|
locale = ?,
|
||||||
|
timeout_id = ?
|
||||||
|
WHERE
|
||||||
|
ID = ?
|
|
@ -5,6 +5,7 @@ import { Database } from "sqlite3";
|
||||||
import "../modules/client";
|
import "../modules/client";
|
||||||
import { loadLocales } from "./locales";
|
import { loadLocales } from "./locales";
|
||||||
import { YoutubeiExtractor } from "discord-player-youtubei";
|
import { YoutubeiExtractor } from "discord-player-youtubei";
|
||||||
|
import { readSQL } from "./db";
|
||||||
|
|
||||||
/** Creation of the client and definition of its properties */
|
/** Creation of the client and definition of its properties */
|
||||||
export default async (isDev: boolean) => {
|
export default async (isDev: boolean) => {
|
||||||
|
@ -59,7 +60,7 @@ export default async (isDev: boolean) => {
|
||||||
|
|
||||||
client.db = new Database(`${process.env.DOCKERIZED === "1" ? "/config" : "./config"}/db.sqlite3`);
|
client.db = new Database(`${process.env.DOCKERIZED === "1" ? "/config" : "./config"}/db.sqlite3`);
|
||||||
|
|
||||||
initDatabase(client.db);
|
client.db.run(readSQL("init"));
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
@ -75,25 +76,3 @@ export const quit = (client: Client) => {
|
||||||
// Close client
|
// Close client
|
||||||
client.destroy();
|
client.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Initalize the database
|
|
||||||
* @param db Database
|
|
||||||
*/
|
|
||||||
const initDatabase = (db: Database) => {
|
|
||||||
// Table for reminders
|
|
||||||
db.run(
|
|
||||||
"CREATE TABLE IF NOT EXISTS reminder ( \
|
|
||||||
id INTEGER PRIMARY KEY, \
|
|
||||||
data TEXT, \
|
|
||||||
expiration_date TEXT, \
|
|
||||||
option_id INTEGER, \
|
|
||||||
channel_id TEXT, \
|
|
||||||
creation_date TEXT, \
|
|
||||||
user_id TEXT, \
|
|
||||||
guild_id TEXT, \
|
|
||||||
locale TEXT, \
|
|
||||||
timeout_id TEXT \
|
|
||||||
);",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
73
src/utils/commands/help.ts
Normal file
73
src/utils/commands/help.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import {
|
||||||
|
APIApplicationCommandSubcommandOption,
|
||||||
|
ApplicationCommandOptionType,
|
||||||
|
Client,
|
||||||
|
Locale,
|
||||||
|
SlashCommandBuilder,
|
||||||
|
} from "discord.js";
|
||||||
|
|
||||||
|
type Data = SlashCommandBuilder | APIApplicationCommandSubcommandOption;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the name of the command, trying to get the correct locale
|
||||||
|
* @param data Command data
|
||||||
|
* @param locale Locale wanted
|
||||||
|
* @returns Command's name
|
||||||
|
*/
|
||||||
|
export const goodName = (data: Data, locale: Locale) =>
|
||||||
|
data.name_localizations?.[locale] ?? data.name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the description of the command, trying to get the correct locale
|
||||||
|
* @param data Command data
|
||||||
|
* @param locale Locale wanted
|
||||||
|
* @returns Command's description
|
||||||
|
*/
|
||||||
|
export const goodDescription = (data: Data, locale: Locale) =>
|
||||||
|
data.description_localizations?.[locale] ?? data.description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aux function for Sub/NameNotLocalized
|
||||||
|
* @param cmd data
|
||||||
|
* @param command command researched
|
||||||
|
* @returns if we found or not the researched command
|
||||||
|
*/
|
||||||
|
const filterLocalizations = (cmd: Data, command: string) => {
|
||||||
|
let res = false;
|
||||||
|
for (const key in cmd?.name_localizations) {
|
||||||
|
res = res || cmd.name_localizations?.[key as Locale] === command;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a command based on any string, localized or not
|
||||||
|
* @param command string
|
||||||
|
* @returns the not localized corresponding string's command name
|
||||||
|
*/
|
||||||
|
export const NameNotLocalized = (client: Client, command: string): SlashCommandBuilder | null => {
|
||||||
|
const list = client.commands.list.map((cmd) => cmd.data);
|
||||||
|
|
||||||
|
return (
|
||||||
|
list.find((cmd) => cmd.name === command) ||
|
||||||
|
list.filter((cmd) => filterLocalizations(cmd, command))[0]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a subcommand of a command based on any string, localized or not
|
||||||
|
* @param parent command of the subcommand
|
||||||
|
* @param command string
|
||||||
|
* @returns the not localized corresponding string's subcommand name
|
||||||
|
*/
|
||||||
|
export const SubnameNotLocalized = (parent: SlashCommandBuilder, command: string) => {
|
||||||
|
const list = parent
|
||||||
|
?.toJSON()
|
||||||
|
.options?.filter((option) => option.type === ApplicationCommandOptionType.Subcommand);
|
||||||
|
|
||||||
|
return (
|
||||||
|
list?.find((cmd) => cmd?.name === command) ||
|
||||||
|
list?.filter((cmd) => filterLocalizations(cmd, command))[0]
|
||||||
|
);
|
||||||
|
};
|
|
@ -3,6 +3,7 @@ import { GuildQueue, QueueRepeatMode } from "discord-player";
|
||||||
import { Client } from "discord.js";
|
import { Client } from "discord.js";
|
||||||
import { getLocale } from "../locales";
|
import { getLocale } from "../locales";
|
||||||
import { blank } from "../misc";
|
import { blank } from "../misc";
|
||||||
|
import { discord_limit_embed_field } from "../constants";
|
||||||
|
|
||||||
export const embedListQueue = (
|
export const embedListQueue = (
|
||||||
client: Client,
|
client: Client,
|
||||||
|
@ -17,8 +18,7 @@ export const embedListQueue = (
|
||||||
// Add the current song at the top of the list
|
// Add the current song at the top of the list
|
||||||
tracks.unshift(queue.history.currentTrack!);
|
tracks.unshift(queue.history.currentTrack!);
|
||||||
|
|
||||||
// Limit of discord is 25
|
const limit_fields = discord_limit_embed_field;
|
||||||
const limit_fields = 25;
|
|
||||||
|
|
||||||
const pageMax = Math.ceil(tracks.length / limit_fields);
|
const pageMax = Math.ceil(tracks.length / limit_fields);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { getLocale } from "../locales";
|
||||||
import { blank, cleanCodeBlock } from "../misc";
|
import { blank, cleanCodeBlock } from "../misc";
|
||||||
import { showDate, strToSeconds, timeDeltaToString } from "../time";
|
import { showDate, strToSeconds, timeDeltaToString } from "../time";
|
||||||
import { RegexC, RegExpFlags } from "../regex";
|
import { RegexC, RegExpFlags } from "../regex";
|
||||||
|
import { readSQL } from "../db";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Option possible for reminders
|
* Option possible for reminders
|
||||||
|
@ -92,9 +93,7 @@ export const newReminder = async (client: Client, time: string, info: infoRemind
|
||||||
|
|
||||||
// Add the remind to the db
|
// Add the remind to the db
|
||||||
client.db.run(
|
client.db.run(
|
||||||
"INSERT INTO reminder ( \
|
readSQL("reminder/add"),
|
||||||
data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id, locale, timeout_id \
|
|
||||||
) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? );",
|
|
||||||
[
|
[
|
||||||
info.message,
|
info.message,
|
||||||
`${expiration_date}`,
|
`${expiration_date}`,
|
||||||
|
@ -127,19 +126,15 @@ export const newReminder = async (client: Client, time: string, info: infoRemind
|
||||||
export const deleteReminder = (client: Client, createdAt: string, userId: string) => {
|
export const deleteReminder = (client: Client, createdAt: string, userId: string) => {
|
||||||
// Delete the reminder for the database
|
// Delete the reminder for the database
|
||||||
return new Promise((ok, ko) => {
|
return new Promise((ok, ko) => {
|
||||||
// Add the remind to the db
|
// Remove the remind to the db
|
||||||
client.db.run(
|
client.db.run(readSQL("reminder/remove"), [createdAt, userId], (err) => {
|
||||||
"DELETE FROM reminder WHERE creation_date = ? AND user_id = ?",
|
if (err) {
|
||||||
[createdAt, userId],
|
ko(err);
|
||||||
(err) => {
|
}
|
||||||
if (err) {
|
|
||||||
ko(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send confirmation to user
|
// Send confirmation to user
|
||||||
ok(true);
|
ok(true);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,16 +194,20 @@ const sendReminderAux = (client: Client, info: infoReminder, option: OptionRemin
|
||||||
// Channel
|
// Channel
|
||||||
client.channels.fetch(info.channelId!).then((channel) => {
|
client.channels.fetch(info.channelId!).then((channel) => {
|
||||||
if (channel?.isSendable()) {
|
if (channel?.isSendable()) {
|
||||||
let content = `<@${info.userId}>`;
|
const author_mention = `<@${info.userId}>`;
|
||||||
|
|
||||||
|
let content = author_mention;
|
||||||
embed.setFooter({
|
embed.setFooter({
|
||||||
text: `${loc.get("c_reminder17")} ${timeDeltaToString(info.createdAt)}`,
|
text: `${loc.get("c_reminder17")} ${timeDeltaToString(info.createdAt)}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mention everybody if needed
|
// Mention everybody if needed
|
||||||
if (option === OptionReminder.Mention) {
|
if (option === OptionReminder.Mention) {
|
||||||
(info.message?.match(/<@\d+>/g) ?? []).forEach((mention) => {
|
[...new Set(info.message?.match(/<@\d+>/g) ?? [])]
|
||||||
content += " " + mention;
|
.filter((mention) => mention !== author_mention)
|
||||||
});
|
.forEach((mention: string) => {
|
||||||
|
content += " " + mention;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.send({ content, embeds: [embed] });
|
channel.send({ content, embeds: [embed] });
|
||||||
|
@ -276,12 +275,7 @@ export const checkOwnershipReminder = async (
|
||||||
const data = (await new Promise((ok, ko) => {
|
const data = (await new Promise((ok, ko) => {
|
||||||
// Check the ownership
|
// Check the ownership
|
||||||
client.db.all<returnData>(
|
client.db.all<returnData>(
|
||||||
"SELECT EXISTS ( \
|
readSQL("reminder/ownership_check"),
|
||||||
SELECT 1 FROM reminder \
|
|
||||||
WHERE id = ? \
|
|
||||||
AND user_id = ? \
|
|
||||||
AND (guild_id = ? OR guild_id = 0) \
|
|
||||||
)",
|
|
||||||
[id, userId, guildId],
|
[id, userId, guildId],
|
||||||
(err, row) => {
|
(err, row) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -304,19 +298,14 @@ export const checkOwnershipReminder = async (
|
||||||
export const getReminderInfo = async (client: Client, id: number) => {
|
export const getReminderInfo = async (client: Client, id: number) => {
|
||||||
return (await new Promise((ok, ko) => {
|
return (await new Promise((ok, ko) => {
|
||||||
// Check the ownership
|
// Check the ownership
|
||||||
client.db.all<dbReminder>(
|
client.db.all<dbReminder>(readSQL("reminder/findById"), [id], (err, row) => {
|
||||||
"SELECT * FROM reminder \
|
if (err) {
|
||||||
WHERE id = ?",
|
ko(err);
|
||||||
[id],
|
}
|
||||||
(err, row) => {
|
|
||||||
if (err) {
|
|
||||||
ko(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send all the current reminders
|
// Send all the current reminders
|
||||||
ok(row[0]);
|
ok(row[0]);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
})) as dbReminder;
|
})) as dbReminder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -330,17 +319,7 @@ export const updateReminder = (client: Client, data: dbReminder) => {
|
||||||
return new Promise((ok, ko) => {
|
return new Promise((ok, ko) => {
|
||||||
// Update the db
|
// Update the db
|
||||||
client.db.run(
|
client.db.run(
|
||||||
"UPDATE reminder \
|
readSQL("reminder/update"),
|
||||||
SET data = ?, \
|
|
||||||
expiration_date = ?, \
|
|
||||||
option_id = ?, \
|
|
||||||
channel_id = ?, \
|
|
||||||
creation_date = ?, \
|
|
||||||
user_id = ?, \
|
|
||||||
guild_id = ?, \
|
|
||||||
locale = ?, \
|
|
||||||
timeout_id = ? \
|
|
||||||
WHERE ID = ?",
|
|
||||||
[
|
[
|
||||||
data.data,
|
data.data,
|
||||||
data.expiration_date,
|
data.expiration_date,
|
||||||
|
@ -374,19 +353,14 @@ export const updateReminder = (client: Client, data: dbReminder) => {
|
||||||
const listReminders = async (client: Client, userId: string, guildId: string | null) => {
|
const listReminders = async (client: Client, userId: string, guildId: string | null) => {
|
||||||
return (await new Promise((ok, ko) => {
|
return (await new Promise((ok, ko) => {
|
||||||
// Check the ownership
|
// Check the ownership
|
||||||
client.db.all<dbReminder>(
|
client.db.all<dbReminder>(readSQL("reminder/find"), [userId, guildId ?? 0], (err, row) => {
|
||||||
"SELECT data, creation_date, expiration_date, id FROM reminder \
|
if (err) {
|
||||||
WHERE user_id = ? AND (guild_id = ? OR guild_id = 0)",
|
ko(err);
|
||||||
[userId, guildId ?? 0],
|
}
|
||||||
(err, row) => {
|
|
||||||
if (err) {
|
|
||||||
ko(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send all the current reminders
|
// Send all the current reminders
|
||||||
ok(row);
|
ok(row);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
})) as dbReminder[];
|
})) as dbReminder[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
11
src/utils/constants.ts
Normal file
11
src/utils/constants.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/** Max message length */
|
||||||
|
export const discord_limit_message = 2000;
|
||||||
|
|
||||||
|
/** Max embed field an embed can have */
|
||||||
|
export const discord_limit_embed_field = 25;
|
||||||
|
|
||||||
|
/** Max element the autocompletion of slash commands can have */
|
||||||
|
export const discord_limit_autocompletion_list_length = 25;
|
||||||
|
|
||||||
|
/** Max length of an element in autocompletion of slash commands */
|
||||||
|
export const discord_limit_autocompletion_value_length = 100;
|
15
src/utils/db.ts
Normal file
15
src/utils/db.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
export const readSQL = (path: string) => {
|
||||||
|
const dir = "./src/sql/";
|
||||||
|
if (!path.startsWith(dir)) {
|
||||||
|
path = dir + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ext = ".sql";
|
||||||
|
if (!path.endsWith(ext)) {
|
||||||
|
path += ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.readFileSync(path, "utf8");
|
||||||
|
};
|
|
@ -94,14 +94,12 @@ export const strToSeconds = (time: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculating the difference between a date and now
|
* Returns the time in a readable way
|
||||||
* @param lang Locale
|
* @param seconds Time in milliseconds
|
||||||
* @param time Time
|
* @returns Time as string
|
||||||
* @returns Delta between the time and now
|
|
||||||
*/
|
*/
|
||||||
export const timeDeltaToString = (time: number) => {
|
export const timeToString = (time: number) => {
|
||||||
const now = Date.now();
|
let secondsDifference = Math.abs(Math.ceil(time / 1000));
|
||||||
let secondsDifference = Math.abs(Math.ceil((time - now) / 1000));
|
|
||||||
|
|
||||||
if (secondsDifference === 0) {
|
if (secondsDifference === 0) {
|
||||||
return "0s";
|
return "0s";
|
||||||
|
@ -123,3 +121,13 @@ export const timeDeltaToString = (time: number) => {
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(" ");
|
.join(" ");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculating the difference between a date and now
|
||||||
|
* @param time Time in milliseconds
|
||||||
|
* @returns Delta between the time and now
|
||||||
|
*/
|
||||||
|
export const timeDeltaToString = (time: number) => {
|
||||||
|
const now = Date.now();
|
||||||
|
return timeToString(time - now);
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue