irc-medialink/main.go

255 lines
7.1 KiB
Go
Raw Normal View History

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)
2016-06-11 23:32:13 +00:00
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()
}