/* Copyright 2018 Martin Dosch Licensed under the "MIT License" */ package main import ( "encoding/json" "flag" "log" "os" "os/user" "strings" "time" "github.com/chilts/sid" "github.com/mattn/go-xmpp" ) func main() { var err error var configPath, configFile string type configuration struct { ServerAddress string BotJid string Password string Muc string MucNick string MaxArticles int Feeds []string } // Read path to config from command line option. configFilePtr := flag.String("config", "none", "path to configuration file") flag.Parse() if *configFilePtr != "none" { configFile = *configFilePtr } else { // Get systems user config path. osConfigDir := os.Getenv("$XDG_CONFIG_HOME") if osConfigDir != "" { // Create configPath if not yet existing. configPath = osConfigDir + "/.config/feed-to-muc/" if _, err := os.Stat(configPath); os.IsNotExist(err) { err = os.MkdirAll(configPath, 0700) if err != nil { log.Fatal("Error: ", err) } } } else { // Get the current user. curUser, err := user.Current() if err != nil { log.Fatal("Error: ", err) return } // Get home directory. home := curUser.HomeDir if home == "" { log.Fatal("Error: No home directory available.") return } // Create configPath if not yet existing. configPath = home + "/.config/feed-to-muc/" if _, err := os.Stat(configPath + "config.json"); os.IsNotExist(err) { err = os.MkdirAll(configPath, 0700) if err != nil { log.Fatal("Error: ", err) } } } configFile = configPath + "config.json" } // Check that config file is existing. if _, err := os.Stat(configFile); os.IsNotExist(err) { log.Fatal("Error: ", err) } // Read configuration file into variable config. file, _ := os.Open(configFile) defer file.Close() decoder := json.NewDecoder(file) config := configuration{} if err := decoder.Decode(&config); err != nil { log.Fatal("Error: ", err) } if _, err := os.Stat(configFile); os.IsNotExist(err) { err = os.MkdirAll(configPath, 0700) if err != nil { log.Fatal("Error: ", err) } } var client *xmpp.Client options := xmpp.Options{ Host: config.ServerAddress, User: config.BotJid, Password: config.Password, NoTLS: true, StartTLS: true, Debug: false, } // Connect to server. client, err = options.NewClient() if err != nil { log.Fatal(err) } // Join the MUC mucStatus, err := client.JoinMUCNoHistory(config.Muc, config.MucNick) if err != nil { log.Fatal(err) } // Exit if Status is > 300, see https://xmpp.org/registrar/mucstatus.html if mucStatus > 300 { os.Exit(mucStatus) } // Starting goroutine to ping the server every 30 seconds. go pingServer(client, config.ServerAddress, config.BotJid) // Starting goroutine to ping the MUC every 30 seconds. go pingMUC(client, config.BotJid, config.Muc, config.MucNick) for { // Check all configured feeds for new articles and send // new articles to configured MUC. for i := 0; i < len(config.Feeds); i++ { output, err := getArticles(config.Feeds[i], config.MaxArticles) if err != nil { // Exit if an error occurs checking the feeds. log.Fatal(err) } if output != "" { _, err = client.Send(xmpp.Chat{Remote: config.Muc, Type: "groupchat", Text: output}) if err != nil { // ToDo: Save message for resend. // Exit if message can not be sent. log.Fatal(err) } } } // Wait 30 seconds before checking feeds again. time.Sleep(30 * time.Second) } } // Send a ping every 30 seconds after last successful ping to check if the MUC is still available. func pingMUC(client *xmpp.Client, botJid string, Muc string, MucNick string) { for { time.Sleep(30 * time.Second) // Send ping to own MUC participant to check we are still joined. id, err := client.RawInformation(botJid, Muc+"/"+MucNick, sid.Id(), "get", "") if err != nil { log.Fatal(err) } pingSent := time.Now() pingReceived := false // Check for result IQ as long as there was no reply yet. for (time.Since(pingSent).Seconds() < 10.0) && (pingReceived == false) { // Receive Stanzas. stanza, err := client.Recv() if err != nil { log.Fatal(err) } // Check IQs for type result and UID. switch v := stanza.(type) { case xmpp.IQ: if (v.Type == "error") && (v.ID == id) { log.Fatal("MUC not available.") } if (v.Type == "result") && (v.ID == id) { pingReceived = true } break default: break } } // Quit if no ping reply was received. if pingReceived == false { log.Fatal("MUC not available.") } } } // Send a ping to the server every 30 seconds to check if the connection is still alive. func pingServer(client *xmpp.Client, server string, botJid string) { for { time.Sleep(30 * time.Second) // Send ping to server to check if connection still exists. err := client.PingC2S(botJid, strings.Split(server, ":")[0]) if err != nil { log.Fatal(err) } } }