Merge pull request #90 from GabrielPlassard/master

Add support for !shuffle, !shuffleon and !shuffleoff commands #39
This commit is contained in:
Matthieu Grieger 2015-10-12 14:01:06 -07:00
commit ae61d8cdf2
6 changed files with 155 additions and 23 deletions

View file

@ -152,6 +152,31 @@ func parseCommand(user *gumble.User, username, command string) {
} else { } else {
dj.SendPrivateMessage(user, NO_PERMISSION_MSG) dj.SendPrivateMessage(user, NO_PERMISSION_MSG)
} }
// Shuffle command
case dj.conf.Aliases.ShuffleAlias:
if dj.HasPermission(username, dj.conf.Permissions.AdminShuffle) {
shuffleSongs(user, username)
} else {
dj.SendPrivateMessage(user, NO_PERMISSION_MSG)
}
// Shuffleon command
case dj.conf.Aliases.ShuffleOnAlias:
if dj.HasPermission(username, dj.conf.Permissions.AdminShuffleToggle) {
toggleAutomaticShuffle(true, user, username)
} else {
dj.SendPrivateMessage(user, NO_PERMISSION_MSG)
}
// Shuffleoff command
case dj.conf.Aliases.ShuffleOffAlias:
if dj.HasPermission(username, dj.conf.Permissions.AdminShuffleToggle) {
toggleAutomaticShuffle(false, user, username)
} else {
dj.SendPrivateMessage(user, NO_PERMISSION_MSG)
}
default: default:
dj.SendPrivateMessage(user, COMMAND_DOESNT_EXIST_MSG) dj.SendPrivateMessage(user, COMMAND_DOESNT_EXIST_MSG)
} }
@ -391,3 +416,29 @@ func deleteSongs() error {
} }
return nil return nil
} }
// shuffles the song list
func shuffleSongs(user *gumble.User, username string) {
if dj.queue.Len() > 1 {
dj.queue.ShuffleSongs()
dj.client.Self.Channel.Send(fmt.Sprintf(SHUFFLE_SUCCESS_MSG, username), false)
} else {
dj.SendPrivateMessage(user, CANT_SHUFFLE_MSG)
}
}
// handles toggling of automatic shuffle playing
func toggleAutomaticShuffle(activate bool, user *gumble.User, username string){
if (dj.conf.General.AutomaticShuffleOn != activate){
dj.conf.General.AutomaticShuffleOn = activate
if (activate){
dj.client.Self.Channel.Send(fmt.Sprintf(SHUFFLE_ON_MESSAGE, username), false)
} else{
dj.client.Self.Channel.Send(fmt.Sprintf(SHUFFLE_OFF_MESSAGE, username), false)
}
} else if (activate){
dj.SendPrivateMessage(user, SHUFFLE_ACTIVATED_ERROR_MESSAGE)
} else{
dj.SendPrivateMessage(user, SHUFFLE_DEACTIVATED_ERROR_MESSAGE)
}
}

View file

@ -26,6 +26,10 @@ DefaultComment = "Hello! I am a bot. Type !help for a list of commands."
# Default Value: 0 # Default Value: 0
MaxSongDuration = 0 MaxSongDuration = 0
# Is playlist shuffling enabled when the bot starts?
# Default Value: false
AutomaticShuffleOn = false
[Cache] [Cache]
# Cache songs as they are downloaded? # Cache songs as they are downloaded?
@ -55,7 +59,7 @@ LowestVolume = 0.01
# DEFAULT VALUE: 0.8 # DEFAULT VALUE: 0.8
HighestVolume = 0.8 HighestVolume = 0.8
[Aliases] [Aliases]
# Alias used for add command # Alias used for add command
@ -126,6 +130,17 @@ CacheSizeAlias = "cachesize"
# DEFAULT VALUE: "kill" # DEFAULT VALUE: "kill"
KillAlias = "kill" KillAlias = "kill"
# Alias used for shuffle command
# DEFAULT VALUE: "shuffle"
ShuffleAlias = "shuffle"
# Alias used for shuffleon command
# DEFAULT VALUE: "shuffleon"
ShuffleOnAlias = "shuffleon"
# Alias used for shuffleoff command
# DEFAULT VALUE: "shuffleoff"
ShuffleOffAlias = "shuffleoff"
[Permissions] [Permissions]
@ -201,3 +216,12 @@ AdminCacheSize = true
# Make kill an admin command? # Make kill an admin command?
# DEFAULT VALUE: true (I recommend never changing this to false) # DEFAULT VALUE: true (I recommend never changing this to false)
AdminKill = true AdminKill = true
# Make shuffle an admin command?
# DEFAULT VALUE: true
AdminShuffle = true
# Make shuffleon and shuffleoff admin commands?
# DEFAULT VALUE: true
AdminShuffleToggle = true

View file

@ -17,11 +17,12 @@ import (
// DjConfig is a Golang struct representation of mumbledj.gcfg file structure for parsing. // DjConfig is a Golang struct representation of mumbledj.gcfg file structure for parsing.
type DjConfig struct { type DjConfig struct {
General struct { General struct {
CommandPrefix string CommandPrefix string
SkipRatio float32 SkipRatio float32
PlaylistSkipRatio float32 PlaylistSkipRatio float32
DefaultComment string DefaultComment string
MaxSongDuration int MaxSongDuration int
AutomaticShuffleOn bool
} }
Cache struct { Cache struct {
Enabled bool Enabled bool
@ -51,25 +52,30 @@ type DjConfig struct {
NumCachedAlias string NumCachedAlias string
CacheSizeAlias string CacheSizeAlias string
KillAlias string KillAlias string
ShuffleAlias string
ShuffleOnAlias string
ShuffleOffAlias string
} }
Permissions struct { Permissions struct {
AdminsEnabled bool AdminsEnabled bool
Admins []string Admins []string
AdminAdd bool AdminAdd bool
AdminAddPlaylists bool AdminAddPlaylists bool
AdminSkip bool AdminSkip bool
AdminHelp bool AdminHelp bool
AdminVolume bool AdminVolume bool
AdminMove bool AdminMove bool
AdminReload bool AdminReload bool
AdminReset bool AdminReset bool
AdminNumSongs bool AdminNumSongs bool
AdminNextSong bool AdminNextSong bool
AdminCurrentSong bool AdminCurrentSong bool
AdminSetComment bool AdminSetComment bool
AdminNumCached bool AdminNumCached bool
AdminCacheSize bool AdminCacheSize bool
AdminKill bool AdminKill bool
AdminShuffle bool
AdminShuffleToggle bool
} }
} }

View file

@ -115,6 +115,9 @@ func FindServiceAndAdd(user *gumble.User, url string) error {
// Starts playing the new song if nothing else is playing // Starts playing the new song if nothing else is playing
if oldLength == 0 && dj.queue.Len() != 0 && !dj.audioStream.IsPlaying() { if oldLength == 0 && dj.queue.Len() != 0 && !dj.audioStream.IsPlaying() {
if (dj.conf.General.AutomaticShuffleOn){
dj.queue.RandomNextSong(true)
}
if err := dj.queue.CurrentSong().Download(); err == nil { if err := dj.queue.CurrentSong().Download(); err == nil {
dj.queue.CurrentSong().Play() dj.queue.CurrentSong().Play()
} else { } else {

View file

@ -11,8 +11,13 @@ import (
"errors" "errors"
"fmt" "fmt"
"time" "time"
"math/rand"
) )
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
// SongQueue type declaration. // SongQueue type declaration.
type SongQueue struct { type SongQueue struct {
queue []Song queue []Song
@ -59,6 +64,9 @@ func (q *SongQueue) NextSong() {
// PeekNext peeks at the next Song and returns it. // PeekNext peeks at the next Song and returns it.
func (q *SongQueue) PeekNext() (Song, error) { func (q *SongQueue) PeekNext() (Song, error) {
if q.Len() > 1 { if q.Len() > 1 {
if dj.conf.General.AutomaticShuffleOn{ //Shuffle mode is active
q.RandomNextSong(false)
}
return q.queue[1], nil return q.queue[1], nil
} }
return nil, errors.New("There isn't a Song coming up next.") return nil, errors.New("There isn't a Song coming up next.")
@ -104,3 +112,22 @@ func (q *SongQueue) PrepareAndPlayNextSong() {
q.OnSongFinished() q.OnSongFinished()
} }
} }
// Shuffles the songqueue using inside-out algorithm
func (q *SongQueue) ShuffleSongs() {
for i := range q.queue[1:] { //Don't touch currently playing song
j := rand.Intn(i + 1)
q.queue[i + 1], q.queue[j + 1] = q.queue[j + 1], q.queue[i + 1]
}
}
// Sets a random song as next song to be played
// queueWasEmpty wether the queue was empty before adding the last song
func (q *SongQueue) RandomNextSong(queueWasEmpty bool){
nextSongIndex := 1
if queueWasEmpty{
nextSongIndex = 0
}
swapIndex := nextSongIndex + rand.Intn(q.Len())
q.queue[nextSongIndex], q.queue[swapIndex] = q.queue[swapIndex], q.queue[nextSongIndex]
}

View file

@ -71,6 +71,24 @@ const CACHE_SIZE_MSG = "The cache is currently %g MB in size."
// Message shown to user when they attempt to issue a cache-related command when caching is not enabled. // Message shown to user when they attempt to issue a cache-related command when caching is not enabled.
const CACHE_NOT_ENABLED_MSG = "The cache is not currently enabled." const CACHE_NOT_ENABLED_MSG = "The cache is not currently enabled."
// Message shown to user when they attempt to shuffle the queue and it has less than 2 elements.
const CANT_SHUFFLE_MSG = "Can't shuffle the queue if there is less than 2 songs."
// Message shown to users when the songqueue has been successfully shuffled.
const SHUFFLE_SUCCESS_MSG = "The current songqueue has been successfully shuffled by <b>%s</b> (starting from next song)."
// Message shown to users when automatic shuffle is activated
const SHUFFLE_ON_MESSAGE = "<b>%s</b> has turned automatic shuffle on."
// Message shown to users when automatic shuffle is deactivated
const SHUFFLE_OFF_MESSAGE = "<b>%s</b> has turned automatic shuffle off."
// Message shown to user when they attempt to enable automatic shuffle while it's already activated
const SHUFFLE_ACTIVATED_ERROR_MESSAGE = "Automatic shuffle is already activated."
// Message shown to user when they attempt to disable automatic shuffle while it's already deactivated
const SHUFFLE_DEACTIVATED_ERROR_MESSAGE = "Automatic shuffle is already deactivated."
// Message shown to channel when a song is added to the queue by a user. // Message shown to channel when a song is added to the queue by a user.
const SONG_ADDED_HTML = ` const SONG_ADDED_HTML = `
<b>%s</b> has added "%s" to the queue. <b>%s</b> has added "%s" to the queue.
@ -107,6 +125,9 @@ const HELP_HTML = `<br/>
<p><b>!reset</b> - An admin command that resets the song queue. </p> <p><b>!reset</b> - An admin command that resets the song queue. </p>
<p><b>!forceskip</b> - An admin command that forces a song skip. </p> <p><b>!forceskip</b> - An admin command that forces a song skip. </p>
<p><b>!forceskipplaylist</b> - An admin command that forces a playlist skip. </p> <p><b>!forceskipplaylist</b> - An admin command that forces a playlist skip. </p>
<p><b>!shuffle</b> - An admin command that shuffles the current queue. </p>
<p><b>!shuffleon</b> - An admin command that enables auto shuffling.</p>
<p><b>!shuffleoff</b> - An admin command that disables auto shuffling.</p>
<p><b>!move </b>- Moves MumbleDJ into channel if it exists.</p> <p><b>!move </b>- Moves MumbleDJ into channel if it exists.</p>
<p><b>!reload</b> - Reloads mumbledj.gcfg configuration settings.</p> <p><b>!reload</b> - Reloads mumbledj.gcfg configuration settings.</p>
<p><b>!setcomment</b> - Sets the comment for the bot.</p> <p><b>!setcomment</b> - Sets the comment for the bot.</p>