diff --git a/feed-to-muc.go b/feed-to-muc.go
index ad35701..4d7476c 100644
--- a/feed-to-muc.go
+++ b/feed-to-muc.go
@@ -4,112 +4,43 @@ 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"
)
+type configuration struct {
+ ServerAddress string
+ BotJid string
+ Password string
+ Muc string
+ MucNick string
+ MaxArticles int
+ RefreshTime time.Duration
+ NoExcerpt bool
+ Quiet bool
+ Contact string
+ Filter []string
+ Feeds []string
+}
+
// Variables defined globally as they are used by functions pingMUC
// and processStanzas.
var (
- id string
- err error
+ ID string
pingSent time.Time
pingReceived bool
)
func main() {
- var configPath, configFile string
-
- type configuration struct {
- ServerAddress string
- BotJid string
- Password string
- Muc string
- MucNick string
- MaxArticles int
- RefreshTime time.Duration
- NoExcerpt bool
- Quiet bool
- Contact string
- Filter []string
- 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: Can't create config path: ", err)
- }
- }
-
- } else { // Get the current user.
- curUser, err := user.Current()
- if err != nil {
- log.Fatal("Error: Can't get current user: ", 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: Can't create config path: ", err)
- }
- }
-
- }
- configFile = configPath + "config.json"
- }
-
- // Check that config file is existing.
- if _, err := os.Stat(configFile); os.IsNotExist(err) {
- log.Fatal("Error: Can't find config file: ", 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: Can't decode config: ", err)
- }
-
- if _, err := os.Stat(configFile); os.IsNotExist(err) {
- err = os.MkdirAll(configPath, 0700)
- if err != nil {
- log.Fatal("Error: Can't create config path: ", err)
- }
- }
+ config := openConfig(configFilePtr)
var client *xmpp.Client
@@ -123,13 +54,13 @@ func main() {
}
// Connect to server.
- client, err = options.NewClient()
+ client, err := options.NewClient()
if err != nil {
log.Fatal("Error: Can't connect to xmpp server: ", err)
}
// Join the MUC
- _, err := client.JoinMUCNoHistory(config.Muc, config.MucNick)
+ _, err = client.JoinMUCNoHistory(config.Muc, config.MucNick)
if err != nil {
log.Fatal("Error: Can't join MUC: ", err)
}
@@ -172,6 +103,7 @@ func main() {
time.Sleep(config.RefreshTime * time.Second)
}
}
+<<<<<<< HEAD
// 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) {
@@ -414,3 +346,5 @@ func processStanzas(client *xmpp.Client, muc string, mucNick string, feeds []str
}
}
}
+=======
+>>>>>>> 62dfe9d... Distributed the code on several files to get a better overview.
diff --git a/getarticles.go b/getArticles.go
similarity index 100%
rename from getarticles.go
rename to getArticles.go
diff --git a/openConfig.go b/openConfig.go
new file mode 100644
index 0000000..1f08864
--- /dev/null
+++ b/openConfig.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "os/user"
+)
+
+func openConfig(configFilePtr *string) configuration {
+ var (
+ configFile string
+ configPath string
+ )
+ 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: Can't create config path: ", err)
+ }
+ }
+
+ } else { // Get the current user.
+ curUser, err := user.Current()
+ if err != nil {
+ log.Fatal("Error: Can't get current user: ", err)
+ }
+ // Get home directory.
+ home := curUser.HomeDir
+
+ if home == "" {
+ log.Fatal("Error: No home directory available.")
+ }
+
+ // 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: Can't create config path: ", err)
+ }
+ }
+
+ }
+ configFile = configPath + "config.json"
+ }
+
+ // Check that config file is existing.
+ if _, err := os.Stat(configFile); os.IsNotExist(err) {
+ log.Fatal("Error: Can't find config file: ", 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: Can't decode config: ", err)
+ }
+
+ if _, err := os.Stat(configFile); os.IsNotExist(err) {
+ err = os.MkdirAll(configPath, 0700)
+ if err != nil {
+ log.Fatal("Error: Can't create config path: ", err)
+ }
+ }
+ return config
+}
diff --git a/processStanzas.go b/processStanzas.go
new file mode 100644
index 0000000..4affea6
--- /dev/null
+++ b/processStanzas.go
@@ -0,0 +1,209 @@
+package main
+
+import (
+ "log"
+ "strings"
+
+ "github.com/mattn/go-xmpp"
+)
+
+func processStanzas(client *xmpp.Client, muc string, mucNick string, feeds []string, quiet bool, contact string) {
+ for {
+ // Receive stanzas.
+ stanza, err := client.Recv()
+ if err != nil {
+ log.Fatal("Error: Failed receiving Stanzas:", err)
+ }
+
+ // Check stanzas, maybe we want to reply.
+ switch v := stanza.(type) {
+ // Reply to requests for source and feeds.
+ case xmpp.Chat:
+ var command string
+
+ // Check for room mention of the bots nick if the the message type is groupchat.
+ if v.Type == "groupchat" {
+ // Leave if option quiet is set.
+ if quiet == true {
+ break
+ }
+ // Get first word of the message and transform it to lower case.
+ mention := strings.ToLower(strings.Split(v.Text, " ")[0])
+
+ // If it is not the bots nick remove one trailing character as
+ // a lot of clients append `:` or `,` to mentions.
+ if mention != strings.ToLower(mucNick) {
+ mentionLength := len(mention)
+ // Leave if mentionLength is <= 0
+ if mentionLength <= 0 {
+ break
+ }
+ mention = mention[:mentionLength-1]
+ // Leave if the message is not addressed to the bot.
+ if mention != strings.ToLower(mucNick) {
+ break
+ }
+ }
+ // As the first word is the mention of the bots nickname, the command is
+ // the second word in a groupchat message.
+ command = strings.ToLower(strings.Split(v.Text, " ")[1])
+ // If the message type is chat (e.g. private message), the command is the
+ // first word.
+ } else if v.Type == "chat" {
+ command = strings.ToLower(strings.Split(v.Text, " ")[0])
+ } else {
+ break
+ }
+
+ // Check for the command.
+ switch command {
+
+ // Reply with a short summary of available commands for `help`.
+ case "help":
+
+ reply := "The following commands are available:\n" +
+ "\"contact\": Show contact for this bot.\n" +
+ "\"feeds\": List feeds I'm following.\n" +
+ "\"ping\": Sends back a pong.\n" +
+ "\"source\": Show source code URL."
+
+ if v.Type == "groupchat" {
+ _, err = client.Send(xmpp.Chat{Remote: muc,
+ Type: "groupchat", Text: strings.Split(v.Remote, "/")[1] + ": " + reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to MUC:", err)
+ }
+ } else if v.Type == "chat" {
+ _, err = client.Send(xmpp.Chat{Remote: v.Remote,
+ Type: "chat", Text: reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to ", v.Remote, ": ", err)
+ }
+ }
+
+ // Reply with repo address for `source`.
+ case "source":
+
+ reply := "My source can be found at " +
+ "https://salsa.debian.org/mdosch-guest/feed-to-muc"
+
+ if v.Type == "groupchat" {
+ _, err = client.Send(xmpp.Chat{Remote: muc,
+ Type: "groupchat", Text: strings.Split(v.Remote, "/")[1] + ": " + reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to MUC:", err)
+ }
+ } else if v.Type == "chat" {
+ _, err = client.Send(xmpp.Chat{Remote: v.Remote,
+ Type: "chat", Text: reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to ", v.Remote, ": ", err)
+ }
+ }
+ // Reply with the list of monitored feeds for `feeds`.
+ case "feeds":
+ var feedList string
+ for _, feed := range feeds {
+ // Add next feed element and a newline.
+ feedList = feedList + feed + "\n"
+ }
+
+ reply := "Feeds I'm following:\n" + feedList
+
+ if v.Type == "groupchat" {
+ _, err = client.Send(xmpp.Chat{Remote: muc,
+ Type: "groupchat", Text: strings.Split(v.Remote, "/")[1] + ": " + reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to MUC:", err)
+ }
+ } else if v.Type == "chat" {
+ _, err = client.Send(xmpp.Chat{Remote: v.Remote,
+ Type: "chat", Text: reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to ", v.Remote, ": ", err)
+ }
+ }
+
+ case "ping":
+
+ reply := "pong"
+
+ if v.Type == "groupchat" {
+ _, err = client.Send(xmpp.Chat{Remote: muc,
+ Type: "groupchat", Text: strings.Split(v.Remote, "/")[1] + ": " + reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to MUC:", err)
+ }
+ } else if v.Type == "chat" {
+ _, err = client.Send(xmpp.Chat{Remote: v.Remote,
+ Type: "chat", Text: reply})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to ", v.Remote, ": ", err)
+ }
+ }
+
+ case "contact":
+
+ if contact == "" {
+ contact = "Sorry, no contact information provided."
+ }
+
+ if v.Type == "groupchat" {
+ _, err = client.Send(xmpp.Chat{Remote: muc,
+ Type: "groupchat", Text: strings.Split(v.Remote, "/")[1] + ": " + contact})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to MUC:", err)
+ }
+ } else if v.Type == "chat" {
+ _, err = client.Send(xmpp.Chat{Remote: v.Remote,
+ Type: "chat", Text: contact})
+ if err != nil {
+ log.Fatal("Error: Failed sending message to ", v.Remote, ": ", err)
+ }
+ }
+ }
+
+ // Reply to pings and disco queries.
+ case xmpp.IQ:
+
+ if (v.Type == "error") && (v.ID == ID) {
+ log.Fatal("MUC not available. processStanza")
+ }
+
+ if (v.Type == "result") && (v.ID == ID) {
+ pingReceived = true
+ }
+
+ if v.Type == "get" {
+ // Reply to disco#info requests according to https://xmpp.org/extensions/xep-0030.html.
+ if strings.Contains(string(v.Query),
+ "") == true {
+ _, err := client.RawInformation(client.JID(), v.From, v.ID,
+ "result", ""+
+ ""+
+ "")
+ if err != nil {
+ log.Fatal("Error: Failed to reply to disco#info:", err)
+ }
+ } else if strings.Contains(string(v.Query), "") == true {
+ // Reply to pings.
+ _, err := client.RawInformation(client.JID(), v.From, v.ID, "result", "")
+ if err != nil {
+ log.Fatal("Error: Failed to reply to ping:", err)
+ }
+ } else {
+ // Send error replies for all other IQs.
+ _, err := client.RawInformation(client.JID(), v.From, v.ID, "error",
+ "")
+ if err != nil {
+ log.Fatal("Error: Failed to send error IQ:", err)
+ }
+ }
+ }
+
+ default:
+ break
+ }
+ }
+}
diff --git a/removetracking.go b/removeTracking.go
similarity index 100%
rename from removetracking.go
rename to removeTracking.go
diff --git a/sendPings.go b/sendPings.go
new file mode 100644
index 0000000..e2077b9
--- /dev/null
+++ b/sendPings.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+ "log"
+ "strings"
+ "time"
+
+ "github.com/chilts/sid"
+ "github.com/mattn/go-xmpp"
+)
+
+// 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) {
+
+ var err error
+
+ 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("Error: Can't send MUC self ping: ", 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) {
+ time.Sleep(1 * time.Second)
+ if pingReceived == true {
+ break
+ }
+
+ }
+ // Quit if no ping reply was received.
+ if pingReceived == false {
+ log.Fatal("MUC not available. pingMUC")
+ }
+ }
+}
+
+// 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("Error: Client2Server ping failed:", err)
+ }
+ }
+}