diff --git a/chatcommands.go b/chatcommands.go
index ec75766..d83f0cf 100644
--- a/chatcommands.go
+++ b/chatcommands.go
@@ -11,158 +11,213 @@ var commands *CommandControl
var colorRegex *regexp.Regexp = regexp.MustCompile(`^#[0-9A-Fa-f]{6}$`)
type CommandControl struct {
- user map[string]CommandFunction
- mod map[string]CommandFunction
- admin map[string]CommandFunction
+ user map[string]Command
+ mod map[string]Command
+ admin map[string]Command
+}
+
+type Command struct {
+ HelpText string
+ Function CommandFunction
}
type CommandFunction func(client *Client, args []string) string
+//type HelpFunction func(client *Client) string
+
func init() {
commands = &CommandControl{
- user: map[string]CommandFunction{
- "me": func(client *Client, args []string) string {
- client.Me(strings.Join(args, " "))
- return ""
+ user: map[string]Command{
+ "me": Command{
+ HelpText: "Display an action message.",
+ Function: func(client *Client, args []string) string {
+ client.Me(strings.Join(args, " "))
+ return ""
+ },
},
- "help": func(client *Client, args []string) string {
- return "I haven't written this yet LUL"
+
+ "help": Command{
+ HelpText: "This help text.",
+ Function: cmdHelp,
},
- "count": func(client *Client, args []string) string {
- return fmt.Sprintf("Users in chat: %d", client.belongsTo.UserCount())
+
+ "count": Command{
+ HelpText: "Display number of users in chat.",
+ Function: func(client *Client, args []string) string {
+ return fmt.Sprintf("Users in chat: %d", client.belongsTo.UserCount())
+ },
},
+
"color": cmdColor,
"colour": cmdColor,
- "w": func(cl *Client, args []string) string {
- return fmt.Sprintf("Name: %s IsMod: %t IsAdmin: %t", cl.name, cl.IsMod, cl.IsAdmin)
- },
- "whoami": func(cl *Client, args []string) string {
- return fmt.Sprintf("Name: %s IsMod: %t IsAdmin: %t", cl.name, cl.IsMod, cl.IsAdmin)
- },
- "auth": func(cl *Client, args []string) string {
- if cl.IsAdmin {
- return "You are already authenticated."
- }
- pw := html.UnescapeString(strings.Join(args, " "))
+ "w": cmdWhoAmI,
+ "whoami": cmdWhoAmI,
- //fmt.Printf("/auth from %s. expecting %q [%X], received %q [%X]\n", cl.name, settings.AdminPassword, settings.AdminPassword, pw, pw)
- if settings.AdminPassword == pw {
- cl.IsMod = true
- cl.IsAdmin = true
- return "Admin rights granted."
- }
-
- // Don't let on that this command exists. Not the most secure, but should be "good enough" LUL.
- return "Invalid command."
- },
- "users": func(cl *Client, args []string) string {
- names := cl.belongsTo.GetNames()
- return strings.Join(names, " ")
- },
- },
-
- mod: map[string]CommandFunction{
- "sv": func(cl *Client, args []string) string {
- if len(args) == 0 {
- return "Missing message"
- }
- svmsg := formatLinks(ParseEmotes(strings.Join(args, " ")))
- cl.belongsTo.AddCmdMsg(fmt.Sprintf(`
%s
`, svmsg))
- return ""
- },
- "playing": func(cl *Client, args []string) string {
- // Clear/hide title if sent with no arguments.
- if len(args) == 1 {
- cl.belongsTo.ClearPlaying()
- //cl.belongsTo.AddMsg(``)
- return ""
- }
- link := ""
- title := ""
-
- // pickout the link (can be anywhere, as long as there are no spaces).
- for _, word := range args {
- word = html.UnescapeString(word)
- if strings.HasPrefix(word, "http://") || strings.HasPrefix(word, "https://") {
- link = word
- } else {
- title = title + " " + word
+ "auth": Command{
+ HelpText: "Authenticate to admin",
+ Function: func(cl *Client, args []string) string {
+ if cl.IsAdmin {
+ return "You are already authenticated."
}
- }
- cl.belongsTo.SetPlaying(title, link)
- //cl.belongsTo.AddMsg(fmt.Sprintf(``, title, link))
- return ""
+ pw := html.UnescapeString(strings.Join(args, " "))
+
+ //fmt.Printf("/auth from %s. expecting %q [%X], received %q [%X]\n", cl.name, settings.AdminPassword, settings.AdminPassword, pw, pw)
+ if settings.AdminPassword == pw {
+ cl.IsMod = true
+ cl.IsAdmin = true
+ return "Admin rights granted."
+ }
+
+ // Don't let on that this command exists. Not the most secure, but should be "good enough" LUL.
+ return "Invalid command."
+ },
},
- "unmod": func(cl *Client, args []string) string {
- if len(args) > 0 && !cl.IsAdmin {
- return "You can only unmod yourself, not others."
- }
- if len(args) == 0 {
- cl.Unmod()
- return "You have unmodded yourself."
- }
-
- if err := cl.belongsTo.Unmod(args[0]); err != nil {
- return err.Error()
- }
-
- return fmt.Sprintf(`%s has been unmodded.`, args[0])
- },
- "kick": func(cl *Client, args []string) string {
- if len(args) == 0 {
- return "Missing name to kick."
- }
- return cl.belongsTo.Kick(args[0])
- },
- "ban": func(cl *Client, args []string) string {
- if len(args) == 0 {
- return "missing name to ban."
- }
- fmt.Printf("[ban] Attempting to ban %s\n", strings.Join(args, ""))
- return cl.belongsTo.Ban(args[0])
- },
- "unban": func(cl *Client, args []string) string {
- if len(args) == 0 {
- return "missing name to unban."
- }
- fmt.Printf("[ban] Attempting to unban %s\n", strings.Join(args, ""))
-
- err := settings.RemoveBan(args[0])
- if err != nil {
- return err.Error()
- }
- return ""
+ "users": Command{
+ HelpText: "Show a list of users in chat",
+ Function: func(cl *Client, args []string) string {
+ names := cl.belongsTo.GetNames()
+ return strings.Join(names, " ")
+ },
},
},
- admin: map[string]CommandFunction{
- "mod": func(cl *Client, args []string) string {
- if len(args) == 0 {
- return "Missing user to mod."
- }
- if err := cl.belongsTo.Mod(args[0]); err != nil {
- return err.Error()
- }
- return fmt.Sprintf(`%s has been modded.`, args[0])
+ mod: map[string]Command{
+ "sv": Command{
+ HelpText: "Send a server announcement message. It will show up red with a border in chat.",
+ Function: func(cl *Client, args []string) string {
+ if len(args) == 0 {
+ return "Missing message"
+ }
+ svmsg := formatLinks(ParseEmotes(strings.Join(args, " ")))
+ cl.belongsTo.AddCmdMsg(fmt.Sprintf(`%s
`, svmsg))
+ return ""
+ },
},
- "reloadplayer": func(cl *Client, args []string) string {
- cl.belongsTo.AddCmdMsg(`[SERVER] Video player reload forced.
`)
- return "Reloading player for all chatters."
- },
- "reloademotes": func(cl *Client, args []string) string {
- cl.ServerMessage("Reloading emotes")
- num, err := LoadEmotes()
- if err != nil {
- fmt.Printf("Unbale to reload emotes: %s\n", err)
- return fmt.Sprintf("ERROR: %s", err)
- }
- fmt.Printf("Loaded %d emotes\n", num)
- return fmt.Sprintf("Emotes loaded: %d", num)
+ "playing": Command{
+ HelpText: "Set the title text and info link.",
+ Function: func(cl *Client, args []string) string {
+ // Clear/hide title if sent with no arguments.
+ if len(args) == 1 {
+ cl.belongsTo.ClearPlaying()
+ return ""
+ }
+ link := ""
+ title := ""
+
+ // pickout the link (can be anywhere, as long as there are no spaces).
+ for _, word := range args {
+ word = html.UnescapeString(word)
+ if strings.HasPrefix(word, "http://") || strings.HasPrefix(word, "https://") {
+ link = word
+ } else {
+ title = title + " " + word
+ }
+ }
+
+ cl.belongsTo.SetPlaying(title, link)
+ return ""
+ },
},
+
+ "unmod": Command{
+ HelpText: "Revoke a user's moderator privilages. Moderators can only unmod themselves.",
+ Function: func(cl *Client, args []string) string {
+ if len(args) > 0 && !cl.IsAdmin {
+ return "You can only unmod yourself, not others."
+ }
+
+ if len(args) == 0 {
+ cl.Unmod()
+ return "You have unmodded yourself."
+ }
+
+ if err := cl.belongsTo.Unmod(args[0]); err != nil {
+ return err.Error()
+ }
+
+ return fmt.Sprintf(`%s has been unmodded.`, args[0])
+ },
+ },
+
+ "kick": Command{
+ HelpText: "Kick a user from chat.",
+ Function: func(cl *Client, args []string) string {
+ if len(args) == 0 {
+ return "Missing name to kick."
+ }
+ return cl.belongsTo.Kick(args[0])
+ },
+ },
+
+ "ban": Command{
+ HelpText: "Ban a user from chat. They will not be able to re-join chat, but will still be able to view the stream.",
+ Function: func(cl *Client, args []string) string {
+ if len(args) == 0 {
+ return "missing name to ban."
+ }
+ fmt.Printf("[ban] Attempting to ban %s\n", strings.Join(args, ""))
+ return cl.belongsTo.Ban(args[0])
+ },
+ },
+
+ "unban": Command{
+ HelpText: "Remove a ban on a user.",
+ Function: func(cl *Client, args []string) string {
+ if len(args) == 0 {
+ return "missing name to unban."
+ }
+ fmt.Printf("[ban] Attempting to unban %s\n", strings.Join(args, ""))
+
+ err := settings.RemoveBan(args[0])
+ if err != nil {
+ return err.Error()
+ }
+ return ""
+ },
+ },
+ },
+
+ admin: map[string]Command{
+ "mod": Command{
+ HelpText: "Grant moderator privilages to a user.",
+ Function: func(cl *Client, args []string) string {
+ if len(args) == 0 {
+ return "Missing user to mod."
+ }
+ if err := cl.belongsTo.Mod(args[0]); err != nil {
+ return err.Error()
+ }
+ return fmt.Sprintf(`%s has been modded.`, args[0])
+ },
+ },
+
+ "reloadplayer": Command{
+ HelpText: "Reload the stream player for everybody in chat.",
+ Function: func(cl *Client, args []string) string {
+ cl.belongsTo.AddCmdMsg(`[SERVER] Video player reload forced.
`)
+ return "Reloading player for all chatters."
+ },
+ },
+
+ "reloademotes": Command{
+ HelpText: "Reload the emotes on the server.",
+ Function: func(cl *Client, args []string) string {
+ cl.ServerMessage("Reloading emotes")
+ num, err := LoadEmotes()
+ if err != nil {
+ fmt.Printf("Unbale to reload emotes: %s\n", err)
+ return fmt.Sprintf("ERROR: %s", err)
+ }
+
+ fmt.Printf("Loaded %d emotes\n", num)
+ return fmt.Sprintf("Emotes loaded: %d", num)
+ },
+ },
+
//"reloadsettings": func(cl *Client, args []string) string {
// return ""
//},
@@ -174,14 +229,14 @@ func (cc *CommandControl) RunCommand(command string, args []string, sender *Clie
// Look for user command
if userCmd, ok := cc.user[command]; ok {
fmt.Printf("[user] %s /%s %s\n", sender.name, command, strings.Join(args, " "))
- return userCmd(sender, args)
+ return userCmd.Function(sender, args)
}
// Look for mod command
if modCmd, ok := cc.mod[command]; ok {
if sender.IsMod || sender.IsAdmin {
fmt.Printf("[mod] %s /%s %s\n", sender.name, command, strings.Join(args, " "))
- return modCmd(sender, args)
+ return modCmd.Function(sender, args)
}
fmt.Printf("[mod REJECTED] %s /%s %s\n", sender.name, command, strings.Join(args, " "))
@@ -192,7 +247,7 @@ func (cc *CommandControl) RunCommand(command string, args []string, sender *Clie
if adminCmd, ok := cc.admin[command]; ok {
if sender.IsAdmin {
fmt.Printf("[admin] %s /%s %s\n", sender.name, command, strings.Join(args, " "))
- return adminCmd(sender, args)
+ return adminCmd.Function(sender, args)
}
fmt.Printf("[admin REJECTED] %s /%s %s\n", sender.name, command, strings.Join(args, " "))
return "You are not the admin Jebaited"
@@ -203,48 +258,102 @@ func (cc *CommandControl) RunCommand(command string, args []string, sender *Clie
return "Invalid command."
}
-func cmdColor(cl *Client, args []string) string {
- // If the caller is priviledged enough, they can change the color of another user
- if len(args) == 2 && (cl.IsMod || cl.IsAdmin) {
- color := ""
- name := ""
- for _, s := range args {
- if strings.HasPrefix(s, "#") {
- color = s
- } else {
- name = s
- }
- }
- if color == "" {
-
- fmt.Printf("[color:mod] %s missing color\n", cl.name)
- return "Missing color"
- }
-
- if err := cl.belongsTo.ForceColorChange(name, color); err != nil {
- return err.Error()
- }
- return fmt.Sprintf("Color changed for user %s to %s\n", name, color)
+func cmdHelp(cl *Client, args []string) string {
+ url := "/help"
+ if cl.IsMod {
+ url = "/modhelp"
}
- // Don't allow an unprivilaged user to change their color if
- // it was changed by a mod
- if cl.IsColorForced {
- fmt.Printf("[color] %s tried to change a forced color\n", cl.name)
- return "You are not allowed to change your color."
+ if cl.IsAdmin {
+ url = "/adminhelp"
}
- if len(args) == 0 {
- cl.color = randomColor()
- return "Random color chosen: " + cl.color
- }
-
- // Change the color of the user
- if !colorRegex.MatchString(args[0]) {
- return "To choose a specific color use the format /color #c029ce. Hex values expected."
- }
-
- cl.color = args[0]
- fmt.Printf("[color] %s new color: %s\n", cl.name, cl.color)
- return "Color changed successfully."
+ return `Opening help in new window.`
+}
+
+var hlpTemplate = `- %s
- %s
`
+
+// Return a full HTML page for the help text. This should probably be rewritten with templates.
+func helpPage(ismod, isadmin bool) string {
+ if commands == nil {
+ return "No commands loaded Jebaited"
+ }
+
+ text := []string{}
+ for key, cmd := range commands.user {
+ text = append(text, fmt.Sprintf(hlpTemplate, key, cmd.HelpText))
+ }
+
+ if ismod {
+ for key, cmd := range commands.mod {
+ text = append(text, fmt.Sprintf(hlpTemplate, key, cmd.HelpText))
+ }
+ }
+
+ if isadmin {
+ for key, cmd := range commands.mod {
+ text = append(text, fmt.Sprintf(hlpTemplate, key, cmd.HelpText))
+ }
+ }
+
+ // This is ugly
+ return `Help` + strings.Join(text, "") + ``
+}
+
+// Commands below have more than one invoking command (aliases).
+
+var cmdColor = Command{
+ HelpText: "Change user color.",
+ Function: func(cl *Client, args []string) string {
+ // If the caller is priviledged enough, they can change the color of another user
+ if len(args) == 2 && (cl.IsMod || cl.IsAdmin) {
+ color := ""
+ name := ""
+ for _, s := range args {
+ if strings.HasPrefix(s, "#") {
+ color = s
+ } else {
+ name = s
+ }
+ }
+ if color == "" {
+
+ fmt.Printf("[color:mod] %s missing color\n", cl.name)
+ return "Missing color"
+ }
+
+ if err := cl.belongsTo.ForceColorChange(name, color); err != nil {
+ return err.Error()
+ }
+ return fmt.Sprintf("Color changed for user %s to %s\n", name, color)
+ }
+
+ // Don't allow an unprivilaged user to change their color if
+ // it was changed by a mod
+ if cl.IsColorForced {
+ fmt.Printf("[color] %s tried to change a forced color\n", cl.name)
+ return "You are not allowed to change your color."
+ }
+
+ if len(args) == 0 {
+ cl.color = randomColor()
+ return "Random color chosen: " + cl.color
+ }
+
+ // Change the color of the user
+ if !colorRegex.MatchString(args[0]) {
+ return "To choose a specific color use the format /color #c029ce. Hex values expected."
+ }
+
+ cl.color = args[0]
+ fmt.Printf("[color] %s new color: %s\n", cl.name, cl.color)
+ return "Color changed successfully."
+ },
+}
+
+var cmdWhoAmI = Command{
+ HelpText: "Shows debug user info",
+ Function: func(cl *Client, args []string) string {
+ return fmt.Sprintf("Name: %s IsMod: %t IsAdmin: %t", cl.name, cl.IsMod, cl.IsAdmin)
+ },
}
diff --git a/chathandler.go b/chathandler.go
index a4464d4..4fe056c 100644
--- a/chathandler.go
+++ b/chathandler.go
@@ -24,6 +24,17 @@ func wsStaticFiles(w http.ResponseWriter, r *http.Request) {
case "/justvideo":
http.ServeFile(w, r, "./static/justvideo.html")
return
+
+ // TODO: use a template for this, lol.
+ case "/help":
+ w.Write([]byte(helpPage(false, false)))
+ return
+ case "/modhelp":
+ w.Write([]byte(helpPage(true, false)))
+ return
+ case "/adminhelp":
+ w.Write([]byte(helpPage(true, true)))
+ return
}
goodPath := r.URL.Path[8:len(r.URL.Path)]
diff --git a/main.go b/main.go
index 5bc806a..d30ff27 100644
--- a/main.go
+++ b/main.go
@@ -96,6 +96,9 @@ func main() {
http.HandleFunc("/favicon.ico", wsStaticFiles)
http.HandleFunc("/justchat", wsStaticFiles)
http.HandleFunc("/justvideo", wsStaticFiles)
+ http.HandleFunc("/help", wsStaticFiles)
+ http.HandleFunc("/modhelp", wsStaticFiles)
+ http.HandleFunc("/adminhelp", wsStaticFiles)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
l.RLock()
diff --git a/static/site.css b/static/site.css
index 58425d7..ccd6947 100644
--- a/static/site.css
+++ b/static/site.css
@@ -84,3 +84,11 @@ span.svmsg {
#playing {
font-size: x-Large;
}
+
+.helptext {
+ color: white;
+}
+
+dt {
+ font-weight: bold;
+}