Version 3.0 sortie, fusion avec JujulBot
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export interface LoginDetails {
|
||||
username: string
|
||||
password: string
|
||||
remember?: boolean
|
||||
otp?: string
|
||||
}
|
||||
|
||||
export const ADSModule = {
|
||||
async GetInstances(host: string, SESSIONID: string) {
|
||||
return await axios.post(host + '/API/ADSModule/GetInstances', {
|
||||
@@ -56,7 +63,7 @@ export const ADSModule = {
|
||||
}
|
||||
|
||||
export const Core = {
|
||||
async Login(host: string, details: any) {
|
||||
async Login(host: string, details: LoginDetails) {
|
||||
return await axios.post(host + '/API/Core/Login',
|
||||
details
|
||||
).then(response => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import axios from 'axios'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
export declare class Game {
|
||||
interface Game {
|
||||
name: string
|
||||
link: string
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ export default async (guild: Guild) => {
|
||||
},
|
||||
guildRss: { enabled: false, feeds: [] },
|
||||
guildAmp: { enabled: false },
|
||||
guildFbx: { enabled: false }
|
||||
guildFbx: { enabled: false },
|
||||
guildTwitch: { enabled: false }
|
||||
})
|
||||
await guildProfile.save().catch(console.error)
|
||||
return guildProfile
|
||||
|
||||
@@ -20,7 +20,7 @@ export const Core = {
|
||||
return { status: 'error', data: error }
|
||||
})
|
||||
},
|
||||
async Init(host: string, version: Number, httpsAgent: https.Agent, app: App, trackId: String) {
|
||||
async Init(host: string, version: number, httpsAgent: https.Agent, app: App, trackId: string) {
|
||||
let request
|
||||
|
||||
if (trackId) request = axios.get(host + `/api/v${version}/login/authorize/` + trackId, { httpsAgent })
|
||||
@@ -37,7 +37,7 @@ export const Core = {
|
||||
}
|
||||
|
||||
export const Login = {
|
||||
async Challenge(host: string, version: Number, httpsAgent: https.Agent) {
|
||||
async Challenge(host: string, version: number, httpsAgent: https.Agent) {
|
||||
let request = axios.get(host + `/api/v${version}/login/`, { httpsAgent })
|
||||
|
||||
return await request.then(response => {
|
||||
@@ -49,7 +49,7 @@ export const Login = {
|
||||
return { status: 'error', data: error }
|
||||
})
|
||||
},
|
||||
async Session(host: string, version: Number, httpsAgent: https.Agent, app_id: string, password: string) {
|
||||
async Session(host: string, version: number, httpsAgent: https.Agent, app_id: string, password: string) {
|
||||
let request = axios.post(host + `/api/v${version}/login/session/`, { app_id, password }, { httpsAgent })
|
||||
|
||||
return await request.then(response => {
|
||||
@@ -64,7 +64,7 @@ export const Login = {
|
||||
}
|
||||
|
||||
export const Get = {
|
||||
async Connection(host: string, version: Number, httpsAgent: https.Agent, sessionToken: string) {
|
||||
async Connection(host: string, version: number, httpsAgent: https.Agent, sessionToken: string) {
|
||||
let request = axios.get(host + `/api/v${version}/connection/`, { httpsAgent, headers: { 'X-Fbx-App-Auth': sessionToken } })
|
||||
|
||||
return await request.then(response => {
|
||||
|
||||
206
src/utils/player.ts
Normal file
206
src/utils/player.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { Client, TextChannel, CommandInteraction, Guild, GuildChannel, TextBasedChannel, VoiceChannel, EmbedBuilder, ButtonBuilder, ActionRowBuilder, ButtonInteraction } from 'discord.js'
|
||||
import { useMainPlayer, useQueue } from 'discord-player'
|
||||
import { Document } from 'mongoose'
|
||||
import getUptime from './getUptime'
|
||||
|
||||
type ChannelInferrable = {
|
||||
channel: TextBasedChannel | VoiceChannel
|
||||
guild?: Guild
|
||||
}
|
||||
|
||||
export class PlayerMetadata {
|
||||
public constructor(public data: ChannelInferrable) {
|
||||
if (data.channel.isDMBased()) { throw new Error('PlayerMetadata cannot be created from a DM') }
|
||||
if (!data.channel) { throw new Error('PlayerMetadata can only be created from a channel') }
|
||||
}
|
||||
public get channel() { return this.data.channel! }
|
||||
public get guild() { return this.data.guild || (this.data.channel as GuildChannel).guild }
|
||||
|
||||
public static create(data: ChannelInferrable | CommandInteraction) {
|
||||
if (data instanceof CommandInteraction) {
|
||||
if (!data.inGuild()) { throw new Error('PlayerMetadata cannot be created from a DM') }
|
||||
|
||||
return new PlayerMetadata({ channel: data.channel!, guild: data.guild! })
|
||||
}
|
||||
return new PlayerMetadata(data);
|
||||
}
|
||||
}
|
||||
|
||||
export const bots = ['1065047326860783636', '1119343522059927684', '1119344050412204032', '1210714000321548329', '660961595006124052']
|
||||
export const playerButtons = ['loop', 'pause', 'previous', 'resume', 'shuffle', 'skip', 'stop', 'volume_down', 'volume_up']
|
||||
|
||||
export async function playerDisco (client: Client, guildProfile: Document) {
|
||||
try {
|
||||
let guild = client.guilds.cache.get(guildProfile.get('guildId'))
|
||||
if (!guild) {
|
||||
clearInterval(client.disco.interval)
|
||||
return 'clear'
|
||||
}
|
||||
|
||||
let dbData = guildProfile.get('guildPlayer.disco')
|
||||
let queue = useQueue(guild.id)
|
||||
if (queue) if (queue.isPlaying()) {
|
||||
dbData['progress'] = queue.node.playbackTime.toString()
|
||||
|
||||
guildProfile.set('guildPlayer.disco', dbData)
|
||||
guildProfile.markModified('guildPlayer.disco')
|
||||
await guildProfile.save().catch(console.error)
|
||||
}
|
||||
|
||||
let channel = client.channels.cache.get(dbData.channelId) as TextChannel
|
||||
if (!channel) {
|
||||
console.log(`Aucun channel trouvé avec l'id \`${dbData.channelId}\`, veuillez utiliser la commande \`/database edit 'value': guildPlayer.disco.channelId\` !`)
|
||||
clearInterval(client.disco.interval)
|
||||
return 'clear'
|
||||
}
|
||||
|
||||
let { embed, components } = await playerGenerate(guild)
|
||||
if (components && embed.data.footer) embed.setFooter({ text: `Uptime: ${getUptime(client.uptime)} \n ${embed.data.footer.text}` })
|
||||
else embed.setFooter({ text: `Uptime: ${getUptime(client.uptime)}` })
|
||||
|
||||
let messages = await channel.messages.fetch()
|
||||
messages.forEach(msg => { if (!bots.includes(msg.author.id)) msg.delete() })
|
||||
|
||||
let botMessage = messages.find(msg => client.user && msg.author.id === client.user.id)
|
||||
if (botMessage) {
|
||||
if (!components && botMessage.components.length > 0) {
|
||||
await botMessage.delete()
|
||||
return channel.send({ embeds: [embed] })
|
||||
|
||||
} else if (components) return botMessage.edit({ embeds: [embed], components })
|
||||
|
||||
else return botMessage.edit({ embeds: [embed] })
|
||||
|
||||
} else return channel.send({ embeds: [embed] })
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return 'clear'
|
||||
}
|
||||
}
|
||||
|
||||
export async function playerEdit (interaction: ButtonInteraction) {
|
||||
let guild = interaction.guild
|
||||
if (!guild) return await interaction.reply({ content: 'Cette commande n\'est pas disponible en message privé.', ephemeral: true })
|
||||
|
||||
let { components } = await playerGenerate(guild)
|
||||
if (!components) return
|
||||
|
||||
components.forEach((actionRow) => actionRow.components.forEach((button) => button.setDisabled(true)))
|
||||
await interaction.update({ components })
|
||||
}
|
||||
|
||||
export async function playerGenerate (guild: Guild) {
|
||||
let embed = new EmbedBuilder().setColor('#ffc370')
|
||||
|
||||
let queue = useQueue(guild.id)
|
||||
if (!queue) {
|
||||
embed.setTitle('Aucune session d\'écoute en cours !')
|
||||
return ({ embed, components: null })
|
||||
}
|
||||
|
||||
let track = queue.currentTrack
|
||||
if (!track) {
|
||||
embed.setTitle('Aucune musique en cours de lecture !')
|
||||
return ({ embed, components: null })
|
||||
}
|
||||
|
||||
embed.setTitle(track.title)
|
||||
.setAuthor({ name: track.author })
|
||||
.setURL(track.url)
|
||||
.setImage(track.thumbnail)
|
||||
.addFields(
|
||||
{ name: 'Durée', value: track.duration, inline: true },
|
||||
{ name: 'Source', value: track.source === 'youtube' ? 'Youtube' : track.source === 'spotify' ? 'Spotify' : 'Inconnu', inline: true },
|
||||
{ name: 'Volume', value: `${queue.node.volume}%`, inline: true },
|
||||
{ name: queue.node.isPaused() ? 'Progression (en pause)' : 'Progression', value: queue.node.createProgressBar() || 'Aucune' },
|
||||
{ name: 'Loop', value: queue.repeatMode === 3 ? 'Autoplay' : queue.repeatMode === 2 ? 'File d\'Attente' : queue.repeatMode === 1 ? 'Titre' : 'Off', inline: true }
|
||||
)
|
||||
.setDescription(`**Musique suivante :** ${queue.tracks.data[0] ? queue.tracks.data[0].title : 'Aucune'}`)
|
||||
.setFooter({ text: `Demandé par ${track.requestedBy ? track.requestedBy.tag : 'Inconnu'}` })
|
||||
|
||||
let components = [
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel(queue.node.isPaused() ? '▶️' : '⏸️')
|
||||
.setStyle(2)
|
||||
.setCustomId(queue.node.isPaused() ? 'resume' : 'pause'),
|
||||
new ButtonBuilder()
|
||||
.setLabel('⏹️')
|
||||
.setStyle(2)
|
||||
.setCustomId('stop'),
|
||||
new ButtonBuilder()
|
||||
.setLabel('⏭️')
|
||||
.setStyle(2)
|
||||
.setCustomId('skip')
|
||||
.setDisabled(queue.tracks.data.length !== 0),
|
||||
new ButtonBuilder()
|
||||
.setLabel('🔉')
|
||||
.setStyle(2)
|
||||
.setCustomId('volume_down')
|
||||
.setDisabled(queue.node.volume === 0),
|
||||
new ButtonBuilder()
|
||||
.setLabel('🔊')
|
||||
.setStyle(2)
|
||||
.setCustomId('volume_up')
|
||||
.setDisabled(queue.node.volume === 100)
|
||||
),
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel('🔀')
|
||||
.setStyle(2)
|
||||
.setCustomId('shuffle'),
|
||||
new ButtonBuilder()
|
||||
.setLabel('🔁')
|
||||
.setStyle(2)
|
||||
.setCustomId('loop'),
|
||||
new ButtonBuilder()
|
||||
.setLabel('⏮️')
|
||||
.setStyle(2)
|
||||
.setCustomId('previous')
|
||||
.setDisabled(queue.history.previousTrack ? false : true)
|
||||
)
|
||||
]
|
||||
return ({ embed, components })
|
||||
}
|
||||
|
||||
export async function playerReplay (client: Client, guildProfile: Document) {
|
||||
let dbData = guildProfile.get('guildPlayer.replay')
|
||||
|
||||
let textChannel = client.channels.cache.get(dbData.textChannelId) as TextChannel
|
||||
if (!textChannel) return console.log(`Aucun channel trouvé avec l'id \`${dbData.textChannelId}\`, veuillez utiliser la commande \`/setchannel\` !`)
|
||||
let voiceChannel = client.channels.cache.get(dbData.voiceChannelId) as VoiceChannel
|
||||
if (!voiceChannel) return console.log(`Aucun channel trouvé avec l'id \`${dbData.voiceChannelId}\`, veuillez utiliser la commande \`/setchannel\` !`)
|
||||
|
||||
let player = useMainPlayer()
|
||||
let queue = player.nodes.create(textChannel.guild, {
|
||||
metadata: {
|
||||
channel: textChannel,
|
||||
client: textChannel.guild.members.me,
|
||||
requestedBy: client.user
|
||||
},
|
||||
selfDeaf: true,
|
||||
volume: 20,
|
||||
leaveOnEmpty: true,
|
||||
leaveOnEmptyCooldown: 30000,
|
||||
leaveOnEnd: true,
|
||||
leaveOnEndCooldown: 300000
|
||||
})
|
||||
|
||||
try { if (!queue.connection) await queue.connect(voiceChannel) }
|
||||
catch (error) { console.error(error) }
|
||||
|
||||
let result = await player.search(dbData.trackUrl as string, { requestedBy: client.user || undefined })
|
||||
if (!result.hasTracks()) await textChannel.send(`Aucune musique trouvée pour **${dbData.trackUrl}** !`)
|
||||
let track = result.tracks[0]
|
||||
|
||||
let entry = queue.tasksQueue.acquire()
|
||||
await entry.getTask()
|
||||
queue.addTrack(track)
|
||||
|
||||
try {
|
||||
await queue.node.play()
|
||||
await queue.node.seek(Number(dbData.progress) / 1000)
|
||||
await textChannel.send(`Relancement de la musique suite à mon redémarrage...`)
|
||||
} catch (error) { console.error(error) }
|
||||
finally { queue.tasksQueue.release() }
|
||||
}
|
||||
@@ -88,7 +88,7 @@ export default async (client: Client, guildProfile: Document) => {
|
||||
]
|
||||
|
||||
if (!dbData.feeds) dbData.feeds = feeds
|
||||
dbData.feeds.forEach((feed: Feed, i: Number) => { setTimeout(async () => {
|
||||
dbData.feeds.forEach((feed: Feed, i: number) => { setTimeout(async () => {
|
||||
let parser = new Parser()
|
||||
if (feed.token) feed.url += feed.token
|
||||
let feedData = await parser.parseURL(feed.url)
|
||||
@@ -119,5 +119,5 @@ export default async (client: Client, guildProfile: Document) => {
|
||||
if (lastMessage.content !== `**${lastItem.title}**\n${lastItem.link}`) await thread.send({ content: `**${lastItem.title}**\n${lastItem.link}` })
|
||||
//else console.log('No new item found for ' + feed.name)
|
||||
}, Number(i) * 1000) })
|
||||
} catch (error: any) { console.error(error); return 'clear' }
|
||||
} catch (error) { console.error(error); return 'clear' }
|
||||
}
|
||||
173
src/utils/twitch.ts
Normal file
173
src/utils/twitch.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { Guild, TextChannel, EmbedBuilder } from 'discord.js'
|
||||
import axios, { AxiosHeaderValue } from 'axios'
|
||||
import dbGuild from '../schemas/guild'
|
||||
|
||||
interface NotificationData {
|
||||
metadata: {
|
||||
message_type: string
|
||||
}
|
||||
payload: {
|
||||
subscription: {
|
||||
type: string
|
||||
}
|
||||
event: {
|
||||
broadcaster_user_name: string
|
||||
broadcaster_user_login: string
|
||||
}
|
||||
session: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Condition {
|
||||
broadcaster_user_id: string
|
||||
}
|
||||
|
||||
export async function checkChannel (client_id: string, client_secret: string, channel_access_token: string, guild: Guild) {
|
||||
let guildProfile = await dbGuild.findOne({ guildId: guild?.id })
|
||||
if (!guildProfile) return console.log(`Database data for ${guild?.name} does not exist, please initialize with \`/database init\` !`)
|
||||
|
||||
let dbData = guildProfile.get('guildTwitch')
|
||||
if (!dbData?.enabled) return console.log(`Twitch module is disabled for "${guild?.name}", please activate with \`/database edit guildTwitch.enabled True\` !`)
|
||||
|
||||
if (!await validateToken(channel_access_token)) {
|
||||
let channel_refresh_token = dbData.channelRefreshToken
|
||||
if (!channel_refresh_token) return console.log('No refresh token found in database !')
|
||||
|
||||
let GetAccessFromRefresh = await refreshToken(client_id, client_secret, channel_refresh_token)
|
||||
if (!GetAccessFromRefresh) return false;
|
||||
|
||||
[channel_access_token, channel_refresh_token] = [dbData.channelAccessToken, dbData.channelRefreshToken] = GetAccessFromRefresh
|
||||
|
||||
guildProfile.set('guildTwitch', dbData)
|
||||
guildProfile.markModified('guildTwitch')
|
||||
await guildProfile.save().catch(console.error)
|
||||
}
|
||||
return channel_access_token
|
||||
}
|
||||
|
||||
export async function validateToken (access_token: string) {
|
||||
return await axios.get('https://id.twitch.tv/oauth2/validate', {
|
||||
headers: {
|
||||
'Authorization': `OAuth ${access_token}`,
|
||||
}
|
||||
}).then(() => {
|
||||
return true
|
||||
}).catch(error => { console.error(error.response.data) })
|
||||
}
|
||||
|
||||
export async function refreshToken (client_id: string, client_secret: string, refresh_token: string) {
|
||||
return await axios.post('https://id.twitch.tv/oauth2/token', {
|
||||
client_id,
|
||||
client_secret,
|
||||
refresh_token,
|
||||
grant_type: 'refresh_token'
|
||||
}).then(response => {
|
||||
if (response.data.token_type === 'bearer') return [response.data.access_token, response.data.refresh_token]
|
||||
}).catch(error => { console.error(error.response.data) })
|
||||
}
|
||||
|
||||
export async function getStreams (client_id: string, access_token: string, user_login: string) {
|
||||
return await axios.get(`https://api.twitch.tv/helix/streams?user_login=${user_login}`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${access_token}`,
|
||||
'Client-Id': client_id as AxiosHeaderValue
|
||||
}
|
||||
}).then(response => {
|
||||
return response.data.data[0]
|
||||
}).catch(error => { console.error(error.response) })
|
||||
}
|
||||
|
||||
export async function getUserInfo (client_id: string, access_token: string, type: string) {
|
||||
return await axios.get('https://api.twitch.tv/helix/users', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${access_token}`,
|
||||
'Client-Id': client_id as AxiosHeaderValue
|
||||
}
|
||||
}).then(response => {
|
||||
if (type === 'login') return response.data.data[0].login
|
||||
if (type === 'id') return response.data.data[0].id
|
||||
if (type === 'profile_image_url') return response.data.data[0].profile_image_url
|
||||
}).catch(error => { console.error(error.response.data) })
|
||||
}
|
||||
|
||||
export async function notification (client_id: string, channel_access_token: string, data: NotificationData, guild: Guild) {
|
||||
let { subscription, event } = data.payload
|
||||
|
||||
let guildProfile = await dbGuild.findOne({ guildId: guild?.id })
|
||||
if (!guildProfile) return console.log(`Database data for ${guild?.name} does not exist, please initialize with \`/database init\` !`)
|
||||
|
||||
let dbData = guildProfile.get('guildTwitch')
|
||||
if (!dbData?.enabled) return console.log(`Twitch module is disabled for "${guild?.name}", please activate with \`/database edit guildTwitch.enabled True\` !`)
|
||||
|
||||
let liveChannelId = dbData.liveChannelId
|
||||
if (!liveChannelId) return console.log('No live channel id found in database !')
|
||||
|
||||
let liveChannel = guild.channels.cache.get(liveChannelId) as TextChannel
|
||||
if (!liveChannel) return console.log(`\u001b[1;35m Can't find channel with id ${liveChannelId}`)
|
||||
|
||||
if (subscription.type === 'stream.online') {
|
||||
console.log(`\u001b[1;35m Stream from ${event.broadcaster_user_name} is now online, sending Discord message...`)
|
||||
|
||||
let stream_data = await getStreams(client_id, channel_access_token, event.broadcaster_user_login)
|
||||
let user_profile_image_url = await getUserInfo(client_id, channel_access_token, 'profile_image_url')
|
||||
|
||||
let embed = new EmbedBuilder()
|
||||
.setColor('#6441a5')
|
||||
.setTitle(stream_data.title)
|
||||
.setURL(`https://twitch.tv/laytho_`)
|
||||
.setAuthor({ name: `${event.broadcaster_user_login.toUpperCase()} EST ACTUELLEMENT EN LIVE !`, iconURL: user_profile_image_url })
|
||||
.setDescription(`Joue à ${stream_data.game_name} avec ${stream_data.viewer_count} viewers`)
|
||||
.setImage(stream_data.thumbnail_url.replace('{width}', '1920').replace('{height}', '1080'))
|
||||
.setTimestamp()
|
||||
let message = await liveChannel.send({ content: `Hey @everyone ! <@${dbData.liveBroadcasterId}> démarre son live sur **Twitch** !`, embeds: [embed] })
|
||||
|
||||
dbData.liveMessageId = message.id
|
||||
guildProfile.set('guildTwitch', dbData)
|
||||
guildProfile.markModified('guildTwitch')
|
||||
await guildProfile.save().catch(console.error)
|
||||
}
|
||||
else if (subscription.type === 'stream.offline') {
|
||||
console.log(`\u001b[1;35m Stream from laytho_ is now offline, editing Discord message...`)
|
||||
|
||||
let message = liveChannel.messages.cache.find(message => message.id === dbData.liveMessageId)
|
||||
if (!message) return console.log(`\u001b[1;35m Can't find message with id ${dbData.liveMessageId}`)
|
||||
if (!message.embeds[0]) return console.log(`\u001b[1;35m Can't find embed in message with id ${dbData.liveMessageId}`)
|
||||
|
||||
let duration = new Date().getTime() - new Date(message.embeds[0].data.timestamp ?? 0).getTime()
|
||||
let seconds = Math.floor(duration / 1000)
|
||||
let minutes = Math.floor(seconds / 60)
|
||||
let hours = Math.floor(minutes / 60)
|
||||
let duration_string = `${hours ? hours + 'H' : ''} ${minutes % 60 ? minutes % 60 + 'M' : ''} ${seconds % 60 ? seconds % 60 + 'S' : ''}`
|
||||
|
||||
let user_profile_image_url = await getUserInfo(client_id, channel_access_token, 'profile_image_url')
|
||||
|
||||
let embed = new EmbedBuilder()
|
||||
.setColor('#6441a5')
|
||||
.setAuthor({ name: `C'EST FINI, LE LIVE A DURÉ ${duration_string} !`, iconURL: user_profile_image_url })
|
||||
.setTimestamp()
|
||||
|
||||
message.edit({ content: `Re @everyone ! <@${dbData.liveBroadcasterId}> a terminé son live sur **Twitch** !`, embeds: [embed] })
|
||||
}
|
||||
}
|
||||
|
||||
export async function subscribeToEvents (client_id: string, access_token: string, session_id: string, type: string, version: string, condition: Condition) {
|
||||
return await axios.post('https://api.twitch.tv/helix/eventsub/subscriptions', {
|
||||
type,
|
||||
version,
|
||||
condition,
|
||||
transport: {
|
||||
method: 'websocket',
|
||||
session_id
|
||||
}
|
||||
}, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${access_token}`,
|
||||
'Client-Id': client_id,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then(response => {
|
||||
return response.data.data[0].status
|
||||
}).catch(error => { return error.response.data })
|
||||
}
|
||||
Reference in New Issue
Block a user