Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
uses: docker/setup-buildx-action@v3

- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Expand Down Expand Up @@ -72,13 +72,13 @@ jobs:
attestations: write
id-token: write
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Set up Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v3

- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Expand Down Expand Up @@ -120,7 +120,7 @@ jobs:
runs-on: ubuntu-latest
needs: combine
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Get version from tag
id: tag_name
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/type-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ jobs:
name: Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npm test
34 changes: 34 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';

export default [
js.configs.recommended,
{
files: ['src/**/*.{ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
project: './tsconfig.json',
},
},
plugins: {
'@typescript-eslint': typescript,
},
rules: {
// Rules from package.json eslintConfig
'new-cap': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/prefer-readonly-parameter-types': 'off',
'@typescript-eslint/no-implicit-any-catch': 'off',

// Basic rules
'no-unused-vars': 'off',
'no-undef': 'off', // TypeScript handles this
'prefer-const': 'warn',
'no-var': 'error',
},
},
];
120 changes: 58 additions & 62 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,32 @@
"env:set-database-url": "tsx src/scripts/run-with-database-url.ts",
"release": "release-it",
"build": "tsc",
"postinstall": "patch-package"
"postinstall": "patch-package",
"prepare": "husky"
},
"devDependencies": {
"@release-it/keep-a-changelog": "^2.3.0",
"@types/bluebird": "^3.5.35",
"@types/debug": "^4.1.5",
"@types/fluent-ffmpeg": "^2.1.17",
"@types/fs-capacitor": "^2.0.0",
"@types/ms": "0.7.34",
"@types/node": "^17.0.0",
"@types/node-emoji": "^1.8.1",
"@types/spotify-web-api-node": "^5.0.2",
"@types/validator": "^13.12.2",
"@types/ws": "8.5.4",
"@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/parser": "^4.31.1",
"eslint": "^7.32.0",
"eslint-config-xo": "^0.39.0",
"eslint-config-xo-typescript": "^0.44.0",
"eslint-plugin-import": "2.29.1",
"husky": "^4.3.8",
"prisma": "5.21.1",
"release-it": "^14.11.8",
"type-fest": "^2.12.0",
"typescript": "^4.6.4"
"@release-it/keep-a-changelog": "^7.0.0",
"@types/bluebird": "^3.5.42",
"@types/debug": "^4.1.12",
"@types/fluent-ffmpeg": "^2.1.27",
"@types/fs-capacitor": "^8.0.2",
"@types/ms": "2.1.0",
"@types/node": "^24.7.2",
"@types/node-emoji": "^2.1.0",
"@types/spotify-web-api-node": "^5.0.11",
"@types/validator": "^13.15.3",
"@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "^8.46.0",
"@typescript-eslint/parser": "^8.46.0",
"eslint": "^9.37.0",
"eslint-config-xo": "^0.49.0",
"eslint-config-xo-typescript": "^9.0.0",
"eslint-plugin-import": "2.32.0",
"husky": "^9.1.7",
"prisma": "6.17.1",
"release-it": "^19.0.5",
"type-fest": "^5.0.1",
"typescript": "^5.9.3"
},
"eslintConfig": {
"extends": [
Expand All @@ -79,55 +80,50 @@
]
}
},
"husky": {
"hooks": {
"pre-commit": "npm test"
}
},
"dependencies": {
"@discordjs/builders": "1.1.0",
"@discordjs/builders": "1.12.2",
"@discordjs/opus": "^0.10.0",
"@discordjs/rest": "1.0.1",
"@discordjs/voice": "0.18.0",
"@distube/ytdl-core": "^4.16.10",
"@discordjs/rest": "2.6.0",
"@discordjs/voice": "0.19.0",
"@distube/ytdl-core": "^4.16.12",
"@distube/ytsr": "^2.0.4",
"@prisma/client": "4.16.0",
"@types/libsodium-wrappers": "^0.7.9",
"array-shuffle": "^3.0.0",
"debug": "^4.3.3",
"delay": "^5.0.0",
"discord-api-types": "0.32.1",
"discord.js": "14.11.0",
"dotenv": "^16.4.5",
"execa": "^6.1.0",
"@prisma/client": "6.17.1",
"@types/libsodium-wrappers": "^0.7.14",
"array-shuffle": "^4.0.0",
"debug": "^4.4.3",
"delay": "^6.0.0",
"discord-api-types": "0.38.29",
"discord.js": "14.23.2",
"dotenv": "^17.2.3",
"execa": "^9.6.0",
"fluent-ffmpeg": "^2.1.3",
"fs-capacitor": "^7.0.1",
"fs-capacitor": "^8.0.0",
"get-youtube-id": "^1.0.1",
"got": "^12.0.2",
"hasha": "^5.2.2",
"inversify": "^6.0.1",
"iso8601-duration": "^2.1.2",
"libsodium-wrappers": "^0.7.9",
"make-dir": "^3.1.0",
"node-emoji": "^1.10.0",
"got": "^14.5.0",
"hasha": "^7.0.0",
"inversify": "^7.10.2",
"iso8601-duration": "^2.1.3",
"libsodium-wrappers": "^0.7.15",
"make-dir": "^5.1.0",
"node-emoji": "^2.2.0",
"nodesplash": "^0.1.1",
"ora": "^8.1.0",
"p-event": "^5.0.1",
"p-limit": "^6.1.0",
"p-queue": "8.1.0",
"p-retry": "6.2.0",
"pagination.djs": "^4.0.10",
"parse-duration": "1.0.2",
"patch-package": "^8.0.0",
"ora": "^9.0.0",
"p-event": "^7.0.0",
"p-limit": "^7.1.1",
"p-queue": "9.0.0",
"p-retry": "7.1.0",
"pagination.djs": "^4.0.18",
"parse-duration": "2.1.4",
"patch-package": "^8.0.1",
"postinstall-postinstall": "^2.1.0",
"read-pkg": "7.1.0",
"read-pkg": "9.0.1",
"reflect-metadata": "^0.2.2",
"sponsorblock-api": "^0.2.4",
"spotify-uri": "^3.0.2",
"spotify-uri": "^4.1.0",
"spotify-web-api-node": "^5.0.2",
"sync-fetch": "^0.3.1",
"tsx": "3.8.2",
"xbytes": "^1.7.0",
"sync-fetch": "^0.5.2",
"tsx": "4.20.6",
"xbytes": "^1.9.1",
"ytsr": "^3.8.4"
},
"resolutions": {
Expand Down
6 changes: 4 additions & 2 deletions src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default class {
}

// Register event handlers
// eslint-disable-next-line complexity
this.client.on('interactionCreate', async interaction => {
try {
if (interaction.isCommand()) {
Expand Down Expand Up @@ -109,7 +109,9 @@ export default class {
} else if (interaction.isCommand() || interaction.isButton()) {
await interaction.reply({content: errorMsg(error as Error), ephemeral: true});
}
} catch {}
} catch {

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Empty catch block should include a parameter name even if unused, as per TypeScript best practices. Consider } catch (_error) { or } catch (error) {.

Suggested change
} catch {
} catch (_error) {

Copilot uses AI. Check for mistakes.
// Ignore errors when replying fails
}
}
});

Expand Down
4 changes: 2 additions & 2 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder} from '@discordjs/builders';
import {SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder, SlashCommandOptionsOnlyBuilder} from '@discordjs/builders';
import {AutocompleteInteraction, ButtonInteraction, ChatInputCommandInteraction} from 'discord.js';

export default interface Command {
readonly slashCommand: Partial<SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder> & Pick<SlashCommandBuilder, 'toJSON'>;
readonly slashCommand: Partial<SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder | SlashCommandOptionsOnlyBuilder> & Pick<SlashCommandBuilder, 'toJSON'>;
readonly handledButtonIds?: readonly string[];
readonly requiresVC?: boolean | ((interaction: ChatInputCommandInteraction) => boolean);
execute: (interaction: ChatInputCommandInteraction) => Promise<void>;
Expand Down
10 changes: 6 additions & 4 deletions src/commands/play.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {AutocompleteInteraction, ChatInputCommandInteraction} from 'discord.js';
import {URL} from 'url';
import {SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder} from '@discordjs/builders';
import {SlashCommandBuilder, SlashCommandSubcommandsOnlyBuilder, SlashCommandOptionsOnlyBuilder} from '@discordjs/builders';
import {inject, injectable, optional} from 'inversify';
import Spotify from 'spotify-web-api-node';
import Command from './index.js';
Expand All @@ -13,7 +13,7 @@ import AddQueryToQueue from '../services/add-query-to-queue.js';

@injectable()
export default class implements Command {
public readonly slashCommand: Partial<SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder> & Pick<SlashCommandBuilder, 'toJSON'>;
public readonly slashCommand: Partial<SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder | SlashCommandOptionsOnlyBuilder> & Pick<SlashCommandBuilder, 'toJSON'>;

public requiresVC = true;

Expand Down Expand Up @@ -75,11 +75,13 @@ export default class implements Command {

try {
// Don't return suggestions for URLs
// eslint-disable-next-line no-new
new URL(query);
await interaction.respond([]);
return;
} catch {}
} catch {

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Empty catch block should include a parameter name even if unused, as per TypeScript best practices. Consider } catch (_error) { or } catch (error) {.

Suggested change
} catch {
} catch (_error) {

Copilot uses AI. Check for mistakes.
// Ignore errors when getting suggestions
}

const suggestions = await this.cache.wrap(
getYouTubeAndSpotifySuggestionsFor,
Expand Down
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import makeDir from 'make-dir';
import {makeDirectory} from 'make-dir';
import path from 'path';
import container from './inversify.config.js';
import {TYPES} from './types.js';
Expand All @@ -12,9 +12,9 @@ const startBot = async () => {
// Create data directories if necessary
const config = container.get<Config>(TYPES.Config);

await makeDir(config.DATA_DIR);
await makeDir(config.CACHE_DIR);
await makeDir(path.join(config.CACHE_DIR, 'tmp'));
await makeDirectory(config.DATA_DIR);
await makeDirectory(config.CACHE_DIR);
await makeDirectory(path.join(config.CACHE_DIR, 'tmp'));

await container.get<FileCacheProvider>(TYPES.FileCache).cleanup();

Expand Down
2 changes: 1 addition & 1 deletion src/services/add-query-to-queue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable complexity */
import {ChatInputCommandInteraction, GuildMember} from 'discord.js';
import {inject, injectable} from 'inversify';
import shuffle from 'array-shuffle';
Expand Down
2 changes: 1 addition & 1 deletion src/services/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default class Config {
if (typeof value === 'number') {
this[key as ConditionalKeys<typeof CONFIG_MAP, number>] = value;
} else if (typeof value === 'string') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(this as any)[key] = value.trim();
} else if (typeof value === 'boolean') {
this[key as ConditionalKeys<typeof CONFIG_MAP, boolean>] = value;
Expand Down
4 changes: 2 additions & 2 deletions src/services/file-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export default class FileCacheProvider {
let totalSizeBytes = await this.getDiskUsageInBytes();
let numOfEvictedFiles = 0;
// Continue to evict until we're under the limit
/* eslint-disable no-await-in-loop */
while (totalSizeBytes > this.config.CACHE_LIMIT_IN_BYTES) {
const oldest = await prisma.fileCache.findFirst({
orderBy: {
Expand All @@ -137,7 +137,7 @@ export default class FileCacheProvider {

totalSizeBytes = await this.getDiskUsageInBytes();
}
/* eslint-enable no-await-in-loop */

if (numOfEvictedFiles > 0) {
debug(`${numOfEvictedFiles} files have been evicted`);
Expand Down
Loading