diff --git a/commands.go b/commands.go index c5f282f..61c83fb 100644 --- a/commands.go +++ b/commands.go @@ -82,6 +82,13 @@ func parseCommand(user *gumble.User, username, command string) { } else { dj.SendPrivateMessage(user, NO_PERMISSION_MSG) } + // Web command + case dj.conf.Aliases.WebAlias: + if dj.HasPermission(username, dj.conf.Permissions.AdminWeb) { + GetWebAddress(user) + } else { + dj.SendPrivateMessage(user, NO_PERMISSION_MSG) + } // Move command case dj.conf.Aliases.MoveAlias: if dj.HasPermission(username, dj.conf.Permissions.AdminMove) { diff --git a/config.gcfg b/config.gcfg index db67c05..5c7a595 100644 --- a/config.gcfg +++ b/config.gcfg @@ -86,6 +86,10 @@ HelpAlias = "help" # DEFAULT VALUE: "volume" VolumeAlias = "volume" +# Alias used for web address command +# DEFAULT VALUE: "web" +WebAlias = "web" + # Alias used for move command # DEFAULT VALUE: "move" MoveAlias = "move" @@ -162,6 +166,10 @@ AdminHelp = false # DEFAULT VALUE: false AdminVolume = false +# Make web an admin command? +# DEFAULT VALUE: false +AdminWeb = false + # Make move an admin command? # DEFAULT VALUE: true AdminMove = true diff --git a/main.go b/main.go index 6ef1170..bc8d710 100644 --- a/main.go +++ b/main.go @@ -13,9 +13,9 @@ import ( "fmt" "os" "os/user" + "reflect" "strings" "time" - "reflect" "github.com/layeh/gopus" "github.com/layeh/gumble/gumble" @@ -148,8 +148,8 @@ func Verbose(msg string) { } func isNil(a interface{}) bool { - defer func() { recover() }() - return a == nil || reflect.ValueOf(a).IsNil() + defer func() { recover() }() + return a == nil || reflect.ValueOf(a).IsNil() } // dj variable declaration. This is done outside of main() to allow global use. @@ -228,7 +228,9 @@ func main() { if err := dj.client.Connect(); err != nil { fmt.Printf("Could not connect to Mumble server at %s:%s.\n", address, port) os.Exit(1) - } + } + + Webserver() <-dj.keepAlive } diff --git a/parseconfig.go b/parseconfig.go index 9da7ec2..4b4a16a 100644 --- a/parseconfig.go +++ b/parseconfig.go @@ -51,6 +51,7 @@ type DjConfig struct { NumCachedAlias string CacheSizeAlias string KillAlias string + WebAlias string } Permissions struct { AdminsEnabled bool @@ -70,6 +71,7 @@ type DjConfig struct { AdminNumCached bool AdminCacheSize bool AdminKill bool + AdminWeb bool } } diff --git a/service_youtube.go b/service_youtube.go index 7754935..4c60cd0 100644 --- a/service_youtube.go +++ b/service_youtube.go @@ -39,8 +39,7 @@ var youtubeVideoPatterns = []string{ // YOUTUBE SERVICE // --------------- -type YouTube struct { -} +type YouTube struct {} // Name of the service func (y YouTube) ServiceName() string { @@ -405,14 +404,14 @@ func NewYouTubePlaylist(user, id string) (*YouTubePlaylist, error) { } // Retrieve items in playlist - url = fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=2&playlistId=%s&key=%s", + url = fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=%s&key=%s", id, os.Getenv("YOUTUBE_API_KEY")) if apiResponse, err = PerformGetRequest(url); err != nil { return nil, err } numVideos, _ := apiResponse.Int("pageInfo", "totalResults") - if numVideos > 2 { - numVideos = 2 + if numVideos > 50 { + numVideos = 50 } for i := 0; i < numVideos; i++ { diff --git a/songqueue.go b/songqueue.go index 67db542..f1bdc1c 100644 --- a/songqueue.go +++ b/songqueue.go @@ -42,14 +42,16 @@ func (q *SongQueue) CurrentSong() Song { // NextSong moves to the next Song in SongQueue. NextSong() removes the first Song in the queue. func (q *SongQueue) NextSong() { - if s, err := q.PeekNext(); err == nil { - if !isNil(q.CurrentSong().Playlist()) && !isNil(s.Playlist()) { - if q.CurrentSong().Playlist().ID() != s.Playlist().ID() { - q.CurrentSong().Playlist().DeleteSkippers() + if !isNil(q.CurrentSong().Playlist()) { + if s, err := q.PeekNext(); err == nil { + if !isNil(s.Playlist()) { + if q.CurrentSong().Playlist().ID() != s.Playlist().ID() { + q.CurrentSong().Playlist().DeleteSkippers() + } } + } else { + q.CurrentSong().Playlist().DeleteSkippers() } - } else { - q.CurrentSong().Playlist().DeleteSkippers() } q.queue = q.queue[1:] } diff --git a/strings.go b/strings.go index d48e7a7..e8cf2b5 100644 --- a/strings.go +++ b/strings.go @@ -170,3 +170,8 @@ const CURRENT_SONG_HTML = ` const CURRENT_SONG_PLAYLIST_HTML = ` The song currently playing is "%s", added %s from the playlist "%s". ` + +// URL of the server for connecting via a web address +const WEB_ADDRESS = ` + Control mumbledj from a web browser: http://%s:9563/%s +` \ No newline at end of file diff --git a/web.go b/web.go new file mode 100644 index 0000000..76f826b --- /dev/null +++ b/web.go @@ -0,0 +1,90 @@ +package main + +import ( + "fmt" + "io/ioutil" + "math/rand" + "net/http" + "strings" + "time" + + "github.com/layeh/gumble/gumble" +) + +var client_token = make(map[string]string) +var token_client = make(map[string]string) +var external_ip = "" + +type Page struct { + Title string + Body []byte +} + +func (p *Page) save() error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) +} + +func loadPage(title string) (*Page, error) { + filename := title + ".txt" + body, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return &Page{Title: title, Body: body}, nil +} + +func handler(w http.ResponseWriter, r *http.Request) { + var uname = token_client[r.URL.Path[1:]] + if uname == "" { + fmt.Fprintf(w, "I don't know you") + } else { + fmt.Fprintf(w, "Hi there, I love %s!", uname) + } +} + +func Webserver() { + http.HandleFunc("/", handler) + http.ListenAndServe(":9563", nil) + rand.Seed(time.Now().UnixNano()) +} + +func GetWebAddress(user *gumble.User) { + if client_token[user.Name] != "" { + token_client[client_token[user.Name]] = "" + } + // dealing with collisions + var firstLoop = true + for firstLoop || token_client[client_token[user.Name]] != "" { + client_token[user.Name] = randSeq(10) + firstLoop = false + } + token_client[client_token[user.Name]] = user.Name + dj.SendPrivateMessage(user, fmt.Sprintf(WEB_ADDRESS, getIP(), client_token[user.Name], getIP(), client_token[user.Name])) +} + +func getIP() string { + if external_ip != "" { + return external_ip + } else { + if response, err := http.Get("http://myexternalip.com/raw"); err == nil { + defer response.Body.Close() + if response.StatusCode == 200 { + if body, err := ioutil.ReadAll(response.Body); err == nil { + external_ip = strings.TrimSpace(string(body)) + } + } + } + return external_ip + } +} + +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func randSeq(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +}