diff --git a/commands.go b/commands.go
index 5d3d0de..8ff8846 100644
--- a/commands.go
+++ b/commands.go
@@ -152,6 +152,31 @@ func parseCommand(user *gumble.User, username, command string) {
} else {
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:
dj.SendPrivateMessage(user, COMMAND_DOESNT_EXIST_MSG)
}
@@ -391,3 +416,29 @@ func deleteSongs() error {
}
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)
+ }
+}
diff --git a/config.gcfg b/config.gcfg
index db67c05..408f51b 100644
--- a/config.gcfg
+++ b/config.gcfg
@@ -26,6 +26,10 @@ DefaultComment = "Hello! I am a bot. Type !help for a list of commands."
# Default Value: 0
MaxSongDuration = 0
+# Is playlist shuffling enabled when the bot starts?
+# Default Value: false
+AutomaticShuffleOn = false
+
[Cache]
# Cache songs as they are downloaded?
@@ -55,7 +59,7 @@ LowestVolume = 0.01
# DEFAULT VALUE: 0.8
HighestVolume = 0.8
-
+
[Aliases]
# Alias used for add command
@@ -126,6 +130,17 @@ CacheSizeAlias = "cachesize"
# DEFAULT VALUE: "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]
@@ -201,3 +216,12 @@ AdminCacheSize = true
# Make kill an admin command?
# DEFAULT VALUE: true (I recommend never changing this to false)
AdminKill = true
+
+# Make shuffle an admin command?
+# DEFAULT VALUE: true
+AdminShuffle = true
+
+
+# Make shuffleon and shuffleoff admin commands?
+# DEFAULT VALUE: true
+AdminShuffleToggle = true
diff --git a/parseconfig.go b/parseconfig.go
index 9da7ec2..c69e8b3 100644
--- a/parseconfig.go
+++ b/parseconfig.go
@@ -17,11 +17,12 @@ import (
// DjConfig is a Golang struct representation of mumbledj.gcfg file structure for parsing.
type DjConfig struct {
General struct {
- CommandPrefix string
- SkipRatio float32
- PlaylistSkipRatio float32
- DefaultComment string
- MaxSongDuration int
+ CommandPrefix string
+ SkipRatio float32
+ PlaylistSkipRatio float32
+ DefaultComment string
+ MaxSongDuration int
+ AutomaticShuffleOn bool
}
Cache struct {
Enabled bool
@@ -51,25 +52,30 @@ type DjConfig struct {
NumCachedAlias string
CacheSizeAlias string
KillAlias string
+ ShuffleAlias string
+ ShuffleOnAlias string
+ ShuffleOffAlias string
}
Permissions struct {
- AdminsEnabled bool
- Admins []string
- AdminAdd bool
- AdminAddPlaylists bool
- AdminSkip bool
- AdminHelp bool
- AdminVolume bool
- AdminMove bool
- AdminReload bool
- AdminReset bool
- AdminNumSongs bool
- AdminNextSong bool
- AdminCurrentSong bool
- AdminSetComment bool
- AdminNumCached bool
- AdminCacheSize bool
- AdminKill bool
+ AdminsEnabled bool
+ Admins []string
+ AdminAdd bool
+ AdminAddPlaylists bool
+ AdminSkip bool
+ AdminHelp bool
+ AdminVolume bool
+ AdminMove bool
+ AdminReload bool
+ AdminReset bool
+ AdminNumSongs bool
+ AdminNextSong bool
+ AdminCurrentSong bool
+ AdminSetComment bool
+ AdminNumCached bool
+ AdminCacheSize bool
+ AdminKill bool
+ AdminShuffle bool
+ AdminShuffleToggle bool
}
}
diff --git a/service.go b/service.go
index 1e855b5..245947e 100644
--- a/service.go
+++ b/service.go
@@ -115,6 +115,9 @@ func FindServiceAndAdd(user *gumble.User, url string) error {
// Starts playing the new song if nothing else is playing
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 {
dj.queue.CurrentSong().Play()
} else {
diff --git a/songqueue.go b/songqueue.go
index f1bdc1c..f36e973 100644
--- a/songqueue.go
+++ b/songqueue.go
@@ -11,8 +11,13 @@ import (
"errors"
"fmt"
"time"
+ "math/rand"
)
+func init() {
+ rand.Seed(time.Now().UTC().UnixNano())
+}
+
// SongQueue type declaration.
type SongQueue struct {
queue []Song
@@ -59,6 +64,9 @@ func (q *SongQueue) NextSong() {
// PeekNext peeks at the next Song and returns it.
func (q *SongQueue) PeekNext() (Song, error) {
if q.Len() > 1 {
+ if dj.conf.General.AutomaticShuffleOn{ //Shuffle mode is active
+ q.RandomNextSong(false)
+ }
return q.queue[1], nil
}
return nil, errors.New("There isn't a Song coming up next.")
@@ -104,3 +112,22 @@ func (q *SongQueue) PrepareAndPlayNextSong() {
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]
+}
diff --git a/strings.go b/strings.go
index 76b7541..8e19199 100644
--- a/strings.go
+++ b/strings.go
@@ -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.
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 %s (starting from next song)."
+
+// Message shown to users when automatic shuffle is activated
+const SHUFFLE_ON_MESSAGE = "%s has turned automatic shuffle on."
+
+// Message shown to users when automatic shuffle is deactivated
+const SHUFFLE_OFF_MESSAGE = "%s 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.
const SONG_ADDED_HTML = `
%s has added "%s" to the queue.
@@ -107,6 +125,9 @@ const HELP_HTML = `
!reset - An admin command that resets the song queue.
!forceskip - An admin command that forces a song skip.
!forceskipplaylist - An admin command that forces a playlist skip.
+!shuffle - An admin command that shuffles the current queue.
+!shuffleon - An admin command that enables auto shuffling.
+!shuffleoff - An admin command that disables auto shuffling.
!move - Moves MumbleDJ into channel if it exists.
!reload - Reloads mumbledj.gcfg configuration settings.
!setcomment - Sets the comment for the bot.