setup linter

This commit is contained in:
Snowflake107 2021-06-22 16:09:05 +05:45
parent 821e3d1053
commit b485ed8a9f
16 changed files with 94 additions and 53 deletions

7
.eslintignore Normal file
View file

@ -0,0 +1,7 @@
example/
node_modules/
lib/
.github/
docs/
*.d.ts

20
.eslintrc.json Normal file
View file

@ -0,0 +1,20 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": {
"node": true
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/ban-ts-comment": "error"
}
}

View file

@ -172,7 +172,7 @@ client.on("interaction", async (interaction) => {
requestedBy: interaction.user,
searchEngine: interaction.commandName === "soundcloud" ? QueryType.SOUNDCLOUD_SEARCH : QueryType.AUTO
})
.catch(() => {});
.catch(Util.noop);
if (!searchResult || !searchResult.tracks.length) return void interaction.followUp({ content: "No results were found!" });
const queue = await player.createQueue(interaction.guild, {

View file

@ -10,10 +10,12 @@
"scripts": {
"dev": "cd example/test && ts-node index.ts",
"build": "rimraf lib && tsc",
"build:check": "tsc --noEmit --incremental false",
"format": "prettier --write \"src/**/*.ts\" \"example/**/*.ts\"",
"lint": "tslint -p tsconfig.json",
"docs": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml --output docs/docs.json",
"docs:test": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml"
"docs:test": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml",
"lint": "eslint src --ext .ts",
"lint:fix": "eslint src --ext .ts --fix"
},
"funding": "https://github.com/Androz2091/discord-player?sponsor=1",
"contributors": [
@ -68,15 +70,16 @@
"@discordjs/opus": "^0.5.3",
"@types/node": "^15.12.2",
"@types/ws": "^7.4.4",
"@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0",
"discord-api-types": "^0.18.1",
"discord.js": "^13.0.0-dev.c850ae10270076c4b2e10b130dd8f88eed4ed201",
"discord.js-docgen": "discordjs/docgen#ts-patch",
"eslint": "^7.29.0",
"jsdoc-babel": "^0.5.0",
"prettier": "^2.3.1",
"rimraf": "^3.0.2",
"ts-node": "^10.0.0",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.3.2"
"typescript": "^4.3.4"
}
}

View file

@ -8,6 +8,7 @@ import { QueryResolver } from "./utils/QueryResolver";
import YouTube from "youtube-sr";
import { Util } from "./utils/Util";
import Spotify from "spotify-url-info";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { Client as SoundCloud } from "soundcloud-scraper";
import { Playlist } from "./Structures/Playlist";
@ -49,7 +50,7 @@ class Player extends EventEmitter<PlayerEvents> {
this.client.on("voiceStateUpdate", this._handleVoiceState.bind(this));
if (this.options?.autoRegisterExtractor) {
let nv: any;
let nv: any; // eslint-disable-line @typescript-eslint/no-explicit-any
if ((nv = Util.require("@discord-player/extractor"))) {
["Attachment", "Facebook", "Reverbnation", "Vimeo"].forEach((ext) => void this.use(ext, nv[ext]));
@ -139,7 +140,7 @@ class Player extends EventEmitter<PlayerEvents> {
try {
prev.destroy();
} catch {}
} catch {} // eslint-disable-line no-empty
this.queues.delete(guild.id);
return prev;
@ -162,6 +163,7 @@ class Player extends EventEmitter<PlayerEvents> {
options.requestedBy = this.client.users.resolve(options.requestedBy);
if (!("searchEngine" in options)) options.searchEngine = QueryType.AUTO;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_, extractor] of this.extractors) {
if (!extractor.validate(query)) continue;
const data = await extractor.handle(query);
@ -194,11 +196,11 @@ class Player extends EventEmitter<PlayerEvents> {
case QueryType.YOUTUBE_SEARCH: {
const videos = await YouTube.search(query, {
type: "video"
}).catch(() => {});
}).catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
if (!videos) return { playlist: null, tracks: [] };
const tracks = videos.map((m) => {
(m as any).source = "youtube";
(m as any).source = "youtube"; // eslint-disable-line @typescript-eslint/no-explicit-any
return new Track(this, {
title: m.title,
description: m.description,
@ -216,12 +218,13 @@ class Player extends EventEmitter<PlayerEvents> {
}
case QueryType.SOUNDCLOUD_TRACK:
case QueryType.SOUNDCLOUD_SEARCH: {
const result: any[] = QueryResolver.resolve(query) === QueryType.SOUNDCLOUD_TRACK ? [{ url: query }] : await soundcloud.search(query, "track").catch(() => {});
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-function
const result: any[] = QueryResolver.resolve(query) === QueryType.SOUNDCLOUD_TRACK ? [{ url: query }] : await soundcloud.search(query, "track").catch(Util.noop);
if (!result || !result.length) return { playlist: null, tracks: [] };
const res: Track[] = [];
for (const r of result) {
const trackInfo = await soundcloud.getSongInfo(r.url).catch(() => {});
const trackInfo = await soundcloud.getSongInfo(r.url).catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
if (!trackInfo) continue;
const track = new Track(this, {
@ -243,7 +246,7 @@ class Player extends EventEmitter<PlayerEvents> {
return { playlist: null, tracks: res };
}
case QueryType.SPOTIFY_SONG: {
const spotifyData = await Spotify.getData(query).catch(() => {});
const spotifyData = await Spotify.getData(query).catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
if (!spotifyData) return { playlist: null, tracks: [] };
const spotifyTrack = new Track(this, {
title: spotifyData.name,
@ -264,7 +267,7 @@ class Player extends EventEmitter<PlayerEvents> {
}
case QueryType.SPOTIFY_PLAYLIST:
case QueryType.SPOTIFY_ALBUM: {
const spotifyPlaylist = await Spotify.getData(query).catch(() => {});
const spotifyPlaylist = await Spotify.getData(query).catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
if (!spotifyPlaylist) return { playlist: null, tracks: [] };
const playlist = new Playlist(this, {
@ -290,6 +293,7 @@ class Player extends EventEmitter<PlayerEvents> {
});
if (spotifyPlaylist.type !== "playlist") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
playlist.tracks = spotifyPlaylist.tracks.items.map((m: any) => {
const data = new Track(this, {
title: m.name ?? "",
@ -307,6 +311,7 @@ class Player extends EventEmitter<PlayerEvents> {
return data;
}) as Track[];
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
playlist.tracks = spotifyPlaylist.tracks.items.map((m: any) => {
const data = new Track(this, {
title: m.track.name ?? "",
@ -328,7 +333,7 @@ class Player extends EventEmitter<PlayerEvents> {
return { playlist: playlist, tracks: playlist.tracks };
}
case QueryType.SOUNDCLOUD_PLAYLIST: {
const data = await SoundCloud.getPlaylist(query).catch(() => {});
const data = await SoundCloud.getPlaylist(query).catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
if (!data) return { playlist: null, tracks: [] };
const res = new Playlist(this, {
@ -367,11 +372,11 @@ class Player extends EventEmitter<PlayerEvents> {
return { playlist: res, tracks: res.tracks };
}
case QueryType.YOUTUBE_PLAYLIST: {
const ytpl = await YouTube.getPlaylist(query).catch(() => {});
const ytpl = await YouTube.getPlaylist(query).catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
if (!ytpl) return { playlist: null, tracks: [] };
// @todo: better way of handling large playlists
await ytpl.fetch().catch(() => {});
await ytpl.fetch().catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
const playlist: Playlist = new Playlist(this, {
title: ytpl.title,
@ -420,6 +425,7 @@ class Player extends EventEmitter<PlayerEvents> {
* @param {boolean} [force=false] Overwrite existing extractor with this name (if available)
* @returns {ExtractorModel}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
use(extractorName: string, extractor: ExtractorModel | any, force = false): ExtractorModel {
if (!extractorName) throw new Error("Cannot use unknown extractor!");
if (this.extractors.has(extractorName) && !force) return this.extractors.get(extractorName);

View file

@ -2,13 +2,14 @@ import { ExtractorModelData } from "../types/types";
class ExtractorModel {
name: string;
private _raw: any;
private _raw: any; // eslint-disable-line @typescript-eslint/no-explicit-any
/**
* Model for raw Discord Player extractors
* @param {string} extractorName Name of the extractor
* @param {object} data Extractor object
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(extractorName: string, data: any) {
/**
* The extractor name
@ -37,6 +38,7 @@ class ExtractorModel {
return {
playlist: data.playlist ?? null,
data:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data.info?.map((m: any) => ({
title: m.title,
duration: m.duration,

View file

@ -16,7 +16,7 @@ class Playlist {
};
public id: string;
public url: string;
public readonly rawPlaylist?: any;
public readonly rawPlaylist?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
/**
* Playlist constructor

View file

@ -2,7 +2,7 @@ import { Collection, Guild, StageChannel, VoiceChannel } from "discord.js";
import { Player } from "../Player";
import { StreamDispatcher } from "../VoiceInterface/BasicStreamDispatcher";
import Track from "./Track";
import { FiltersName, PlayerOptions, PlayOptions, QueueFilters, QueueRepeatMode } from "../types/types";
import { PlayerOptions, PlayOptions, QueueFilters, QueueRepeatMode } from "../types/types";
import ytdl from "discord-ytdl-core";
import { AudioResource, StreamType } from "@discordjs/voice";
import { Util } from "../utils/Util";
@ -19,9 +19,9 @@ class Queue<T = unknown> {
public playing = false;
public metadata?: T = null;
public repeatMode: QueueRepeatMode = 0;
private _streamTime: number = 0;
private _streamTime = 0;
public _cooldownsTimeout = new Collection<string, NodeJS.Timeout>();
private _activeFilters: any[] = [];
private _activeFilters: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any
private _filtersUpdate = false;
public destroyed = false;
@ -134,7 +134,7 @@ class Queue<T = unknown> {
});
this.connection = connection;
if (channel.type === "stage") await channel.guild.me.voice.setRequestToSpeak(true).catch(() => {});
if (channel.type === "stage") await channel.guild.me.voice.setRequestToSpeak(true).catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
this.connection.on("error", (err) => this.player.emit("connectionError", this, err));
this.connection.on("debug", (msg) => this.player.emit("debug", this, msg));
@ -332,7 +332,7 @@ class Queue<T = unknown> {
});
}
const _filters: any[] = [];
const _filters: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any
for (const filter in filters) {
if (filters[filter as keyof QueueFilters] === true) _filters.push(filter);
@ -466,7 +466,7 @@ class Queue<T = unknown> {
const info = await ytdl
.getInfo(track.url)
.then((x) => x.related_videos[0])
.catch(() => {});
.catch(Util.noop); // eslint-disable-line @typescript-eslint/no-empty-function
if (!info) {
if (this.options.leaveOnEnd) this.destroy();
return void this.player.emit("queueEnd", this);

View file

@ -18,10 +18,10 @@ import Track from "../Structures/Track";
import { Util } from "../utils/Util";
export interface VoiceEvents {
error: (error: AudioPlayerError) => any;
debug: (message: string) => any;
start: () => any;
finish: () => any;
error: (error: AudioPlayerError) => any; // eslint-disable-line @typescript-eslint/no-explicit-any
debug: (message: string) => any; // eslint-disable-line @typescript-eslint/no-explicit-any
start: () => any; // eslint-disable-line @typescript-eslint/no-explicit-any
finish: () => any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
class StreamDispatcher extends EventEmitter<VoiceEvents> {
@ -29,7 +29,7 @@ class StreamDispatcher extends EventEmitter<VoiceEvents> {
public readonly audioPlayer: AudioPlayer;
public readonly channel: VoiceChannel | StageChannel;
public audioResource?: AudioResource<Track>;
private readyLock: boolean = false;
private readyLock = false;
/**
* Creates new connection object
@ -108,6 +108,7 @@ class StreamDispatcher extends EventEmitter<VoiceEvents> {
* @param {object} [ops={}] Options
* @returns {AudioResource}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
createStream(src: Readable | Duplex | string, ops?: { type?: StreamType; data?: any }) {
this.audioResource = createAudioResource(src, {
inputType: ops?.type ?? StreamType.Arbitrary,
@ -134,7 +135,7 @@ class StreamDispatcher extends EventEmitter<VoiceEvents> {
try {
this.audioPlayer.stop(true);
this.voiceConnection.destroy();
} catch {}
} catch {} // eslint-disable-line no-empty
}
/**

View file

@ -52,7 +52,7 @@ class VoiceUtils {
let conn = joinVoiceChannel({
guildId: channel.guild.id,
channelId: channel.id,
adapterCreator: (channel.guild as any).voiceAdapterCreator,
adapterCreator: (channel.guild as any).voiceAdapterCreator, // eslint-disable-line @typescript-eslint/no-explicit-any
selfDeaf: Boolean(options.deaf)
});

View file

@ -84,9 +84,9 @@ export interface RawTrackData {
requestedBy: User;
playlist?: Playlist;
source?: TrackSource;
engine?: any;
engine?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
live?: boolean;
raw?: any;
raw?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
/**
@ -182,7 +182,7 @@ export interface ExtractorModelData {
};
id: string;
url: string;
rawPlaylist?: any;
rawPlaylist?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
};
data: {
title: string;
@ -304,6 +304,7 @@ export enum QueryType {
* @param {Track} track The track
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface PlayerEvents {
botDisconnect: (queue: Queue) => any;
channelEmpty: (queue: Queue) => any;
@ -317,6 +318,8 @@ export interface PlayerEvents {
trackStart: (queue: Queue, track: Track) => any;
}
/* eslint-enable @typescript-eslint/no-explicit-any */
/**
* @typedef {object} PlayOptions
* @property {boolean} [filtersUpdate=false] If this play was triggered for filters update
@ -384,7 +387,7 @@ export interface PlaylistInitData {
};
id: string;
url: string;
rawPlaylist?: any;
rawPlaylist?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
/**

View file

@ -90,7 +90,7 @@ const FilterList = {
},
toString() {
return this.names.map((m) => (this as any)[m]).join(",");
return this.names.map((m) => (this as any)[m]).join(","); // eslint-disable-line @typescript-eslint/no-explicit-any
},
create(filter?: FiltersName[]): string {

View file

@ -1,6 +1,7 @@
import { validateID, validateURL } from "ytdl-core";
import { YouTube } from "youtube-sr";
import { QueryType } from "../types/types";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { validateURL as SoundcloudValidateURL } from "soundcloud-scraper";

View file

@ -14,7 +14,7 @@ class Util {
* @param {object} durObj The duration object
* @returns {string}
*/
static durationString(durObj: object) {
static durationString(durObj: Record<string, number>) {
return Object.values(durObj)
.map((m) => (isNaN(m) ? 0 : m))
.join(":");
@ -58,6 +58,7 @@ class Util {
* @param {any[]} arr The array
* @returns {any}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static last<T = any>(arr: T[]): T {
if (!Array.isArray(arr)) return;
return arr[arr.length - 1];
@ -93,6 +94,10 @@ class Util {
static wait(time: number) {
return new Promise((r) => setTimeout(r, time).unref());
}
static get noop() {
return () => {}; // eslint-disable-line @typescript-eslint/no-empty-function
}
}
export { Util };

8
tsconfig.eslint.json Normal file
View file

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": [
"**/*.ts",
"**/*.js"
],
"exclude": []
}

View file

@ -1,15 +0,0 @@
{
"defaultSeverity": "error",
"extends": ["tslint:recommended", "tslint-config-prettier"],
"jsRules": {
"no-unused-expression": true
},
"rules": {
"object-literal-sort-keys": false,
"interface-name": false,
"no-empty": false,
"no-console": false,
"radix": false
},
"rulesDirectory": []
}