Compare commits
4 Commits
93fa5b0666
...
f52968b2c2
Author | SHA1 | Date |
---|---|---|
Thibaut Broggi | f52968b2c2 | |
Thibaut Broggi | 0245ab275c | |
Thibaut Broggi | f1f46267d8 | |
Arthur POULET | 8057d3fa7a |
|
@ -4,6 +4,14 @@ const { PlaylistsManager } = require('./playlists_manager')
|
||||||
const { PlayerManager } = require('./player_manager')
|
const { PlayerManager } = require('./player_manager')
|
||||||
const config = require('../config.json')
|
const config = require('../config.json')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class handles each guilds independantly.
|
||||||
|
* In order to do so, it accept every message in {@link handle_message}
|
||||||
|
* and redirect the action to execute to other Manager dedicated to only one
|
||||||
|
* {@link Discord.Guild}.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
class GuildsManager {
|
class GuildsManager {
|
||||||
#guilds
|
#guilds
|
||||||
|
|
||||||
|
@ -20,6 +28,10 @@ class GuildsManager {
|
||||||
return this.#guilds[guild][name];
|
return this.#guilds[guild][name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Discord.Message} message
|
||||||
|
*/
|
||||||
async handle_message(message) {
|
async handle_message(message) {
|
||||||
const guild_id = message.guild.id
|
const guild_id = message.guild.id
|
||||||
if (!this.#guilds[guild_id]) {
|
if (!this.#guilds[guild_id]) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const { Playlist } = require('./playlist')
|
const { Playlist } = require('./playlist')
|
||||||
const { GuildAction } = require('./guild_action')
|
const { GuildAction } = require('./guild_action')
|
||||||
|
|
||||||
|
const ONE_SECOND = 1000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Trigger
|
* @typedef {Object} Trigger
|
||||||
* @property {RegExp} pattern The pattern that will trigger the trigger
|
* @property {RegExp} pattern The pattern that will trigger the trigger
|
||||||
|
@ -13,6 +15,13 @@ const { GuildAction } = require('./guild_action')
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* InputManager handle message in order to defines which other manager should
|
||||||
|
* handle it. It uses to do so a mix { target, action } which can be used by
|
||||||
|
* the GuildsManager.
|
||||||
|
*
|
||||||
|
* It also is able to generate Trigger and execute them when some conditions
|
||||||
|
* are met.
|
||||||
|
*
|
||||||
* @class
|
* @class
|
||||||
*/
|
*/
|
||||||
class InputManager {
|
class InputManager {
|
||||||
|
@ -26,6 +35,12 @@ class InputManager {
|
||||||
this.#triggers = []
|
this.#triggers = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a {@link GuildAction} which describe a route to a class (Manager) and an action.
|
||||||
|
*
|
||||||
|
* @param {Discord.Message} message discord triggers the current event based on this message
|
||||||
|
* @returns {GuildAction}
|
||||||
|
*/
|
||||||
async handle_message(message) {
|
async handle_message(message) {
|
||||||
if (message.content.slice(0, this.#config.prefix.length) === this.#config.prefix) {
|
if (message.content.slice(0, this.#config.prefix.length) === this.#config.prefix) {
|
||||||
const [command, ...command_params] = message.content.slice(this.#config.prefix.length).trim().split(/\s+/)
|
const [command, ...command_params] = message.content.slice(this.#config.prefix.length).trim().split(/\s+/)
|
||||||
|
@ -48,9 +63,9 @@ class InputManager {
|
||||||
this.#triggers = this.#triggers.filter(Boolean)
|
this.#triggers = this.#triggers.filter(Boolean)
|
||||||
}
|
}
|
||||||
const params = message.content
|
const params = message.content
|
||||||
.split(trigger_target_action.pattern)[1]
|
.split(trigger_target_action.pattern)[1]
|
||||||
.trim()
|
.trim()
|
||||||
.split(/\s+/)
|
.split(/\s+/)
|
||||||
return new GuildAction({ ...trigger_target_action, message, params: params })
|
return new GuildAction({ ...trigger_target_action, message, params: params })
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -77,7 +92,7 @@ class InputManager {
|
||||||
if (idx !== -1 && typeof trigger.on_expire === 'function') {
|
if (idx !== -1 && typeof trigger.on_expire === 'function') {
|
||||||
trigger.on_expire(trigger)
|
trigger.on_expire(trigger)
|
||||||
}
|
}
|
||||||
}, ttl * 1000)
|
}, ttl * ONE_SECOND)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,58 +9,65 @@ const ytdl = require('ytdl-core')
|
||||||
*/
|
*/
|
||||||
class Youtube {
|
class Youtube {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
/**
|
async #get_json_from_url(url) {
|
||||||
* @param {string} search The search pattern that will be sent to Youtube
|
const req = await fetch(url)
|
||||||
* @return {Track[]} An array of objects representing the search results. <code>source</code> is set to <code>"youtube"</code>
|
|
||||||
*/
|
|
||||||
async search_track(search) {
|
|
||||||
const req = await fetch('https://www.youtube.com/results?search_query='
|
|
||||||
+ encodeURIComponent(search))
|
|
||||||
const body = await req.text()
|
const body = await req.text()
|
||||||
const json = JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
|
return JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
async #get_json_from_route(route) {
|
||||||
|
return await this.#get_json_from_url('https://www.youtube.com/' + route)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} search The search pattern that will be sent to Youtube
|
||||||
|
* @return {Track[]} An array of objects representing the search results. <code>source</code> is set to <code>"youtube"</code>
|
||||||
|
*/
|
||||||
|
async search_track(search) {
|
||||||
|
const json = await this.#get_json_from_route('results?search_query='
|
||||||
|
+ encodeURIComponent(search))
|
||||||
const results = json.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0].itemSectionRenderer.contents
|
const results = json.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0].itemSectionRenderer.contents
|
||||||
return results
|
return results
|
||||||
.filter(e => e.videoRenderer)
|
.filter(e => e.videoRenderer)
|
||||||
.map(e => {
|
.map(e => {
|
||||||
return new Track({
|
return new Track({
|
||||||
url: 'https://www.youtube.com/watch?v=' + e.videoRenderer.videoId,
|
url: 'https://www.youtube.com/watch?v=' + e.videoRenderer.videoId,
|
||||||
title: e.videoRenderer.title.runs[0].text,
|
title: e.videoRenderer.title.runs[0].text,
|
||||||
length: e.videoRenderer.lengthText.simpleText,
|
length: e.videoRenderer.lengthText.simpleText,
|
||||||
source: 'youtube',
|
source: 'youtube',
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all tracks of the playlist on youtube
|
* Retrieve all tracks of the playlist on youtube
|
||||||
* @param {string} playlist_query an url or id of the youtube playlist. It will recognize the list=xxx pattern.
|
* @param {string} playlist_query an url or id of the youtube playlist. It will recognize the list=xxx pattern.
|
||||||
* @returns {Playlist}
|
* @returns {Playlist}
|
||||||
*/
|
*/
|
||||||
async get_playlist(playlist_query) {
|
async get_playlist(playlist_query) {
|
||||||
const playlist_id = playlist_query.match(/list=([\w-]+)/)?.[1] || playlist_query
|
const playlist_id = playlist_query.match(/list=([\w-]+)/)?.[1] || playlist_query
|
||||||
const req = await fetch(`https://www.youtube.com/playlist?list=${playlist_id}`)
|
const json = await this.#get_json_from_route('playlist?list='
|
||||||
const body = await req.text()
|
+ encodeURIComponent(playlist_id))
|
||||||
const json = JSON.parse(body.match(/ytInitialData = (.*);<\/script>/)[1])
|
|
||||||
// console.log(JSON.stringify(json, null, 2))
|
// console.log(JSON.stringify(json, null, 2))
|
||||||
const results = json.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].playlistVideoListRenderer.contents
|
const results = json.contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents[0].playlistVideoListRenderer.contents
|
||||||
|
|
||||||
const tracks = results
|
const tracks = results
|
||||||
.filter(video => video?.playlistVideoRenderer?.lengthText)
|
.filter(video => video?.playlistVideoRenderer?.lengthText)
|
||||||
.map((video) => new Track({
|
.map((video) => new Track({
|
||||||
title: video.playlistVideoRenderer.title.runs[0].text,
|
title: video.playlistVideoRenderer.title.runs[0].text,
|
||||||
url: 'https://www.youtube.com/watch?v=' + video.playlistVideoRenderer.videoId,
|
url: 'https://www.youtube.com/watch?v=' + video.playlistVideoRenderer.videoId,
|
||||||
length: video.playlistVideoRenderer.lengthText.simpleText,
|
length: video.playlistVideoRenderer.lengthText.simpleText,
|
||||||
source: 'youtube',
|
source: 'youtube',
|
||||||
}))
|
}))
|
||||||
return new Playlist({ tracks })
|
return new Playlist({ tracks })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the metadata of a YouTube video
|
* Retrieve the metadata of a YouTube video
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @returns {Track}
|
* @returns {Track}
|
||||||
*/
|
*/
|
||||||
async get_metadata(url) {
|
async get_metadata(url) {
|
||||||
const video_infos = (await ytdl.getBasicInfo(url)).videoDetails
|
const video_infos = (await ytdl.getBasicInfo(url)).videoDetails
|
||||||
return new Track({
|
return new Track({
|
||||||
|
|
Loading…
Reference in New Issue