A simple IRC notifier
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

149 lines
4.0 KiB

package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"regexp"
"strings"
"syscall"
"gopkg.in/sorcix/irc.v2"
)
const disconnectExitStatus int = 2
const PushoverAPI string = "https://api.pushover.net/1/messages.json"
func main() {
config, err := LoadConfig()
if err != nil {
log.Fatalf("Unable to configure: %s", err)
}
//TODO (noah): probably should check for protocol + port or something
conn, err := irc.DialTLS(config.Host, &tls.Config{})
if err != nil {
log.Fatalf("Unable to connect to IRC server: %s", err)
}
running := true
//Register signal so we can clean up the socket before we exit
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
signal.Notify(c, os.Interrupt, syscall.SIGINT)
go func() {
<-c
log.Printf("Exiting")
running = false
conn.Close()
os.Exit(0)
}()
if config.Pass != "" {
_, err = conn.Write([]byte("PASS " + config.Pass))
if err != nil {
log.Fatalf("Unable to authenticate to server: %s", err)
}
}
log.Printf("User: %s", config.User)
_, err = conn.Write([]byte("USER " + config.User))
if err != nil {
log.Fatalf("Unable to send message to server: %s", err)
}
log.Printf("Nick: %s", config.Nick)
_, err = conn.Write([]byte("NICK " + config.Nick))
if err != nil {
log.Fatalf("Unable to send message to server: %s", err)
}
_, err = conn.Write([]byte("WHOIS " + config.Nick))
if err != nil {
log.Fatalf("Unable to send message to server: %s", err)
}
userRegexp, err := regexp.Compile(`(\w+)!(\w+)@\S+`)
if err != nil {
log.Fatalf("Unable to compile user regexp: %s", err)
}
away := false
for running {
message, err := conn.Decode()
if err != nil {
log.Printf("Unable to decode: %s", err)
os.Exit(disconnectExitStatus)
}
if message == nil {
continue
}
if message.Command == irc.QUIT && message.Prefix.Name == config.Nick {
log.Printf("Server sent QUIT, exiting")
running = false
//Exit with a non-zero status so the system process restarts this process
os.Exit(disconnectExitStatus)
}
for _, user := range config.ExcludeUsers {
if user == message.Prefix.Name {
log.Printf("User %s is in excluded users, skipping message", message.Prefix.Name)
continue
}
}
for _, excludedChannel := range config.ExcludeChannels {
if message.Params[0] == excludedChannel {
log.Printf("Channel %s is in excluded channels, skipping message", message.Params[0])
continue
}
}
if away && message.Command == irc.PRIVMSG {
matched := false
for _, matchString := range config.Match {
if strings.Contains(message.Params[1], matchString) {
log.Printf("match %s is contained in %s", matchString, message.Params[1])
matched = true
break
}
}
if matched {
log.Print("Message contains search word, sending notification")
err = sendNotification(config.UserToken, config.AppToken, message)
if err != nil {
log.Printf("Unable to send notification: %s", err)
}
}
} else if message.Command == irc.RPL_NOWAWAY {
log.Printf("Marking as away, will notify")
away = true
} else if message.Command == irc.RPL_UNAWAY {
log.Printf("No longer away, won't notify")
away = false
} else if message.Command == irc.RPL_WELCOME {
matches := userRegexp.FindSubmatch([]byte(message.Params[0]))
if matches != nil {
config.Nick = string(matches[0])
log.Printf("Nick: %s\tUsername %s\tHost: %s", matches[0], matches[1], matches[2])
}
}
}
}
func sendNotification(user_token, app_token string, message *irc.Message) error {
body := map[string]string{
"token": app_token,
"user": user_token,
"title": fmt.Sprintf("%s on %s mentioned you", message.Prefix.User, message.Params[0]),
"message": message.Params[1],
}
json_body, err := json.Marshal(body)
if err != nil {
return err
}
resp, err := http.Post(PushoverAPI, "application/json", bytes.NewBuffer(json_body))
if err != nil {
return err
}
log.Printf("Pushover API notify resp code: %d %s", resp.StatusCode, resp.Status)
return nil
}