Add twitch point song requests to the bot

This commits adds new feature for galchedbot: video requests
in the twitch chat via highlighted chat messages. This messages
parsed by the bot and added to the video queue, that can be
accessed by the dedicated web server. Video queue requires
authorization based on random token added to the cookies.
This commit is contained in:
Alex Vanin 2020-01-11 22:38:51 +03:00
parent 65fc1ccad4
commit f0f31a8415
18 changed files with 813 additions and 13 deletions

159
modules/web/server.go Normal file
View file

@ -0,0 +1,159 @@
package web
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"time"
"galched-bot/modules/settings"
"galched-bot/modules/youtube"
)
type (
WebServer struct {
server http.Server
r *youtube.Requester
users map[string]string
authed map[string]struct{}
}
)
func New(s *settings.Settings, r *youtube.Requester) *WebServer {
srv := http.Server{
Addr: s.QueueAddress,
}
webServer := &WebServer{
server: srv,
r: r,
users: s.LoginUsers,
authed: make(map[string]struct{}, 10),
}
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodGet {
return
}
if webServer.IsAuthorized(request) {
http.ServeFile(writer, request, "web/index.html")
} else {
http.Redirect(writer, request, "/login", 301)
}
})
http.HandleFunc("/login", func(writer http.ResponseWriter, request *http.Request) {
if request.Method == http.MethodGet {
http.ServeFile(writer, request, "web/login.html")
return
} else if request.Method != http.MethodPost {
return
}
login := request.FormValue("login")
pwd := request.FormValue("password")
log.Print("web: trying to log in with user: ", login)
if webServer.IsRegistered(login, pwd) {
webServer.Authorize(writer)
} else {
log.Print("web: incorrect password attempt")
}
http.Redirect(writer, request, "/", http.StatusSeeOther)
})
http.HandleFunc("/scripts.js", func(writer http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodGet {
return
}
http.ServeFile(writer, request, "web/scripts.js")
})
http.HandleFunc("/style.css", func(writer http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodGet {
return
}
http.ServeFile(writer, request, "web/style.css")
})
http.HandleFunc("/queue", func(writer http.ResponseWriter, request *http.Request) {
if !webServer.IsAuthorized(request) {
http.Error(writer, "not authorized", http.StatusUnauthorized)
return
}
switch request.Method {
case http.MethodGet:
case http.MethodPost:
body, err := ioutil.ReadAll(request.Body)
if err != nil {
log.Print("web: cannot read body msg, %v", err)
return
}
id := string(body)
if len(id) != youtube.YoutubeIDLength && len(id) > 0 {
log.Printf("web: incorrect data in body, <%s>", id)
return
}
r.Remove(id)
default:
return
}
writer.Header().Set("Content-Type", "application/json")
writer.Header().Set("Access-Control-Allow-Origin", "*")
json.NewEncoder(writer).Encode(webServer.r.List())
})
return webServer
}
func (s WebServer) Start() error {
go func() {
s.server.ListenAndServe()
}()
return nil
}
func (s WebServer) Stop(ctx context.Context) error {
return s.server.Shutdown(ctx)
}
func (s WebServer) IsAuthorized(request *http.Request) bool {
if cookie, err := request.Cookie("session"); err == nil {
if _, ok := s.authed[cookie.Value]; ok {
return true
}
}
return false
}
func (s WebServer) IsRegistered(login, pwd string) bool {
return s.users[login] == pwd
}
func (s WebServer) Authorize(response http.ResponseWriter) {
var byteKey = make([]byte, 16)
rand.Read(byteKey[:])
stringKey := hex.EncodeToString(byteKey)
s.authed[stringKey] = struct{}{}
log.Print("web: authenticated new user")
expires := time.Now().AddDate(0, 1, 0)
http.SetCookie(response, &http.Cookie{
Name: "session",
Value: stringKey,
Path: "/",
Expires: expires,
})
}