Comparing version 1.1.8 to 1.1.9
{ | ||
"name": "poru", | ||
"version": "1.1.8", | ||
"version": "1.1.9", | ||
"description": "A stable and powefull lavalink client with so many features", | ||
@@ -20,8 +20,17 @@ "main": "index.js", | ||
"dependencies": { | ||
"node-fetch": "^3.2.6", | ||
"ws": "^8.8.0", | ||
"axios": "^0.27.2", | ||
"cheerio": "^1.0.0-rc.12" | ||
} | ||
"cheerio": "^1.0.0-rc.12", | ||
"undici": "^5.6.0", | ||
"ws": "^8.8.0" | ||
}, | ||
"keywords": [ | ||
"music", | ||
"lavalink", | ||
"poru", | ||
"bot", | ||
"spotify", | ||
"apple-music", | ||
"soundcloud", | ||
"discordjs", | ||
"eris" | ||
] | ||
} |
@@ -6,7 +6,7 @@ <p align="center"> | ||
[![Discord](https://img.shields.io/discord/567705326774779944?style=flat-square)](https://discord.gg/Zmmc47Nrh8) | ||
[![Discord](https://img.shields.io/discord/567705326774779944?style=flat-square)](https://discord.gg/Zmmc47Nrh8) | ||
[![npm](https://img.shields.io/npm/v/poru?style=flat-square)](https://www.npmjs.com/package/poru) | ||
![Github Stars](https://img.shields.io/github/stars/parasop/poru?style=flat-square) | ||
![GitHub issues](https://img.shields.io/github/issues-raw/parasop/poru?style=flat-square) | ||
![Snyk Vulnerabilities for npm package](https://img.shields.io/snyk/vulnerabilities/npm/poru?style=flat-square) | ||
![Snyk Vulnerabilities for npm package](https://img.shields.io/snyk/vulnerabilities/npm/poru?style=flat-square) | ||
![NPM](https://img.shields.io/npm/l/poru?style=flat-square) | ||
@@ -16,3 +16,2 @@ | ||
<p align="center"> | ||
@@ -30,2 +29,3 @@ <a href="https://nodei.co/npm/poru/"><img src="https://nodei.co/npm/poru.png?downloads=true&downloadRank=true&stars=true"></a> | ||
# Installation | ||
``` | ||
@@ -40,2 +40,3 @@ // Using npm | ||
# About | ||
To use you need a configured [Lavalink](https://github.com/Frederikam/Lavalink) instance. | ||
@@ -50,11 +51,12 @@ | ||
## Implementation | ||
[Poru Music](https://github.com/parasop/poru-example) **Example bot as guide for beginning.** | ||
[Poru Music](https://github.com/parasop/poru-example) **Example bot as guide for beginning.** | ||
## Example usage basic bot | ||
```javascript | ||
// main file | ||
// Require both libraries | ||
const { Client } = require("discord.js"); | ||
const { Poru } = require("poru"); | ||
const { Client } = require('discord.js'); | ||
const { Poru } = require('poru'); | ||
@@ -67,24 +69,34 @@ // Initiate both main classes | ||
{ | ||
host: "localhost", | ||
password: "youshallnotpass", | ||
host: 'localhost', | ||
password: 'youshallnotpass', | ||
port: 2333, | ||
secure:false | ||
} | ||
secure: false, | ||
}, | ||
]; | ||
// Define if you want to integrate spotify | ||
const spotifyOptions = { | ||
clientID: 'Your Client ID', // You'll find this on https://developers.spotify.com/dashboard/ | ||
clientSecret: 'Your Client Secret', // You'll find this on https://developers.spotify.com/dashboard/ | ||
playlistLimit: 10, // The amount of pages to load when a playlist is searched with each page having 50 tracks. | ||
albumLimit: 5, // The amount of pages to load when a album is searched with each page having 50 tracks. | ||
artistLimit: 5, // The amount of pages to load when a artist is searched with each page having 50 tracks. | ||
searchMarket: 'IN', // The market from where the query should be searched from. Mainly this should contain your country. | ||
}; | ||
// Assign Manager to the client variable | ||
client.poru = new Poru(client,nodes); | ||
client.poru = new Poru(client, nodes); | ||
// Emitted whenever a node connects | ||
client.poru.on("nodeConnect", node => { | ||
console.log(`Node "${node.name}" connected.`) | ||
}) | ||
client.poru.on('nodeConnect', node => { | ||
console.log(`Node "${node.name}" connected.`); | ||
}); | ||
// Emitted whenever a node encountered an error | ||
client.poru.on("nodeError", (node, error) => { | ||
console.log(`Node "${node.name}" encountered an error`) | ||
}) | ||
client.poru.on('nodeError', (node, error) => { | ||
console.log(`Node "${node.name}" encountered an error`); | ||
}); | ||
// Listen for when the client becomes ready | ||
client.once("ready", () => { | ||
client.once('ready', () => { | ||
client.poru.init(client); | ||
@@ -94,10 +106,9 @@ console.log(`Logged in as ${client.user.tag}`); | ||
// this event used to make connections upto date with lavalink | ||
client.on('raw', async d => await client.poru.packetUpdate(d)); | ||
// Finally login at the END of your code | ||
client.login("your bot token here"); | ||
client.login('your bot token here'); | ||
``` | ||
```javascript | ||
@@ -110,10 +121,11 @@ // creating player | ||
selfDeaf: true, | ||
selfMute: false, | ||
}) | ||
// Getting tracks | ||
const resolve = await client.poru.resolve('Ignite',"yt"); | ||
selfMute: false, | ||
}); | ||
// Getting tracks | ||
const resolve = await client.poru.resolve('Ignite', 'yt'); | ||
``` | ||
## Need Help? | ||
Feel free to join our [discord server](https://discord.gg/Zmmc47Nrh8), Give us suggestions and advice about errors and new features. | ||
Feel free to join our [discord server](https://discord.gg/Zmmc47Nrh8), Give us suggestions and advice about errors and new features. | ||
with ❤️ by [Paras](https://github.com/parasop) . |
@@ -1,2 +0,2 @@ | ||
const axios = require("axios") | ||
const { fetch } = require('undici'); | ||
const Track = require("../guild/Track") | ||
@@ -32,4 +32,3 @@ const cheerio = require("cheerio") | ||
const urlType = await this.decodeUrl(url); | ||
const page = await axios | ||
.get(url) | ||
const page = await fetch(url) | ||
.then((res) => res.data) | ||
@@ -36,0 +35,0 @@ .catch(() => undefined); |
@@ -1,253 +0,250 @@ | ||
const fetch = (...args) => import('node-fetch').then(({ | ||
default: fetch | ||
}) => fetch(...args)); | ||
const { fetch } = require('undici'); | ||
const Track = require("../guild/Track") | ||
class Spotify { | ||
constructor(manager) { | ||
this.manager = manager; | ||
this.baseURL = "https://api.spotify.com/v1" | ||
this.spotifyPattern = /^(?:https:\/\/open\.spotify\.com\/(?:user\/[A-Za-z0-9]+\/)?|spotify:)(album|playlist|track|artist)(?:[/:])([A-Za-z0-9]+).*$/ | ||
this.clientID = manager.options.spotify.clientID; | ||
this.clientSecret = manager.options.spotify.clientSecret | ||
this.authorization = Buffer | ||
.from(`${this.clientID}:${this.clientSecret}`) | ||
.toString("base64"); | ||
this.interval = 0; | ||
} | ||
constructor(manager) { | ||
this.manager = manager; | ||
this.baseURL = 'https://api.spotify.com/v1'; | ||
this.spotifyPattern = | ||
/^(?:https:\/\/open\.spotify\.com\/(?:user\/[A-Za-z0-9]+\/)?|spotify:)(album|playlist|track|artist)(?:[/:])([A-Za-z0-9]+).*$/; | ||
this.clientID = manager.options.spotify.clientID; | ||
this.clientSecret = manager.options.spotify.clientSecret; | ||
this.playlistLimit = manager.options.spotify.playlistLimit; | ||
this.albumLimit = manager.options.spotify.albumLimit; | ||
this.artistLimit = manager.options.spotify.artistLimit; | ||
this.searchMarket = manager.options.spotify.searchMarket; | ||
this.authorization = Buffer.from(`${this.clientID}:${this.clientSecret}`).toString('base64'); | ||
this.interval = 0; | ||
} | ||
check(url) { | ||
return this.spotifyPattern.test(url); | ||
} | ||
check(url) { | ||
return this.spotifyPattern.test(url); | ||
} | ||
async requestToken() { | ||
if (this.nextRequest) return; | ||
async requestToken() { | ||
if (this.nextRequest) return; | ||
try { | ||
const data = await fetch("https://accounts.spotify.com/api/token?grant_type=client_credentials", { | ||
method: "POST", | ||
headers: { | ||
Authorization: `Basic ${this.authorization}`, | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
} | ||
}) | ||
try { | ||
const data = await fetch('https://accounts.spotify.com/api/token?grant_type=client_credentials', { | ||
method: 'POST', | ||
headers: { | ||
Authorization: `Basic ${this.authorization}`, | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
}); | ||
const body = await data.json(); | ||
const body = await data.json(); | ||
this.token = `Bearer ${body.access_token}`; | ||
this.interval = body.expires_in * 1000 | ||
} catch (e) { | ||
if (e.status === 400) { | ||
throw new Error("Invalid Spotify client.") | ||
} | ||
} | ||
this.token = `Bearer ${body.access_token}`; | ||
this.interval = body.expires_in * 1000; | ||
} catch (e) { | ||
if (e.status === 400) { | ||
throw new Error('Invalid Spotify client.'); | ||
} | ||
} | ||
} | ||
async renew() { | ||
if (Date.now() >= this.interval) { | ||
await this.requestToken(); | ||
} | ||
async renew() { | ||
if (Date.now() >= this.interval) { | ||
await this.requestToken(); | ||
} | ||
} | ||
async requestData(endpoint) { | ||
await this.renew(); | ||
async requestData(endpoint) { | ||
await this.renew(); | ||
const req = await fetch(`${this.baseURL}${/^\//.test(endpoint) ? endpoint : `/${endpoint}`}`, { | ||
headers: { Authorization: this.token } | ||
}) | ||
const data = await req.json() | ||
return data | ||
} | ||
const req = await fetch(`${this.baseURL}${/^\//.test(endpoint) ? endpoint : `/${endpoint}`}`, { | ||
headers: { Authorization: this.token }, | ||
}); | ||
const data = await req.json(); | ||
return data; | ||
} | ||
async resolve(url) { | ||
if (!this.token) await this.requestToken(); | ||
const [, type, id] = this.spotifyPattern.exec(url) ?? []; | ||
async resolve(url) { | ||
if (!this.token) await this.requestToken() | ||
const [, type, id] = await this.spotifyPattern.exec(url) ?? []; | ||
switch (type) { | ||
case 'playlist': { | ||
return this.fetchPlaylist(id); | ||
} | ||
case 'track': { | ||
return this.fetchTrack(id); | ||
} | ||
case 'album': { | ||
return this.fetchAlbum(id); | ||
} | ||
case 'artist': { | ||
return this.fetchArtist(id); | ||
} | ||
switch (type) { | ||
case "playlist": | ||
{ | ||
return this.fetchPlaylist(id) | ||
} | ||
case "track": | ||
{ | ||
return this.fetchTrack(id) | ||
} | ||
case "album": | ||
{ | ||
return this.fetchAlbum(id) | ||
} | ||
case "artist": | ||
{ | ||
return this.fetchArtist(id); | ||
} | ||
default: { | ||
return this.manager.resolve(url) | ||
} | ||
} | ||
default: { | ||
return this.manager.resolve(url); | ||
} | ||
} | ||
} | ||
async fetchPlaylist(id) { | ||
try { | ||
const playlist = await this.requestData(`/playlists/${id}`) | ||
await this.fetchPlaylistTracks(playlist); | ||
const unresolvedPlaylistTracks = await Promise.all(playlist.tracks.items.map(x => this.buildUnresolved(x.track))) | ||
async fetchPlaylist(id) { | ||
try { | ||
const playlist = await this.requestData(`/playlists/${id}`); | ||
await this.fetchPlaylistTracks(playlist); | ||
const limitedTracks = this.playlistLimit | ||
? playlist.tracks.items.slice(0, this.playlistLimit * 50) | ||
: playlist.tracks.items; | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedPlaylistTracks, | ||
playlist.name | ||
); | ||
const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x.track))); | ||
} catch (e) { | ||
return this.buildResponse(e.status === 404 ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message); | ||
} | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, playlist.name); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.status === 404 ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
); | ||
} | ||
} | ||
async fetchAlbum(id) { | ||
try { | ||
const album = await this.requestData(`/albums/${id}`) | ||
async fetchAlbum(id) { | ||
try { | ||
const album = await this.requestData(`/albums/${id}`); | ||
const unresolvedPlaylistTracks = await Promise.all(album.tracks.items.map(x => this.buildUnresolved(x))); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedPlaylistTracks, | ||
album.name | ||
); | ||
const limitedTracks = this.albumLimit ? album.tracks.items.slice(0, this.albumLimit * 50) : album.tracks.items; | ||
} catch (e) { | ||
return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message); | ||
} | ||
const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, album.name); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
); | ||
} | ||
} | ||
async fetchArtist(id) { | ||
try { | ||
const artist = await this.requestData(`/artists/${id}`) | ||
async fetchArtist(id) { | ||
try { | ||
const artist = await this.requestData(`/artists/${id}`); | ||
const data = await this.requestData(`/artists/${id}/top-tracks?market=US`) | ||
const unresolvedPlaylistTracks = await Promise.all(data.tracks.map(x => this.buildUnresolved(x))); | ||
const data = await this.requestData(`/artists/${id}/top-tracks?market=${this.searchMarket ?? 'US'}`); | ||
return this.buildResponse( | ||
"PLAYLIST_LOADED", | ||
unresolvedPlaylistTracks, | ||
artist.name | ||
); | ||
} catch (e) { | ||
return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message); | ||
} | ||
const limitedTracks = this.artistLimit ? data.tracks.slice(0, this.artistLimit * 50) : data.tracks; | ||
} | ||
const unresolvedPlaylistTracks = await Promise.all(limitedTracks.map(x => this.buildUnresolved(x))); | ||
async fetchTrack(id) { | ||
try { | ||
const data = await this.requestData(`/tracks/${id}`) | ||
const unresolvedTrack = await this.buildUnresolved(data); | ||
return this.buildResponse( | ||
"TRACK_LOADED", | ||
[unresolvedTrack] | ||
); | ||
} catch (e) { | ||
return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message); | ||
} | ||
return this.buildResponse('PLAYLIST_LOADED', unresolvedPlaylistTracks, artist.name); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
); | ||
} | ||
} | ||
async fetch(query) { | ||
try { | ||
if (this.check(query)) return this.resolve(query) | ||
const data = await this.requestData(`/search/?q="${query}"&type=artist,album,track`) | ||
const unresolvedTrack = await this.buildUnresolved(data.tracks.items[0]); | ||
return this.buildResponse( | ||
"TRACK_LOADED", | ||
[unresolvedTrack] | ||
); | ||
} catch (e) { | ||
return this.buildResponse(e.body?.error.message === "invalid id" ? "NO_MATCHES" : "LOAD_FAILED", [], undefined, e.body?.error.message ?? e.message); | ||
} | ||
async fetchTrack(id) { | ||
try { | ||
const data = await this.requestData(`/tracks/${id}`); | ||
const unresolvedTrack = await this.buildUnresolved(data); | ||
return this.buildResponse('TRACK_LOADED', [unresolvedTrack]); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
); | ||
} | ||
} | ||
async fetchPlaylistTracks(spotifyPlaylist) { | ||
let nextPage = spotifyPlaylist.tracks.next; | ||
let pageLoaded = 1; | ||
while (nextPage) { | ||
if (!nextPage) break; | ||
const req = await fetch(nextPage, { | ||
headers: { Authorization: this.token } | ||
}) | ||
const body = await req.json() | ||
if (body.error) break; | ||
spotifyPlaylist.tracks.items.push(...body.items); | ||
async fetch(query) { | ||
try { | ||
if (this.check(query)) return this.resolve(query); | ||
nextPage = body.next; | ||
pageLoaded++; | ||
} | ||
} | ||
const data = await this.requestData( | ||
`/search/?q="${query}"&type=artist,album,track&market=${this.searchMarket ?? 'US'}`, | ||
); | ||
const unresolvedTrack = await this.buildUnresolved(data.tracks.items[0]); | ||
async buildUnresolved(track) { | ||
if (!track) throw new ReferenceError("The Spotify track object was not provided"); | ||
return new Track({ | ||
track: "", | ||
info: { | ||
sourceName: 'spotify', | ||
identifier: track.id, | ||
isSeekable: true, | ||
author: track.artists[0] ? track.artists[0].name : 'Unknown', | ||
length: track.duration_ms, | ||
isStream: false, | ||
title: track.name, | ||
uri: `https://open.spotify.com/track/${track.id}`, | ||
image: track.album?.images[0]?.url, | ||
}, | ||
}) | ||
return this.buildResponse('TRACK_LOADED', [unresolvedTrack]); | ||
} catch (e) { | ||
return this.buildResponse( | ||
e.body?.error.message === 'invalid id' ? 'NO_MATCHES' : 'LOAD_FAILED', | ||
[], | ||
undefined, | ||
e.body?.error.message ?? e.message, | ||
); | ||
} | ||
} | ||
async fetchMetaData(track) { | ||
async fetchPlaylistTracks(spotifyPlaylist) { | ||
let nextPage = spotifyPlaylist.tracks.next; | ||
let pageLoaded = 1; | ||
while (nextPage) { | ||
if (!nextPage) break; | ||
const req = await fetch(nextPage, { | ||
headers: { Authorization: this.token }, | ||
}); | ||
const body = await req.json(); | ||
if (body.error) break; | ||
spotifyPlaylist.tracks.items.push(...body.items); | ||
const fetch = await this.manager.resolve(`${track.info.title} ${track.info.author}`) | ||
return fetch.tracks[0]; | ||
nextPage = body.next; | ||
pageLoaded++; | ||
} | ||
} | ||
async buildTrack(unresolvedTrack) { | ||
const lavaTrack = await this.fetchMetaData(unresolvedTrack); | ||
if (lavaTrack) { | ||
unresolvedTrack.track = lavaTrack.track; | ||
unresolvedTrack.info.identifier = lavaTrack.info.identifier | ||
return unresolvedTrack | ||
} | ||
} | ||
async buildUnresolved(track) { | ||
if (!track) throw new ReferenceError('The Spotify track object was not provided'); | ||
return new Track({ | ||
track: '', | ||
info: { | ||
sourceName: 'spotify', | ||
identifier: track.id, | ||
isSeekable: true, | ||
author: track.artists[0] ? track.artists[0].name : 'Unknown', | ||
length: track.duration_ms, | ||
isStream: false, | ||
title: track.name, | ||
uri: `https://open.spotify.com/track/${track.id}`, | ||
image: track.album?.images[0]?.url, | ||
}, | ||
}); | ||
} | ||
compareValue(value) { | ||
return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined'; | ||
} | ||
async fetchMetaData(track) { | ||
const fetch = await this.manager.resolve(`${track.info.title} ${track.info.author}`); | ||
return fetch.tracks[0]; | ||
} | ||
buildResponse(loadType, tracks, playlistName, exceptionMsg) { | ||
return Object.assign({ | ||
loadType, | ||
tracks, | ||
playlistInfo: playlistName ? { name: playlistName } : {} | ||
}, exceptionMsg ? { exception: { message: exceptionMsg, severity: "COMMON" } } : {}); | ||
async buildTrack(unresolvedTrack) { | ||
const lavaTrack = await this.fetchMetaData(unresolvedTrack); | ||
if (lavaTrack) { | ||
unresolvedTrack.track = lavaTrack.track; | ||
unresolvedTrack.info.identifier = lavaTrack.info.identifier; | ||
return unresolvedTrack; | ||
} | ||
} | ||
compareValue(value) { | ||
return typeof value !== 'undefined' ? value !== null : typeof value !== 'undefined'; | ||
} | ||
buildResponse(loadType, tracks, playlistName, exceptionMsg) { | ||
return Object.assign( | ||
{ | ||
loadType, | ||
tracks, | ||
playlistInfo: playlistName ? { name: playlistName } : {}, | ||
}, | ||
exceptionMsg ? { exception: { message: exceptionMsg, severity: 'COMMON' } } : {}, | ||
); | ||
} | ||
} | ||
module.exports = Spotify | ||
module.exports = Spotify; |
211
src/Poru.js
const { EventEmitter } = require("events"); | ||
const fetch = (...args) => import('node-fetch').then(({ | ||
default: fetch | ||
}) => fetch(...args)); | ||
const { fetch } = require('undici'); | ||
const Player = require("./Player"); | ||
@@ -13,22 +11,20 @@ const Node = require("./Node"); | ||
class Poru extends EventEmitter { | ||
constructor(client, nodes, options = {}) { | ||
super(); | ||
if (!client) throw new Error("[Poru Error] you did't provide a valid client"); | ||
if (!nodes) throw new Error("[Poru Error] you did't provide a lavalink nodes"); | ||
if (!options) throw new Error("[Poru Error] options must be provided!") | ||
this.client = client; | ||
this._nodes = nodes; | ||
this.nodes = new Map(); | ||
this.players = new Map(); | ||
this.voiceStates = new Map(); | ||
this.voiceServers = new Map(); | ||
this.user = null; | ||
this.options = options | ||
this.shards = options.shards || 1; | ||
this.sendData = null; | ||
} | ||
constructor(client, nodes, options = {}) { | ||
super(); | ||
if (!client) throw new Error("[Poru Error] you did't provide a valid client"); | ||
if (!nodes) throw new Error("[Poru Error] you did't provide a lavalink nodes"); | ||
if (!options) throw new Error('[Poru Error] options must be provided!'); | ||
this.client = client; | ||
this._nodes = nodes; | ||
this.nodes = new Map(); | ||
this.players = new Map(); | ||
this.voiceStates = new Map(); | ||
this.voiceServers = new Map(); | ||
this.user = null; | ||
this.options = options; | ||
this.shards = options.shards || 1; | ||
this.sendData = null; | ||
} | ||
//create a node and connect it with lavalink | ||
//create a node and connect it with lavalink | ||
addNode(options) { | ||
@@ -81,3 +77,3 @@ const node = new Node(this, options, this.options); | ||
client.on("raw", async packet => { | ||
await this.#packetUpdate(packet); | ||
await this.packetUpdate(packet); | ||
}) | ||
@@ -119,71 +115,79 @@ | ||
return true; | ||
} | ||
this.sendData({ | ||
op: 4, | ||
d: { | ||
guild_id: data.guild.id || data.guild, | ||
channel_id: data.voiceChannel.id || data.voiceChannel, | ||
self_mute: data.selfMute || false, | ||
self_deaf: data.selfDeaf || true, | ||
}, | ||
}); | ||
return this.#Player(data); | ||
} | ||
setStateUpdate(data) { | ||
if (data.user_id !== this.user) return; | ||
if (data.channel_id) { | ||
const guild = data.guild_id; | ||
init(client) { | ||
this.user = client.user.id; | ||
this.sendData = data => { | ||
const guild = client.guilds.cache.get(data.d.guild_id); | ||
if (guild) guild.shard.send(data); | ||
}; | ||
client.on('raw', async packet => { | ||
await this.#packetUpdate(packet); | ||
}); | ||
this.voiceStates.set(data.guild_id, data); | ||
const server = this.voiceServers.get(guild); | ||
const state = this.voiceStates.get(guild); | ||
if (!server) return false; | ||
const player = this.players.get(guild); | ||
if (!player) return false; | ||
this._nodes.forEach(node => this.addNode(node)); | ||
player.connect({ | ||
sessionId: state ? state.session_id : player.voiceUpdateState.sessionId, | ||
event: server, | ||
}); | ||
return true; | ||
} | ||
this.voiceServers.delete(data.guild_id); | ||
this.voiceStates.delete(data.guild_id); | ||
if (this.options.spotify && this.options.spotify.clientID && this.options.spotify.clientSecret) { | ||
this.spotify = new Spotify(this, { | ||
clientID: this.options.clientID, | ||
clientSecret: this.options.clientSecret, | ||
playlistLimit: this.options.playlistLimit, | ||
albumLimit: this.options.albumLimit, | ||
artistLimit: this.options.artistLimit, | ||
searchMarket: this.options.searchMarket, | ||
}); | ||
} | ||
this.voiceServers.delete(data.guild_id); | ||
this.voiceStates.delete(data.guild_id); | ||
} | ||
#packetUpdate(packet) { | ||
if (!['VOICE_STATE_UPDATE', 'VOICE_SERVER_UPDATE'].includes(packet.t)) return; | ||
const player = this.players.get(packet.d.guild_id); | ||
if (!player) return; | ||
#packetUpdate(packet) { | ||
if (!['VOICE_STATE_UPDATE', 'VOICE_SERVER_UPDATE'].includes(packet.t)) return; | ||
const player = this.players.get(packet.d.guild_id); | ||
if (!player) return; | ||
if (packet.t === "VOICE_SERVER_UPDATE") { | ||
this.setServersUpdate(packet.d); | ||
} | ||
if (packet.t === "VOICE_STATE_UPDATE") { | ||
this.setStateUpdate(packet.d); | ||
} | ||
if (packet.t === 'VOICE_SERVER_UPDATE') { | ||
this.setServersUpdate(packet.d); | ||
} | ||
get leastUsedNodes() { | ||
return [...this.nodes.values()] | ||
.filter((node) => node.isConnected) | ||
.sort((a, b) => { | ||
const aLoad = a.stats.cpu ? (a.stats.cpu.systemLoad / a.stats.cpu.cores) * 100 : 0; | ||
const bLoad = b.stats.cpu ? (b.stats.cpu.systemLoad / b.stats.cpu.cores) * 100 : 0; | ||
return aLoad - bLoad; | ||
}); | ||
if (packet.t === 'VOICE_STATE_UPDATE') { | ||
this.setStateUpdate(packet.d); | ||
} | ||
} | ||
#Player(data) { | ||
const guild = data.guild.id || data.guild; | ||
const Nodes = this.nodes.get(guild); | ||
if (Nodes) return Nodes; | ||
if (this.leastUsedNodes.length === 0) throw new Error("[Poru Error] No nodes are avaliable"); | ||
const node = this.nodes.get(this.leastUsedNodes[0].name | ||
|| this.leastUsedNodes[0].host); | ||
if (!node) throw new Error("[Poru Error] No nodes are avalible"); | ||
get leastUsedNodes() { | ||
return [...this.nodes.values()] | ||
.filter(node => node.isConnected) | ||
.sort((a, b) => { | ||
const aLoad = a.stats.cpu ? (a.stats.cpu.systemLoad / a.stats.cpu.cores) * 100 : 0; | ||
const bLoad = b.stats.cpu ? (b.stats.cpu.systemLoad / b.stats.cpu.cores) * 100 : 0; | ||
return aLoad - bLoad; | ||
}); | ||
} | ||
// eslint-disable-next-line new-cap | ||
const player = new Player(this, node, data); | ||
this.players.set(guild, player); | ||
player.connect() | ||
return player; | ||
} | ||
#Player(data) { | ||
const guild = data.guild.id || data.guild; | ||
const Nodes = this.nodes.get(guild); | ||
if (Nodes) return Nodes; | ||
if (this.leastUsedNodes.length === 0) throw new Error('[Poru Error] No nodes are avaliable'); | ||
const node = this.nodes.get(this.leastUsedNodes[0].name || this.leastUsedNodes[0].host); | ||
if (!node) throw new Error('[Poru Error] No nodes are avalible'); | ||
// eslint-disable-next-line new-cap | ||
const player = new Player(this, node, data); | ||
this.players.set(guild, player); | ||
player.connect(); | ||
return player; | ||
} | ||
async resolve(track, source) { | ||
@@ -221,21 +225,38 @@ | ||
} | ||
const regex = /^https?:\/\//; | ||
if (!regex.test(track)) { | ||
// eslint-disable-next-line no-param-reassign | ||
track = `${source || 'yt'}search:${track}`; | ||
} | ||
const result = await this.#fetch(node, 'loadtracks', `identifier=${encodeURIComponent(track)}`); | ||
#fetch(node, endpoint, param) { | ||
return fetch(`http${node.secure ? "s" : ""}://${node.host}:${node.port}/${endpoint}?${param}`, { | ||
headers: { | ||
Authorization: node.password, | ||
if (!result) throw new Error('[Poru Error] No tracks found.'); | ||
return new Response(result); | ||
} | ||
}, | ||
}) | ||
.then((r) => r.json()) | ||
.catch((e) => { | ||
throw new Error(`[Poru Error] Failed to fetch from the lavalink.\n error: ${e}`); | ||
}); | ||
} | ||
async decodeTrack(track) { | ||
const node = this.leastUsedNodes[0]; | ||
if (!node) throw new Error('No nodes are available.'); | ||
const result = await this.#fetch(node, 'decodetrack', `track=${track}`); | ||
if (result.status === 500) return null; | ||
return result; | ||
} | ||
get(guildId) { | ||
return this.players.get(guildId); | ||
} | ||
#fetch(node, endpoint, param) { | ||
return fetch(`http${node.secure ? 's' : ''}://${node.host}:${node.port}/${endpoint}?${param}`, { | ||
headers: { | ||
Authorization: node.password, | ||
}, | ||
}) | ||
.then(r => r.json()) | ||
.catch(e => { | ||
throw new Error(`[Poru Error] Failed to fetch from the lavalink.\n error: ${e}`); | ||
}); | ||
} | ||
get(guildId) { | ||
return this.players.get(guildId); | ||
} | ||
} | ||
module.exports = Poru | ||
module.exports = Poru; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
52902
3
16
1355
124
6
+ Addedundici@^5.6.0
+ Added@fastify/busboy@2.1.1(transitive)
+ Addedundici@5.28.4(transitive)
- Removedaxios@^0.27.2
- Removednode-fetch@^3.2.6
- Removedasynckit@0.4.0(transitive)
- Removedaxios@0.27.2(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removeddata-uri-to-buffer@4.0.1(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removedfetch-blob@3.2.0(transitive)
- Removedfollow-redirects@1.15.9(transitive)
- Removedform-data@4.0.1(transitive)
- Removedformdata-polyfill@4.0.10(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removednode-domexception@1.0.0(transitive)
- Removednode-fetch@3.3.2(transitive)
- Removedweb-streams-polyfill@3.3.3(transitive)