package wat import ( "crypto/tls" "fmt" "strings" "github.com/go-irc/irc" "github.com/ergochat/irc-go/ircfmt" ) type WatBot struct { client *irc.Client conn *tls.Conn c *WatConfig game *WatGame integration *WatIntegration Db *WatDb Nick string } type WatConfig struct { DatabasePath string BotHosts []string BotGames BotGameConfig AdminHosts []string IgnoredHosts []string AutoJoinChannels []string PermittedChannels []string } func NewWatBot(config *irc.ClientConfig, watConfig *WatConfig, serverConn *tls.Conn) *WatBot { wat := WatBot{conn: serverConn, Nick: config.Nick, c: watConfig} wat.Db = NewWatDb(watConfig.DatabasePath) wat.game = NewWatGame(&wat, wat.Db) wat.integration = NewWatIntegration(&wat, wat.Db, &WatIntegrationConfig{BotHosts: watConfig.BotHosts, BotGames: watConfig.BotGames}) config.Handler = irc.HandlerFunc(wat.HandleIrcMsg) wat.client = irc.NewClient(wat.conn, *config) return &wat } func CleanNick(nick string) string { return string(nick[0]) + "\u200c" + nick[1:] } func PrefixChannel(channel string) string { // there could theoretically be other channel prefixes .. if channel[0] != '#' && channel[0] != '!' { channel = "#" + channel } return channel } func (w *WatBot) HandleIrcMsg(c *irc.Client, m *irc.Message) { switch cmd := m.Command; cmd { case "PING": w.write("PONG", m.Params[0]) case "PRIVMSG": w.Msg(m) case "001": for _, channel := range w.c.AutoJoinChannels { w.write("JOIN", PrefixChannel(channel)) } } } func (w *WatBot) Admin(m *irc.Message) bool { return w.Allowed(m.Prefix.Host, w.c.AdminHosts) } func (w *WatBot) Allowed(c string, r []string) bool { for _, allowed := range r { if c == allowed { return true } } return false } func (w *WatBot) CanRespond(m *irc.Message) bool { if w.Admin(m) { return true } if w.Allowed(m.Prefix.Host, w.c.IgnoredHosts) { return false } // if !strings.Contains(m.Prefix.Host, "") { // return false // } if !w.Allowed(PrefixChannel(m.Params[0]), w.c.PermittedChannels) { return false } return true } func (w *WatBot) Msg(m *irc.Message) { // bail out if you're not yves, if you're not tripsit or if you're not in an allowed channel // but if you're an admin you can do whatever if !w.CanRespond(m) { return } // make sure there's actually some text to process if len(m.Params[1]) == 0 { return } // fieldsfunc allows you to obtain rune separated fields/args args := strings.FieldsFunc(ircfmt.Strip(m.Params[1]), func(c rune) bool { return c == ' ' }) if len(args) == 0 { return } if w.Admin(m) { // allow impersonation of the robot from anywhere if (args[0] == "imp" || args[0] == "imps") && len(args) > 2 { if args[0] == "imps" { w.write(args[1], args[2], strings.Join(args[3:], " ")) } else { w.write(args[1], args[2:]...) } return } } // strip offline message prefix from znc for handling offline buffer if args[0][0] == '[' && len(args) > 1 { args = args[1:] } // integration with games in other bots if w.integration.HandleIntegration(m, args) { return } // check if command char (or something weird) is present if args[0] != "wat" && args[0][0] != '#' { return } // clean input if args[0][0] == '#' { args[0] = args[0][1:] } user := strings.ToLower(m.Prefix.Name) player := w.Db.User(user, m.Prefix.Host, true) w.game.Msg(m, &player, args) } func (w *WatBot) Run() { defer w.conn.Close() err := w.client.Run() if err != nil { fmt.Println("Error returned while running client: " + err.Error()) } } func (w *WatBot) say(dest, msg string) { if len(msg) == 0 { return } //fmt.Printf("MSG %s: %s\n", dest, msg) w.write("PRIVMSG", dest, msg) } func (w *WatBot) reply(s *irc.Message, r string) { sender := s.Params[0] if sender == w.Nick { sender = s.Prefix.Name } w.say(sender, r) } func (w *WatBot) write(cmd string, params ...string) { w.client.WriteMessage(&irc.Message{ Command: cmd, Params: params, }) }