255 lines
7.1 KiB
Go
255 lines
7.1 KiB
Go
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()
|
|
}
|