2023-03-20 09:55:09 +01:00
{ pkgs , lib , config , options , . . . }:
with lib ;
let
cfg = config . modules . services . jmusicbot ;
in {
options . modules . services . jmusicbot = {
enable = mkOption {
description = ''
JMusicBot is a self-hostable Discord music bot . This service lets
you host multiple instances of it with seperate configurations .
'' ;
type = types . bool ;
default = false ;
} ;
instances = mkOption {
default = { } ;
type = types . attrsOf ( types . submodule {
options = {
enable = mkOption {
type = types . bool ;
default = false ;
} ;
package = mkOption {
type = types . package ;
default = pkgs . jmusicbot ;
} ;
options = mkOption {
description = ''
The JMusicBot config , see here : https://jmusicbot.com/config/
'' ;
type = types . submodule {
options = {
token = mkOption {
type = types . str ;
description = ''
This sets the token for the bot to log in with
This MUST be a bot token ( user tokens will not work )
If you don't know how to get a bot token , please see the guide here :
https://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token
'' ;
} ;
owner = mkOption {
type = types . int ;
description = ''
This sets the owner of the bot
This needs to be the owner's ID ( a 1 7 -18 digit number )
https://github.com/jagrosh/MusicBot/wiki/Finding-Your-User-ID
'' ;
default = 0 ;
} ;
prefix = mkOption {
type = types . str ;
description = ''
This sets the prefix for the bot
The prefix is used to control the commands
If you use ! ! , the play command will be ! ! play
If you do not set this , the prefix will be a mention of the bot ( @ Botname play )
'' ;
default = " @ m e n t i o n " ;
} ;
game = mkOption {
type = types . str ;
description = ''
If you set this , it modifies the default game of the bot
Set this to NONE to have no game
Set this to DEFAULT to use the default game
You can make the game " P l a y i n g X " , " L i s t e n i n g t o X " , or " W a t c h i n g X "
where X is the title . If you don't include an action , it will use the
default of " P l a y i n g "
'' ;
default = " D E F A U L T " ;
} ;
status = mkOption {
type = types . enum [ " O N L I N E " " I D L E " " D N D " " I N V I S I B L E " ] ;
description = ''
If you set this , it will modify the default status of bot
Valid values : ONLINE IDLE DND INVISIBLE
'' ;
default = " O N L I N E " ;
} ;
songinstatus = mkOption {
type = types . bool ;
description = ''
If you set this to true , the bot will list the title of the song it is currently playing in its
" P l a y i n g " status . Note that this will ONLY work if the bot is playing music on ONE guild ;
if the bot is playing on multiple guilds , this will not work .
'' ;
default = false ;
} ;
altprefix = mkOption {
type = types . str ;
description = ''
If you set this , the bot will also use this prefix in addition to
the one provided above
'' ;
default = " N O N E " ;
} ;
success = mkOption {
type = types . str ;
description = ''
If you set this , the bot will also use this prefix in addition to
the one provided above
'' ;
default = " 🎶 " ;
} ;
warning = mkOption {
type = types . str ;
description = ''
If you set this , the bot will also use this prefix in addition to
the one provided above
'' ;
default = " 💡 " ;
} ;
error = mkOption {
type = types . str ;
description = ''
If you set this , the bot will also use this prefix in addition to
the one provided above
'' ;
default = " 🚫 " ;
} ;
loading = mkOption {
type = types . str ;
description = ''
If you set this , the bot will also use this prefix in addition to
the one provided above
'' ;
default = " ⌚ " ;
} ;
searching = mkOption {
type = types . str ;
description = ''
If you set this , the bot will also use this prefix in addition to
the one provided above
'' ;
default = " 🔎 " ;
} ;
help = mkOption {
type = types . str ;
description = ''
If you set this , you change the word used to view the help .
For example , if you set the prefix to ! ! and the help to cmds , you would type
! ! cmds to see the help text
'' ;
default = " h e l p " ;
} ;
npimages = mkOption {
type = types . bool ;
description = ''
If you set this , the " n o w p l a y i n g " command will show youtube thumbnails
Note : If you set this to true , the nowplaying boxes will NOT refresh
This is because refreshing the boxes causes the image to be reloaded
every time it refreshes .
'' ;
default = false ;
} ;
stayinchannel = mkOption {
type = types . bool ;
description = ''
If you set this , the bot will not leave a voice channel after it finishes a queue .
Keep in mind that being connected to a voice channel uses additional bandwith ,
so this option is not recommended if bandwidth is a concern .
'' ;
default = false ;
} ;
maxtime = mkOption {
type = types . int ;
description = ''
This sets the maximum amount of seconds any track loaded can be . If not set or set
to any number less than or equal to zero , there is no maximum time length . This time
restriction applies to songs loaded from any source .
'' ;
default = 0 ;
} ;
alonetimeuntilstop = mkOption {
type = types . int ;
description = ''
This sets the amount of seconds the bot will stay alone on a voice channel until it
automatically leaves the voice channel and clears the queue . If not set or set
to any number less than or equal to zero , the bot won't leave when alone .
'' ;
default = 0 ;
} ;
playlistsfolder = mkOption {
type = types . str ;
description = ''
This sets an alternative folder to be used as the Playlists folder
This can be a relative or absolute path
'' ;
default = " P l a y l i s t s " ;
} ;
updatealerts = mkOption {
type = types . bool ;
description = ''
By default , the bot will DM the owner if the bot is running and a new version of the bot
becomes available . Set this to false to disable this feature .
'' ;
default = true ;
} ;
" l y r i c s . d e f a u l t " = mkOption {
type = types . enum [ " A - Z L y r i c s " " G e n i u s " " M u s i c M a t c h " " L y r i c s F r e a k " ] ;
description = ''
Changing this changes the lyrics provider
Currently available providers : " A - Z L y r i c s " , " G e n i u s " , " M u s i c M a t c h " , " L y r i c s F r e a k "
At the time of writing , I would recommend sticking with A-Z Lyrics or MusicMatch ,
as Genius tends to have a lot of non-song results and you might get something
completely unrelated to what you want .
If you are interested in contributing a provider , please see
https://github.com/jagrosh/JLyrics
'' ;
default = " A - Z L y r i c s " ;
} ;
aliases = mkOption {
type = types . attrsOf ( types . listOf types . str ) ;
description = ''
These settings allow you to configure custom aliases for all commands .
Multiple aliases may be given , separated by commas .
Example 1 : Giving command " p l a y " the alias " p " :
play = [ p ]
Example 2 : Giving command " s e a r c h " the aliases " y t s " and " f i n d " :
search = [ yts , find ]
'' ;
default = {
settings = [ " s t a t u s " ] ;
lyrics = [ ] ;
nowplaying = [ " n p " " c u r r e n t " ] ;
play = [ ] ;
playlists = [ " p l s " ] ;
queue = [ " l i s t " ] ;
remove = [ " d e l e t e " ] ;
scsearch = [ ] ;
search = [ " y t s e a r c h " ] ;
shuffle = [ ] ;
skip = [ " v o t e s k i p " ] ;
prefix = [ " s e t p r e f i x " ] ;
setdj = [ ] ;
settc = [ ] ;
setvc = [ ] ;
forceremove = [ " f o r c e d e l e t e " " m o d r e m o v e " " m o d d e l e t e " ] ;
forceskip = [ " m o d s k i p " ] ;
movetrack = [ " m o v e " ] ;
pause = [ ] ;
playnext = [ ] ;
repeat = [ ] ;
skipto = [ " j u m p t o " ] ;
stop = [ ] ;
volume = [ " v o l " ] ;
} ;
} ;
2023-06-22 11:15:08 +02:00
queuetype = mkOption {
type = types . enum [ " F A I R " " R E G U L A R " ] ;
description = ''
Sets the queue type
FAIR : Each user gets a fair chance at the queue by rearranging it such that no user can fill it up entirely
REGULAR : Queue works as first-come , first-served
'' ;
default = " F A I R " ;
} ;
2023-03-20 09:55:09 +01:00
eval = mkOption {
type = types . bool ;
description = ''
If you set this to true , it will enable the eval command for the bot owner . This command
allows the bot owner to run arbitrary code from the bot's account .
WARNING :
This command can be extremely dangerous . If you don't know what you're doing , you could
cause horrific problems on your Discord server or on whatever computer this bot is running
on . Never run this command unless you are completely positive what you are running .
DO NOT ENABLE THIS IF YOU DON'T KNOW WHAT THIS DOES OR HOW TO USE IT
IF SOMEONE ASKS YOU TO ENABLE THIS , THERE IS AN 11/10 CHANCE THEY ARE TRYING TO SCAM YOU
'' ;
default = false ;
} ;
} ;
} ;
} ;
} ;
} ) ;
} ;
} ;
config = let
dataDir = " / v a r / l i b / j m u s i c b o t " ;
# nabbed from https://github.com/NixOS/nixpkgs/blob/61d8fdca02b4647be8d38a94c6f53a7cf072d717/nixos/modules/services/networking/jitsi-videobridge.nix#L11
toHOCON = x : if isAttrs x && x ? __hocon_envvar then ( " \$ { " + x . __hocon_envvar + " } " )
else if isAttrs x then " { ${ concatStringsSep " , " ( mapAttrsToList ( k : v : '' " ${ k } " : ${ toHOCON v } '' ) x ) } } "
else if isList x then " [ ${ concatMapStringsSep " , " toHOCON x } ] "
else builtins . toJSON x ;
in mkIf cfg . enable {
users . users . jmusicbot = {
group = " j m u s i c b o t " ;
home = dataDir ;
createHome = true ;
isSystemUser = true ;
shell = " ${ pkgs . bash } / b i n / b a s h " ;
} ;
users . groups . jmusicbot = { } ;
system . activationScripts . jmusicbot-data-dir . text = ''
mkdir - p $ { dataDir }
chown jmusicbot:jmusicbot $ { dataDir }
chmod - R 775 $ { dataDir }
'' ;
systemd . services = mapAttrs'
( name : conf :
let
stateDir = " ${ dataDir } / ${ name } / " ;
configFile = builtins . toFile " c o n f i g . t x t " ( toHOCON conf . options ) ;
in {
name = " j m u s i c b o t - ${ name } " ;
value = {
enable = conf . enable ;
# referencing https://jmusicbot.com/running-as-a-service/
description = " J M u s i c B o t i n s t a n c e ${ name } " ;
wantedBy = [ " m u l t i - u s e r . t a r g e t " ] ;
after = [ " n e t w o r k . t a r g e t " ] ;
requires = [ " n e t w o r k . t a r g e t " ] ;
serviceConfig = {
ExecStart = pkgs . writeScript " j m u s i c b o t - s t a r t - ${ name } " ''
#!${pkgs.runtimeShell}
umask u = rwx , g = rwx , o = rx
cd $ { stateDir }
$ { getExe conf . package } - Dconfig = $ { configFile }
'' ;
Restart = " a l w a y s " ;
RestartSec = 20 ;
User = " j m u s i c b o t " ;
} ;
preStart = ''
umask u = rwx , g = rwx , o = rx
mkdir - p $ { stateDir }
cd $ { stateDir }
ln - sf $ { configFile } config . txt
'' ;
} ;
}
) cfg . instances ;
} ;
2023-06-22 11:15:08 +02:00
}