All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 3m43s
198 lines
9.6 KiB
TypeScript
198 lines
9.6 KiB
TypeScript
import { SlashCommandBuilder, ChannelType, MessageFlags, PermissionFlagsBits } from "discord.js"
|
|
import type { ChatInputCommandInteraction, AutocompleteInteraction, ApplicationCommandOptionChoiceData } from "discord.js"
|
|
import { twitchClient, listener, onlineSub, offlineSub, generateTwitchEmbed } from "@/utils/twitch"
|
|
import type { GuildTwitch } from "@/types/schemas"
|
|
import dbGuild from "@/schemas/guild"
|
|
import { t } from "@/utils/i18n"
|
|
import { logConsole, logConsoleError } from "@/utils/console"
|
|
|
|
export const data = new SlashCommandBuilder()
|
|
.setName("twitch")
|
|
.setDescription("Manage streamers notifications")
|
|
.setDescriptionLocalizations({ fr: "Gérer les notifications des streameurs" })
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
|
.addSubcommand(subcommand => subcommand
|
|
.setName("status")
|
|
.setDescription("Display Twitch module status")
|
|
.setNameLocalizations({ fr: "statut" })
|
|
.setDescriptionLocalizations({ fr: "Afficher le statut du module Twitch" })
|
|
)
|
|
.addSubcommand(subcommand => subcommand
|
|
.setName("channel")
|
|
.setDescription("Set the channel to send notifications")
|
|
.setNameLocalizations({ fr: "canal" })
|
|
.setDescriptionLocalizations({ fr: "Définir le canal pour envoyer les notifications" })
|
|
.addChannelOption(option => option
|
|
.setName("channel")
|
|
.setDescription("The channel to send notifications")
|
|
.setNameLocalizations({ fr: "canal" })
|
|
.setDescriptionLocalizations({ fr: "Le canal pour envoyer les notifications" })
|
|
.setRequired(true)
|
|
)
|
|
)
|
|
.addSubcommandGroup(subcommandgroup => subcommandgroup
|
|
.setName("streamer")
|
|
.setDescription("Manage streamers")
|
|
.setNameLocalizations({ fr: "streameur" })
|
|
.setDescriptionLocalizations({ fr: "Gérer les streameurs" })
|
|
.addSubcommand(subcommand => subcommand
|
|
.setName("list")
|
|
.setDescription("List all streamers")
|
|
.setNameLocalizations({ fr: "liste" })
|
|
.setDescriptionLocalizations({ fr: "Lister tous les streameurs" })
|
|
)
|
|
.addSubcommand(subcommand => subcommand
|
|
.setName("add")
|
|
.setDescription("Add a streamer")
|
|
.setNameLocalizations({ fr: "ajouter" })
|
|
.setDescriptionLocalizations({ fr: "Ajouter un streameur" })
|
|
.addStringOption(option => option
|
|
.setName("username")
|
|
.setDescription("The username of the streamer to add")
|
|
.setNameLocalizations({ fr: "nom_utilisateur" })
|
|
.setDescriptionLocalizations({ fr: "Le nom d'utilisateur du streameur à ajouter" })
|
|
.setRequired(true)
|
|
.setAutocomplete(true)
|
|
)
|
|
.addUserOption(option => option
|
|
.setName("member")
|
|
.setDescription("The member on the guild to mention")
|
|
.setNameLocalizations({ fr: "membre" })
|
|
.setDescriptionLocalizations({ fr: "Le membre sur le serveur à mentionner" })
|
|
.setRequired(false)
|
|
)
|
|
)
|
|
.addSubcommand(subcommand => subcommand
|
|
.setName("remove")
|
|
.setDescription("Remove a streamer")
|
|
.setNameLocalizations({ fr: "supprimer" })
|
|
.setDescriptionLocalizations({ fr: "Supprimer un streameur" })
|
|
.addStringOption(option => option
|
|
.setName("username")
|
|
.setDescription("The username of the streamer to remove")
|
|
.setNameLocalizations({ fr: "nom_utilisateur" })
|
|
.setDescriptionLocalizations({ fr: "Le nom d'utilisateur du streameur à supprimer" })
|
|
.setRequired(true)
|
|
.setAutocomplete(true)
|
|
)
|
|
)
|
|
)
|
|
|
|
export async function execute(interaction: ChatInputCommandInteraction) {
|
|
const guildProfile = await dbGuild.findOne({ guildId: interaction.guild?.id })
|
|
if (!guildProfile) return interaction.reply({ content: t(interaction.locale, "common.database_not_found"), flags: MessageFlags.Ephemeral })
|
|
|
|
const dbData = guildProfile.get("guildTwitch") as GuildTwitch
|
|
|
|
const subcommandGroup = interaction.options.getSubcommandGroup(false)
|
|
const subcommand = interaction.options.getSubcommand(true)
|
|
if (subcommand == "status") {
|
|
// Utiliser la fonction utilitaire pour générer l'embed et les composants
|
|
const { embed, components } = generateTwitchEmbed(dbData, interaction.client, interaction.guild?.id ?? "", interaction.locale)
|
|
|
|
return interaction.reply({ embeds: [embed], components: components, flags: MessageFlags.Ephemeral })
|
|
}
|
|
|
|
if (!dbData.enabled) return interaction.reply({ content: t(interaction.locale, "twitch.module_disabled_activate"), flags: MessageFlags.Ephemeral })
|
|
|
|
if (subcommand == "channel") {
|
|
const channel = interaction.options.getChannel("channel", true)
|
|
if (channel.type !== ChannelType.GuildText && channel.type !== ChannelType.GuildAnnouncement)
|
|
return interaction.reply({ content: t(interaction.locale, "common.invalid_text_channel"), flags: MessageFlags.Ephemeral })
|
|
|
|
dbData.channelId = channel.id
|
|
guildProfile.set("guildTwitch", dbData)
|
|
guildProfile.markModified("guildTwitch")
|
|
await guildProfile.save().catch(console.error)
|
|
|
|
return interaction.reply({ content: t(interaction.locale, "twitch.notifications_channel_set", { channel: channel.name ?? channel.id }), flags: MessageFlags.Ephemeral })
|
|
}
|
|
else if (subcommandGroup == "streamer") {
|
|
if (!dbData.channelId) return interaction.reply({ content: t(interaction.locale, "twitch.configure_channel_first"), flags: MessageFlags.Ephemeral })
|
|
|
|
if (subcommand == "list") {
|
|
if (!dbData.streamers.length) return interaction.reply({ content: t(interaction.locale, "twitch.no_streamers_list"), flags: MessageFlags.Ephemeral })
|
|
|
|
const streamers = [] as string[]
|
|
await Promise.all(dbData.streamers.map(async streamer => {
|
|
try {
|
|
const user = await twitchClient.users.getUserById(streamer.twitchUserId)
|
|
if (user) streamers.push(`- ${user.displayName} (${streamer.twitchUserId})`)
|
|
else streamers.push(`- ${t(interaction.locale, "twitch.user_not_found_id", { id: streamer.twitchUserId })}`)
|
|
} catch (error) {
|
|
logConsoleError('twitch', 'user_fetch_error', { id: streamer.twitchUserId }, error as Error)
|
|
}
|
|
}))
|
|
const streamerList = streamers.length > 0 ? streamers.join("\n") : t(interaction.locale, "twitch.no_streamers")
|
|
|
|
return interaction.reply({ content: `${t(interaction.locale, "twitch.list.title")}:\n${streamerList}`, flags: MessageFlags.Ephemeral })
|
|
}
|
|
else if (subcommand == "add") {
|
|
const username = interaction.options.getString("username", true)
|
|
const member = interaction.options.getUser("member", false)
|
|
|
|
const user = await twitchClient.users.getUserByName(username)
|
|
if (!user) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_found", { username }), flags: MessageFlags.Ephemeral })
|
|
|
|
if (dbData.streamers.some(s => s.twitchUserId === user.id)) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_already_added", { username }), flags: MessageFlags.Ephemeral })
|
|
dbData.streamers.push({ twitchUserId: user.id, discordUserId: member?.id ?? "", messageId: "" })
|
|
|
|
guildProfile.set("guildTwitch", dbData)
|
|
guildProfile.markModified("guildTwitch")
|
|
await guildProfile.save().catch(console.error)
|
|
|
|
const userSubs = await twitchClient.eventSub.getSubscriptionsForUser(user.id)
|
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
if (!userSubs.data.find(sub => sub.transportMethod === "webhook" && sub.type === "stream.online")) listener.onStreamOnline(user.id, onlineSub)
|
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
if (!userSubs.data.find(sub => sub.transportMethod === "webhook" && sub.type === "stream.offline")) listener.onStreamOffline(user.id, offlineSub)
|
|
|
|
return interaction.reply({ content: t(interaction.locale, "twitch.streamer_added", { username, id: user.id }), flags: MessageFlags.Ephemeral })
|
|
}
|
|
else if (subcommand == "remove") {
|
|
const username = interaction.options.getString("username", true)
|
|
|
|
const user = await twitchClient.users.getUserByName(username)
|
|
if (!user) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_found", { username }), flags: MessageFlags.Ephemeral })
|
|
|
|
const streamerIndex = dbData.streamers.findIndex(s => s.twitchUserId === user.id)
|
|
if (streamerIndex === -1)return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_in_list", { username }), flags: MessageFlags.Ephemeral })
|
|
|
|
dbData.streamers.splice(streamerIndex, 1)
|
|
guildProfile.set("guildTwitch", dbData)
|
|
guildProfile.markModified("guildTwitch")
|
|
await guildProfile.save().catch(console.error)
|
|
|
|
if (!await dbGuild.exists({ "guildTwitch.streamers.twitchUserId": user.id })) {
|
|
const userSubs = await twitchClient.eventSub.getSubscriptionsForUser(user.id)
|
|
await Promise.all(userSubs.data.map(async sub => { if (sub.transportMethod === "webhook" && (sub.type === "stream.online" || sub.type === "stream.offline")) await sub.unsubscribe() }))
|
|
logConsole('twitch', 'listener_removed', { name: user.displayName, id: user.id })
|
|
}
|
|
|
|
return interaction.reply({ content: t(interaction.locale, "twitch.streamer_removed", { username }), flags: MessageFlags.Ephemeral })
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function autocompleteRun(interaction: AutocompleteInteraction) {
|
|
const query = interaction.options.getString("username", true)
|
|
if (!query || query.length < 3) return interaction.respond([])
|
|
|
|
const guildProfile = await dbGuild.findOne({ guildId: interaction.guild?.id })
|
|
if (!guildProfile) return interaction.respond([])
|
|
|
|
const dbData = guildProfile.get("guildTwitch") as GuildTwitch
|
|
if (!dbData.enabled) return interaction.respond([])
|
|
|
|
const choices: ApplicationCommandOptionChoiceData[] = []
|
|
const searchResult = await twitchClient.search.searchChannels(query)
|
|
if (searchResult.data.length === 0) return interaction.respond([])
|
|
|
|
searchResult.data.forEach(streamerResult => {
|
|
if (dbData.streamers.some(s => s.twitchUserId === streamerResult.id)) return
|
|
choices.push({ name: streamerResult.displayName, value: streamerResult.name })
|
|
})
|
|
|
|
return interaction.respond(choices)
|
|
}
|