mirror of
https://github.com/informaticker/discord-jellyfin-bot.git
synced 2024-11-24 18:41:57 +01:00
Env for default member permissions (#185)
* ✨ Add env variable for default member permissions * 🐛 Config loading in environment loader
This commit is contained in:
parent
0de04ecb20
commit
182459f401
@ -22,8 +22,8 @@
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discord-nestjs/common": "^5.2.4",
|
||||
"@discord-nestjs/core": "^5.3.6",
|
||||
"@discord-nestjs/common": "^5.2.5",
|
||||
"@discord-nestjs/core": "^5.3.7",
|
||||
"@discordjs/opus": "^0.9.0",
|
||||
"@discordjs/voice": "^0.16.0",
|
||||
"@jellyfin/sdk": "^0.7.0",
|
||||
@ -45,7 +45,8 @@
|
||||
"rxjs": "^7.8.1",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.13.0",
|
||||
"zod": "^3.21.4"
|
||||
"zod": "^3.21.4",
|
||||
"zod-validation-error": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.4.2",
|
||||
|
@ -1,12 +1,10 @@
|
||||
import { DiscordModule } from '@discord-nestjs/core';
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { Logger, Module, OnModuleInit } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
|
||||
import * as Joi from 'joi';
|
||||
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import { join } from 'path';
|
||||
import { DiscordConfigService } from './clients/discord/discord.config.service';
|
||||
@ -16,22 +14,23 @@ import { CommandModule } from './commands/command.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { PlaybackModule } from './playback/playback.module';
|
||||
import { UpdatesModule } from './updates/updates.module';
|
||||
import {
|
||||
environmentVariablesSchema,
|
||||
getEnvironmentVariables,
|
||||
} from './utils/environment';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
validationSchema: Joi.object({
|
||||
DISCORD_CLIENT_TOKEN: Joi.string().required(),
|
||||
JELLYFIN_SERVER_ADDRESS: Joi.string().uri().required(),
|
||||
JELLYFIN_AUTHENTICATION_USERNAME: Joi.string().required(),
|
||||
JELLYFIN_AUTHENTICATION_PASSWORD: Joi.string().required(),
|
||||
UPDATER_DISABLE_NOTIFICATIONS: Joi.boolean(),
|
||||
LOG_LEVEL: Joi.string()
|
||||
.valid('error', 'warn', 'log', 'debug', 'verbose')
|
||||
.insensitive()
|
||||
.default('log'),
|
||||
PORT: Joi.number().min(1),
|
||||
}),
|
||||
validate(config) {
|
||||
try {
|
||||
const parsed = environmentVariablesSchema.parse(config);
|
||||
return parsed;
|
||||
} catch (err) {
|
||||
throw fromZodError(err);
|
||||
}
|
||||
},
|
||||
}),
|
||||
ServeStaticModule.forRoot({
|
||||
rootPath: join(__dirname, '..', 'client'),
|
||||
@ -52,4 +51,21 @@ import { UpdatesModule } from './updates/updates.module';
|
||||
controllers: [],
|
||||
providers: [],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule implements OnModuleInit {
|
||||
private readonly logger = new Logger(AppModule.name);
|
||||
|
||||
onModuleInit() {
|
||||
const variables = getEnvironmentVariables();
|
||||
|
||||
if (!variables.ALLOW_EVERYONE_FOR_DEFAULT_PERMS) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.warn(
|
||||
'WARNING: You are using a potentially dangerous configuration: Everyone on your server has access to your bot. Ensure, that your bot is properly secured. Disable this by setting the environment variable ALLOW_EVERYONE to false',
|
||||
);
|
||||
this.logger.warn(
|
||||
'WARNING: You are using a feature, that will only work for new server invitations. The permissions on existing servers will not be changed',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,11 @@ export class DiscordVoiceService {
|
||||
}
|
||||
|
||||
playResource(resource: AudioResource<unknown>) {
|
||||
this.logger.debug(`Playing audio resource with volume ${resource.volume}`);
|
||||
this.logger.debug(
|
||||
`Playing audio resource with volume ${
|
||||
resource.volume?.volume ?? 'unknown'
|
||||
}`,
|
||||
);
|
||||
this.createAndReturnOrGetAudioPlayer().play(resource);
|
||||
this.audioResource = resource;
|
||||
}
|
||||
|
@ -6,11 +6,13 @@ import { CommandInteraction } from 'discord.js';
|
||||
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'disconnect',
|
||||
description: 'Join your current voice channel',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
export class DisconnectCommand {
|
||||
constructor(
|
||||
|
@ -5,11 +5,13 @@ import { Injectable } from '@nestjs/common';
|
||||
import { CommandInteraction } from 'discord.js';
|
||||
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'help',
|
||||
description: 'Get help if you're having problems with this bot',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
export class HelpCommand {
|
||||
constructor(private readonly discordMessageService: DiscordMessageService) {}
|
||||
|
@ -6,10 +6,12 @@ import { CommandInteraction } from 'discord.js';
|
||||
|
||||
import { PlaybackService } from '../playback/playback.service';
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Command({
|
||||
name: 'next',
|
||||
description: 'Go to the next track in the playlist',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
@Injectable()
|
||||
export class SkipTrackCommand {
|
||||
|
@ -6,11 +6,13 @@ import { CommandInteraction } from 'discord.js';
|
||||
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'pause',
|
||||
description: 'Pause or resume the playback of the current track',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
export class PausePlaybackCommand {
|
||||
constructor(
|
||||
|
@ -20,19 +20,21 @@ import {
|
||||
InteractionReplyOptions,
|
||||
} from 'discord.js';
|
||||
|
||||
import { PlaybackService } from '../../playback/playback.service';
|
||||
import { formatMillisecondsAsHumanReadable } from '../../utils/timeUtils';
|
||||
import { DiscordMessageService } from '../../clients/discord/discord.message.service';
|
||||
import { DiscordVoiceService } from '../../clients/discord/discord.voice.service';
|
||||
import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service';
|
||||
import { SearchHint } from '../../models/search/SearchHint';
|
||||
import { PlaybackService } from '../../playback/playback.service';
|
||||
import { formatMillisecondsAsHumanReadable } from '../../utils/timeUtils';
|
||||
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
import { PlayCommandParams, SearchType } from './play.params.ts';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'play',
|
||||
description: 'Search for an item on your Jellyfin instance',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
export class PlayItemCommand {
|
||||
private readonly logger: Logger = new Logger(PlayItemCommand.name);
|
||||
|
@ -26,20 +26,23 @@ import { DiscordMessageService } from '../../clients/discord/discord.message.ser
|
||||
import { Track } from '../../models/shared/Track';
|
||||
import { PlaybackService } from '../../playback/playback.service';
|
||||
import { chunkArray } from '../../utils/arrayUtils';
|
||||
import { trimStringToFixedLength, zeroPad } from '../../utils/stringUtils/stringUtils';
|
||||
import {
|
||||
trimStringToFixedLength,
|
||||
zeroPad,
|
||||
} from '../../utils/stringUtils/stringUtils';
|
||||
|
||||
import { Interval } from '@nestjs/schedule';
|
||||
import { lightFormat } from 'date-fns';
|
||||
import { PlaylistInteractionCollector } from './playlist.interaction-collector';
|
||||
import { PlaylistCommandParams } from './playlist.params';
|
||||
import { PlaylistTempCommandData } from './playlist.types';
|
||||
import { tr } from 'date-fns/locale';
|
||||
import { takeCoverage } from 'v8';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'playlist',
|
||||
description: 'Print the current track information',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
@UseInterceptors(CollectorInterceptor)
|
||||
@UseCollectors(PlaylistInteractionCollector)
|
||||
|
@ -6,11 +6,13 @@ import { CommandInteraction } from 'discord.js';
|
||||
|
||||
import { PlaybackService } from '../playback/playback.service';
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'previous',
|
||||
description: 'Go to the previous track',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
export class PreviousTrackCommand {
|
||||
constructor(
|
||||
|
@ -12,10 +12,12 @@ import { JellyfinSearchService } from 'src/clients/jellyfin/jellyfin.search.serv
|
||||
import { SearchHint } from 'src/models/search/SearchHint';
|
||||
import { PlaybackService } from 'src/playback/playback.service';
|
||||
import { RandomCommandParams } from './random.params';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Command({
|
||||
name: 'random',
|
||||
description: 'Enqueues a random selection of tracks to your playlist',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
@Injectable()
|
||||
export class EnqueueRandomItemsCommand {
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { Command, Handler, IA, InjectDiscordClient } from '@discord-nestjs/core';
|
||||
import {
|
||||
Command,
|
||||
Handler,
|
||||
IA,
|
||||
InjectDiscordClient,
|
||||
} from '@discord-nestjs/core';
|
||||
|
||||
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
|
||||
|
||||
@ -8,13 +13,14 @@ import { Client, CommandInteraction, Status } from 'discord.js';
|
||||
|
||||
import { formatDuration, intervalToDuration } from 'date-fns';
|
||||
|
||||
import { Constants } from '../utils/constants';
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { JellyfinService } from '../clients/jellyfin/jellyfin.service';
|
||||
import { Constants } from '../utils/constants';
|
||||
|
||||
@Command({
|
||||
name: 'status',
|
||||
description: 'Display the current status for troubleshooting',
|
||||
defaultMemberPermissions: 'ViewChannel',
|
||||
})
|
||||
@Injectable()
|
||||
export class StatusCommand {
|
||||
|
@ -7,10 +7,12 @@ import { CommandInteraction } from 'discord.js';
|
||||
import { PlaybackService } from '../playback/playback.service';
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Command({
|
||||
name: 'stop',
|
||||
description: 'Stop playback entirely and clear the current playlist',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
@Injectable()
|
||||
export class StopPlaybackCommand {
|
||||
|
@ -6,11 +6,13 @@ import { CommandInteraction, GuildMember } from 'discord.js';
|
||||
|
||||
import { DiscordMessageService } from '../clients/discord/discord.message.service';
|
||||
import { DiscordVoiceService } from '../clients/discord/discord.voice.service';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'summon',
|
||||
description: 'Join your current voice channel',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
export class SummonCommand {
|
||||
private readonly logger = new Logger(SummonCommand.name);
|
||||
|
@ -10,11 +10,13 @@ import { DiscordVoiceService } from 'src/clients/discord/discord.voice.service';
|
||||
import { PlaybackService } from 'src/playback/playback.service';
|
||||
import { sleep } from 'src/utils/timeUtils';
|
||||
import { VolumeCommandParams } from './volume.params';
|
||||
import { defaultMemberPermissions } from 'src/utils/environment';
|
||||
|
||||
@Injectable()
|
||||
@Command({
|
||||
name: 'volume',
|
||||
description: 'Change the volume',
|
||||
defaultMemberPermissions: defaultMemberPermissions,
|
||||
})
|
||||
export class VolumeCommand {
|
||||
private readonly logger = new Logger(VolumeCommand.name);
|
||||
|
@ -2,6 +2,7 @@ import { LogLevel } from '@nestjs/common/services';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
|
||||
function getLoggingLevels(): LogLevel[] {
|
||||
if (!process.env.LOG_LEVEL) {
|
||||
@ -25,8 +26,10 @@ function getLoggingLevels(): LogLevel[] {
|
||||
}
|
||||
}
|
||||
|
||||
export let app: INestApplication;
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
app = await NestFactory.create(AppModule, {
|
||||
logger: getLoggingLevels(),
|
||||
});
|
||||
app.enableShutdownHooks();
|
||||
|
39
src/utils/environment.ts
Normal file
39
src/utils/environment.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { PermissionResolvable } from 'discord.js';
|
||||
import { z } from 'zod';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
import * as env from 'dotenv';
|
||||
env.config();
|
||||
|
||||
export const environmentVariablesSchema = z.object({
|
||||
DISCORD_CLIENT_TOKEN: z.string(),
|
||||
JELLYFIN_SERVER_ADDRESS: z.string().url(),
|
||||
JELLYFIN_AUTHENTICATION_USERNAME: z.string(),
|
||||
JELLYFIN_AUTHENTICATION_PASSWORD: z.string(),
|
||||
UPDATER_DISABLE_NOTIFICATIONS: z
|
||||
.enum(['true', 'false'])
|
||||
.default('false')
|
||||
.transform((value) => Boolean(value)),
|
||||
LOG_LEVEL: z
|
||||
.enum(['ERROR', 'WARN', 'LOG', 'DEBUG', 'VERBOSE'])
|
||||
.default('LOG'),
|
||||
PORT: z.preprocess(
|
||||
(value) => (Number.isInteger(value) ? Number(value) : undefined),
|
||||
z.number().positive().max(9999).default(3000),
|
||||
),
|
||||
ALLOW_EVERYONE_FOR_DEFAULT_PERMS: z
|
||||
.enum(['true', 'false'])
|
||||
.default('false')
|
||||
.transform((value) => Boolean(value)),
|
||||
});
|
||||
|
||||
export const getEnvironmentVariables = () => {
|
||||
try {
|
||||
return environmentVariablesSchema.strip().parse(process.env);
|
||||
} catch (err) {
|
||||
throw fromZodError(err);
|
||||
}
|
||||
};
|
||||
|
||||
export const defaultMemberPermissions: PermissionResolvable | undefined =
|
||||
getEnvironmentVariables().ALLOW_EVERYONE_FOR_DEFAULT_PERMS ? 'ViewChannel' : undefined;
|
21
yarn.lock
21
yarn.lock
@ -374,19 +374,19 @@
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "0.3.9"
|
||||
|
||||
"@discord-nestjs/common@^5.2.4":
|
||||
version "5.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@discord-nestjs/common/-/common-5.2.4.tgz#294aa89f76651a69692acda81513ea99e7c59ebb"
|
||||
integrity sha512-/HbRSqat/3q15nE2OO+QvOSiPopQkHB31KnwEAhzJIUWgqq1l+UdRwqHFm+2pbbcRBQx/o/wpp2ljRgUB1eoGQ==
|
||||
"@discord-nestjs/common@^5.2.5":
|
||||
version "5.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@discord-nestjs/common/-/common-5.2.5.tgz#29ffa962658176e9406ced6220cd165be3f1900c"
|
||||
integrity sha512-sVtXLb1wIiUXOaleLidDC5wfdQrP3DyCjIfQaYgzuoIJPKFAs3FQs0Q43DGXqWj6qeyTaZEr+JgwhYYO4Ju5Ww==
|
||||
dependencies:
|
||||
"@nestjs/mapped-types" "1.2.2"
|
||||
class-transformer "0.5.1"
|
||||
class-validator "0.14.0"
|
||||
|
||||
"@discord-nestjs/core@^5.3.6":
|
||||
version "5.3.6"
|
||||
resolved "https://registry.yarnpkg.com/@discord-nestjs/core/-/core-5.3.6.tgz#ff7f7d456d05ada0cbe51b070b43551d5278c466"
|
||||
integrity sha512-AHPkivFJoL+2G+rqZVzmNGL4X1TGGXvJDgNlULHRN/wn4GXVbHXdB3tLJY5PimgTFtj8jn+XFIbVLlLE/W9kLA==
|
||||
"@discord-nestjs/core@^5.3.7":
|
||||
version "5.3.7"
|
||||
resolved "https://registry.yarnpkg.com/@discord-nestjs/core/-/core-5.3.7.tgz#17da51aadcdd240161b14b8d8b19c299970e8ac1"
|
||||
integrity sha512-wJMNeUAh0ZzN+Q0NAexPsd9oXSE/L/YrBd6/vWmd4t0NF0SgrwOqu5roh7IaB8Q040Ysh7wTqhFvEfHn8PMzhQ==
|
||||
dependencies:
|
||||
class-transformer "0.5.1"
|
||||
|
||||
@ -5477,6 +5477,11 @@ yocto-queue@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zod-validation-error@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-1.3.0.tgz#6a37e8b1896e45362a4e4cf9506eca203fad3c6e"
|
||||
integrity sha512-4WoQnuWnj06kwKR4A+cykRxFmy+CTvwMQO5ogTXLiVx1AuvYYmMjixh7sbkSsQTr1Fvtss6d5kVz8PGeMPUQjQ==
|
||||
|
||||
zod@^3.21.4:
|
||||
version "3.21.4"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
|
||||
|
Loading…
Reference in New Issue
Block a user