Skip to content

Commit

Permalink
Merge pull request #13 from FAYStarNext/dev
Browse files Browse the repository at this point in the history
Fix: Either remove this useless object instantiation or use it.
  • Loading branch information
EvarinDev authored Jul 23, 2024
2 parents 808cfe5 + 9b5ff2a commit a65d298
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 71 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,29 +96,26 @@ manager.on("NodeConnect", (node) => {
manager.on("NodeRaw", async (node) => {
console.log(`sent raw data: ${JSON.stringify(node)}`);
});
client.on("ready", () => {
manager.init();
});
manager.on("PlayerCreate", (player) => {
console.log(`Player created in guild ${player.guild}`);
});
manager.on("NodeError" , (node, error) => {
console.log(`Node ${node.options.host} has an error: ${error.message}`);
});
client.on("messageCreate", async (message) => {
console.log(message.content)
if (message.author.bot) return;
const start = performance.now();
const [command, ...args] = message.content.slice(0).split(/\s+/g);
console.log(command)
console.log(command === 'play')
if (command === 'play') {
if (!message.member?.voice.channel) return message.reply('you need to join a voice channel.');
if (!args.length) return message.reply('you need to give me a URL or a search term.');

const search = args.join(' ');
let res;
let end;
try {
// Search for tracks using a query or url, using a query searches youtube automatically and the track requester object
res = await manager.search(search, message.author);
res = await manager.search({query: search});
end = `Time took: ${Math.round(performance.now() - start)}ms.`;
// Check the load type as this command is not that advanced for basics
if (res.loadType === 'empty') throw res;
if (res.loadType === 'playlist') {
Expand All @@ -142,15 +139,18 @@ client.on("messageCreate", async (message) => {

// Connect to the voice channel and add the track to the queue
player.connect();
console.log(res)
await player.queue.add(res.tracks[0]);
// Checks if the client should play the track if it's the first one added
if (!player.playing && !player.paused && !player.queue.size) player.play();

return message.reply(`enqueuing ${res.tracks[0].title}.`);
return message.reply(`enqueuing ${res.tracks[0].title}. ${end}`);
}
});
client.on("raw", (data) => manager.updateVoiceState(data));
client.on("ready" , () => {
manager.init(client.user?.id as string);
console.log(`Logged in as ${client.user?.tag} | Memory usage: ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`);
});
client.login(process.env.TOKEN);
```

Expand Down
22 changes: 11 additions & 11 deletions example/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Client } from "discord.js";
import { Manager } from "sunday.ts";
import { Manager } from "../../src";
import "dotenv/config";

let client = new Client({
Expand Down Expand Up @@ -33,29 +33,26 @@ manager.on("NodeConnect", (node) => {
manager.on("NodeRaw", async (node) => {
console.log(`sent raw data: ${JSON.stringify(node)}`);
});
client.on("ready", () => {
manager.init();
});
manager.on("PlayerCreate", (player) => {
console.log(`Player created in guild ${player.guild}`);
});
manager.on("NodeError" , (node, error) => {
console.log(`Node ${node.options.host} has an error: ${error.message}`);
});
client.on("messageCreate", async (message) => {
console.log(message.content)
if (message.author.bot) return;
const start = performance.now();
const [command, ...args] = message.content.slice(0).split(/\s+/g);
console.log(command)
console.log(command === 'play')
if (command === 'play') {
if (!message.member?.voice.channel) return message.reply('you need to join a voice channel.');
if (!args.length) return message.reply('you need to give me a URL or a search term.');

const search = args.join(' ');
let res;
let end;
try {
// Search for tracks using a query or url, using a query searches youtube automatically and the track requester object
res = await manager.search(search, message.author);
res = await manager.search({query: search});
end = `Time took: ${Math.round(performance.now() - start)}ms.`;
// Check the load type as this command is not that advanced for basics
if (res.loadType === 'empty') throw res;
if (res.loadType === 'playlist') {
Expand All @@ -79,13 +76,16 @@ client.on("messageCreate", async (message) => {

// Connect to the voice channel and add the track to the queue
player.connect();
console.log(res)
await player.queue.add(res.tracks[0]);
// Checks if the client should play the track if it's the first one added
if (!player.playing && !player.paused && !player.queue.size) player.play();

return message.reply(`enqueuing ${res.tracks[0].title}.`);
return message.reply(`enqueuing ${res.tracks[0].title}. ${end}`);
}
});
client.on("raw", (data) => manager.updateVoiceState(data));
client.on("ready" , () => {
manager.init(client.user?.id as string);
console.log(`Logged in as ${client.user?.tag} | Memory usage: ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`);
});
client.login(process.env.TOKEN);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "sunday.ts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"version": "1.0.8-indev",
"version": "1.0.9-indev",
"description": "Sunday a lavalink wrapper",
"license": "MIT",
"author": "FAYStarNext",
Expand Down
123 changes: 74 additions & 49 deletions src/structures/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,21 @@ export class Manager extends TypedEmitter<ManagerEvents> {
soundcloud: "scsearch",
deezer: "dzsearch",
};
YOUTUBE_REGEX = /https?:\/\/(www\.)?(youtu\.be|youtube\.com|music\.youtube\.com|m\.youtube\.com)/;
SOUNDCLOUD_REGEX = /^(https?:\/\/)?(www\.)?(soundcloud\.com)\/.+$/;
SPOTIFY_REGEX = /^(https?:\/\/)?(open\.spotify\.com)\/.+$/;
BILIBILITV_REGEX = /^(https?:\/\/)?(www\.)?(bilibili\.tv)\/.+$/;
JOOX_REGEX = /^(https?:\/\/)?(www\.)?(joox\.com)\/.+$/;

/** The map of players. */
public readonly players = new Collection<string, Player>();
public readonly players: Collection<string, Player> = new Collection<string, Player>();
/** The map of nodes. */
public readonly nodes = new Collection<string, Node>();
public readonly nodes: Collection<string, Node> = new Collection<string, Node>();
/** The options that were set. */
public readonly options: ManagerOptions;
private initiated = false;
/** The map of search. */
public readonly search_cache: Map<string, SearchResult> = new Map<string, SearchResult>();

/** Returns the nodes that has the least load. */
private get leastLoadNode(): Collection<string, Node> {
Expand Down Expand Up @@ -127,9 +134,10 @@ export class Manager extends TypedEmitter<ManagerEvents> {
}
}

if (this.options.nodes) {
for (const nodeOptions of this.options.nodes) new (Structure.get("Node"))(nodeOptions);
}
if (this.options.nodes) this.options.nodes.forEach((nodeOptions) => { return new (Structure.get("Node"))(nodeOptions); });
setInterval(() => {
this.search_cache.clear();
}, this.options.cache?.time || 10000);
}

/**
Expand All @@ -139,9 +147,7 @@ export class Manager extends TypedEmitter<ManagerEvents> {
public init(clientId?: string): this {
if (this.initiated) return this;
if (typeof clientId !== "undefined") this.options.clientId = clientId;

if (typeof this.options.clientId !== "string") throw new Error('"clientId" set is not type of "string"');

if (!this.options.clientId) throw new Error('"clientId" is not set. Pass it in Manager#init() or as a option in the constructor.');

for (const node of this.nodes.values()) {
Expand All @@ -162,53 +168,45 @@ export class Manager extends TypedEmitter<ManagerEvents> {
* @param requester
* @returns The search result.
*/
public async search(query: string | SearchQuery, requester?: User | ClientUser): Promise<SearchResult> {
const node = this.useableNodes;

if (!node) {
throw new Error("No available nodes.");
}

const _query: SearchQuery = typeof query === "string" ? { query } : query;
public async search(options: SearchQuery): Promise<SearchResult> {
const node = this.useableNodes;
if (!node) throw new Error("No available nodes.");
const _query: SearchQuery = typeof options.query === "string" ? { query: options.query } : options.query;
const _source = Manager.DEFAULT_SOURCES[_query.source ?? this.options.defaultSearchPlatform] ?? _query.source;

let search = _query.query;

if (!/^https?:\/\//.test(search)) {
search = `${_source}:${search}`;
}
if (!/^https?:\/\//.test(search)) search = `${_source}:${search}`;
if (this.search_cache.get(String(search))) {
let data = await this.search_cache.get(String(search))
return data
};

try {
const res = (await node.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(search)}`)) as LavalinkResponse;

if (!res) {
throw new Error("Query not found.");
}
if (!res) throw new Error("Query not found.");

let searchData = [];
let playlistData: PlaylistRawData | undefined;

switch (res.loadType) {
case "search":
searchData = res.data as TrackData[];
break;

case "track":
searchData = [res.data as TrackData[]];
break;

case "playlist":
playlistData = res.data as PlaylistRawData;
break;
}

const tracks = searchData.map((track) => TrackUtils.build(track, requester));
const tracks = searchData.map((track) => TrackUtils.build(track, options.requester));
let playlist = null;

if (res.loadType === "playlist") {
playlist = {
name: playlistData!.info.name,
tracks: playlistData!.tracks.map((track) => TrackUtils.build(track, requester)),
tracks: playlistData!.tracks.map((track) => TrackUtils.build(track, options.requester)),
duration: playlistData!.tracks.reduce((acc, cur) => acc + (cur.info.length || 0), 0),
};
}
Expand All @@ -222,13 +220,13 @@ export class Manager extends TypedEmitter<ManagerEvents> {
if (this.options.replaceYouTubeCredentials) {
let tracksToReplace: Track[] = [];
if (result.loadType === "playlist") {
tracksToReplace = result.playlist.tracks;
tracksToReplace = result.playlist!.tracks;
} else {
tracksToReplace = result.tracks;
}

for (const track of tracksToReplace) {
if (isYouTubeURL(track.uri)) {
if (this.YOUTUBE_REGEX.test(track.uri)) {
track.author = track.author.replace("- Topic", "");
track.title = track.title.replace("Topic -", "");
}
Expand All @@ -240,16 +238,35 @@ export class Manager extends TypedEmitter<ManagerEvents> {
}
}

if (res.loadType === "search") await this.search_cache.set(String(search), result);

return result;
} catch (err) {
throw new Error(err);
}
}

function isYouTubeURL(uri: string): boolean {
return uri.includes("youtube.com") || uri.includes("youtu.be");
private CheckURL(uri: string): string {
let data = this.regex_link(uri);
switch (data) {
case "yt":
return "yt:" + uri.replace("https://www.youtube.com/watch?v=", "");
}
}

private isLink(link: string) {
return /^(https?:\/\/)?(www\.)?([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)\/.+$/.test(link);
}

private regex_link(link: string) {
if (this.YOUTUBE_REGEX.test(link)) return "yt";
if (this.SOUNDCLOUD_REGEX.test(link)) return "sc";
if (this.SPOTIFY_REGEX.test(link)) return "sp";
if (this.BILIBILITV_REGEX.test(link)) return "bi:tv";
if (this.JOOX_REGEX.test(link)) return "jx";
if (this.isLink(link)) return "http";
return null;
}
/**
* Decodes the base64 encoded tracks and returns a TrackData array.
* @param tracks
Expand Down Expand Up @@ -412,6 +429,12 @@ export interface ManagerOptions {
defaultSearchPlatform?: SearchPlatform;
/** Whether the YouTube video titles should be replaced if the Author does not exactly match. */
replaceYouTubeCredentials?: boolean;
cache?: {
/** Whether to enable cache. */
enable: boolean;
/** Clear cache every second */
time: number;
}
/**
* Function to send data to the websocket.
* @param id
Expand All @@ -427,6 +450,8 @@ export interface SearchQuery {
source?: SearchPlatform | string;
/** The query to search for. */
query: string;
requester?: User | ClientUser;
cache?: boolean
}

export interface LavalinkResponse {
Expand Down Expand Up @@ -464,22 +489,22 @@ export interface PlaylistData {
}

export interface ManagerEvents {
NodeCreate: (node: Node) => void;
NodeDestroy: (node: Node) => void;
NodeConnect: (node: Node) => void;
NodeReconnect: (node: Node) => void;
NodeDisconnect: (node: Node, reason: { code?: number; reason?: string }) => void;
NodeError: (node: Node, error: Error) => void;
NodeRaw: (payload: unknown) => void;
PlayerCreate: (player: Player) => void;
PlayerDestroy: (player: Player) => void;
PlayerStateUpdate: (oldPlayer: Player, newPlayer: Player) => void;
PlayerMove: (player: Player, initChannel: string, newChannel: string) => void;
PlayerDisconnect: (player: Player, oldChannel: string) => void;
QueueEnd: (player: Player, track: Track | UnresolvedTrack, payload: TrackEndEvent) => void;
SocketClosed: (player: Player, payload: WebSocketClosedEvent) => void;
TrackStart: (player: Player, track: Track, payload: TrackStartEvent) => void;
TrackEnd: (player: Player, track: Track, payload: TrackEndEvent) => void;
TrackStuck: (player: Player, track: Track, payload: TrackStuckEvent) => void;
TrackError: (player: Player, track: Track | UnresolvedTrack, payload: TrackExceptionEvent) => void;
NodeCreate: (node: Node) => void;
NodeDestroy: (node: Node) => void;
NodeConnect: (node: Node) => void;
NodeReconnect: (node: Node) => void;
NodeDisconnect: (node: Node, reason: { code?: number; reason?: string }) => void;
NodeError: (node: Node, error: Error) => void;
NodeRaw: (payload: unknown) => void;
PlayerCreate: (player: Player) => void;
PlayerDestroy: (player: Player) => void;
PlayerStateUpdate: (oldPlayer: Player, newPlayer: Player) => void;
PlayerMove: (player: Player, initChannel: string, newChannel: string) => void;
PlayerDisconnect: (player: Player, oldChannel: string) => void;
QueueEnd: (player: Player, track: Track | UnresolvedTrack, payload: TrackEndEvent) => void;
SocketClosed: (player: Player, payload: WebSocketClosedEvent) => void;
TrackStart: (player: Player, track: Track, payload: TrackStartEvent) => void;
TrackEnd: (player: Player, track: Track, payload: TrackEndEvent) => void;
TrackStuck: (player: Player, track: Track, payload: TrackStuckEvent) => void;
TrackError: (player: Player, track: Track | UnresolvedTrack, payload: TrackExceptionEvent) => void;
}

0 comments on commit a65d298

Please sign in to comment.