package main import ( "log" "strings" "time" "net/url" "github.com/mvdan/xurls" "github.com/thoj/go-ircevent" "gopkg.in/alecthomas/kingpin.v2" "github.com/icedream/irc-medialink/manager" "github.com/icedream/irc-medialink/parsers/soundcloud" "github.com/icedream/irc-medialink/parsers/web" "github.com/icedream/irc-medialink/parsers/wikipedia" "github.com/icedream/irc-medialink/parsers/youtube" ) func must(err error) { if err == nil { return } log.Fatal(err) } func main() { var youtubeApiKey string var soundcloudClientId string var soundcloudClientSecret string var debug bool var useTLS bool var server string var password string var timeout time.Duration var pingFreq time.Duration nickname := "YouTubeBot" ident := "youtube" var nickservPw string channels := []string{} // IRC config kingpin.Flag("nick", "The nickname.").Short('n').StringVar(&nickname) kingpin.Flag("ident", "The ident.").Short('i').StringVar(&ident) kingpin.Flag("debug", "Enables debug mode.").Short('d').BoolVar(&debug) kingpin.Flag("tls", "Use TLS.").BoolVar(&useTLS) kingpin.Flag("server", "The server to connect to.").Short('s').StringVar(&server) kingpin.Flag("password", "The password to use for logging into the IRC server.").Short('p').StringVar(&password) kingpin.Flag("timeout", "The timeout on the connection.").Short('t').DurationVar(&timeout) kingpin.Flag("pingfreq", "The ping frequency.").DurationVar(&pingFreq) kingpin.Flag("nickserv-pw", "NickServ password.").StringVar(&nickservPw) kingpin.Flag("channels", "Channels to join.").Short('c').StringsVar(&channels) // Youtube config kingpin.Flag("youtube-key", "The API key to use to access the YouTube API.").StringVar(&youtubeApiKey) kingpin.Flag("soundcloud-id", "The SoundCloud ID.").StringVar(&soundcloudClientId) kingpin.Flag("soundcloud-secret", "The SoundCloud secret.").StringVar(&soundcloudClientSecret) kingpin.Parse() if len(nickname) == 0 { log.Fatal("Nickname must be longer than 0 chars.") } if len(ident) == 0 { log.Fatal("Ident must be longer than 0 chars.") } // Manager m := manager.NewManager() // Load youtube parser youtubeParser := &youtube.Parser{ Config: &youtube.Config{ApiKey: youtubeApiKey}, } must(m.RegisterParser(youtubeParser)) // Load soundcloud parser soundcloudParser := &soundcloud.Parser{ Config: &soundcloud.Config{ ClientId: soundcloudClientId, ClientSecret: soundcloudClientSecret, }, } must(m.RegisterParser(soundcloudParser)) // Load wikipedia parser must(m.RegisterParser(new(wikipedia.Parser))) // Load web parser must(m.RegisterParser(new(web.Parser))) // IRC conn := m.AntifloodIrcConn(irc.IRC(nickname, ident)) conn.Debug = debug conn.VerboseCallbackHandler = conn.Debug conn.UseTLS = useTLS conn.Password = password if timeout > time.Duration(0) { conn.Timeout = timeout } if pingFreq > time.Duration(0) { conn.PingFreq = pingFreq } joinChan := make(chan string) inviteChan := make(chan string) // register callbacks conn.AddCallback("001", func(e *irc.Event) { // handle RPL_WELCOME // nickserv login if len(nickservPw) > 0 { conn.Privmsg("NickServ", "IDENTIFY "+nickservPw) log.Print("Sent NickServ login request.") } // I am a bot! (+B user mode) conn.Mode(conn.GetNick(), "+B-iw") // Join configured channels if len(channels) > 0 { conn.Join(strings.Join(channels, ",")) } }) conn.AddCallback("JOIN", func(e *irc.Event) { // Is this JOIN not about us? if !strings.EqualFold(e.Nick, conn.GetNick()) { return } // Asynchronous notification select { case joinChan <- e.Arguments[0]: default: } }) conn.AddCallback("INVITE", func(e *irc.Event) { // Is this INVITE not for us? if !strings.EqualFold(e.Arguments[0], conn.GetNick()) { return } // Asynchronous notification select { case inviteChan <- e.Arguments[1]: default: } // We have been invited, autojoin! go func(sourceNick string, targetChannel string) { joinWaitLoop: for { select { case channel := <-joinChan: if strings.EqualFold(channel, targetChannel) { // TODO - Thanks message time.Sleep(1 * time.Second) conn.Privmsgf(targetChannel, "Thanks for inviting me, %s! I am %s, the friendly bot that shows information about links posted in this channel. I hope I can be of great help for everyone here in %s! :)", sourceNick, conn.GetNick(), targetChannel) time.Sleep(2 * time.Second) conn.Privmsg(targetChannel, "If you ever run into trouble with me (or find any bugs), please use the channel #MediaLink for contact on this IRC.") break joinWaitLoop } case channel := <-inviteChan: if strings.EqualFold(channel, targetChannel) { break joinWaitLoop } case <-time.After(time.Minute): log.Printf("WARNING: Timed out waiting for us to join %s as we got invited", targetChannel) break joinWaitLoop } } }(e.Nick, e.Arguments[1]) conn.Join(e.Arguments[1]) }) conn.AddCallback("PRIVMSG", func(e *irc.Event) { go func(event *irc.Event) { //sender := event.Nick target := event.Arguments[0] isChannel := true if strings.EqualFold(target, conn.GetNick()) { // Private message to us! target = event.Nick isChannel = false } if strings.EqualFold(target, conn.GetNick()) { // Emergency switch to avoid endless loop, // dropping all messages from the bot to the bot! log.Printf("BUG - Emergency switch, caught message from bot to bot: %s", event.Arguments) return } msg := stripIrcFormatting(event.Message()) log.Printf("<%s @ %s> %s", event.Nick, target, msg) urlStr := xurls.Relaxed.FindString(msg) switch { case !isChannel: // Explain who we are and what we do conn.Privmsgf(target, "Hi, I parse links people post to chat rooms to give some information about them. I also allow people to search for YouTube videos and SoundCloud sounds straight from IRC. If you have questions or got any bug reports, please direct them to Icedream in #MediaLink, thank you!") case len(urlStr) > 0: // URL? // Parse URL! u, err := url.ParseRequestURI(urlStr) if err != nil { u, err = url.ParseRequestURI("http://" + urlStr) } if err != nil { log.Print(err) break } // Check if this URL has been recently parsed before (antiflood) shouldIgnore := m.TrackUrl(target, u) if shouldIgnore { log.Printf("WARNING: URL antiflood triggered, dropping URL for %s: %s", target, u) break } _, result := m.Parse(u) if result.Error != nil { log.Print(result.Error) } if result.UserError != nil { if s, err := tplString("error", result.UserError); err != nil { log.Print(err) } else { conn.Privmsg(target, s) } } if result.Error == nil && result.UserError == nil && result.Information != nil { for _, i := range result.Information { if s, err := tplString("link-info", i); err != nil { log.Print(err) } else { conn.Privmsg(target, s) } } } } }(e) }) // connect must(conn.Connect(server)) // listen for errors log.Print("Now looping.") conn.Loop() }