X-Git-Url: https://git.realraum.at/?a=blobdiff_plain;f=r3-webstatus-spaceapi%2Fr3xmppbot%2Fr3xmppbot.go;fp=r3-webstatus-spaceapi%2Fr3xmppbot%2Fr3xmppbot.go;h=465ee951c7b5919bd4592e40a69d251294e6e3dd;hb=07c69c14bb568f5ee3f09000301bcb0c415698e9;hp=0000000000000000000000000000000000000000;hpb=79613199100e15d05084f4e1b1ede0a1a25259b3;p=svn42.git diff --git a/r3-webstatus-spaceapi/r3xmppbot/r3xmppbot.go b/r3-webstatus-spaceapi/r3xmppbot/r3xmppbot.go new file mode 100644 index 0000000..465ee95 --- /dev/null +++ b/r3-webstatus-spaceapi/r3xmppbot/r3xmppbot.go @@ -0,0 +1,367 @@ +package r3xmppbot + +import ( + xmpp "code.google.com/p/goexmpp" + "fmt" + "log" + "crypto/tls" + "os" + "time" + "encoding/json" + "path" +) + +//~ type StdLogger struct { +//~ } + +//~ func (s *StdLogger) Log(v ...interface{}) { + //~ log.Println(v...) +//~ } + +//~ func (s *StdLogger) Logf(fmt string, v ...interface{}) { + //~ log.Printf(fmt, v...) +//~ } + +func (botdata *XmppBot) makeXMPPMessage(to, message string, subject interface{}) *xmpp.Message { + xmppmsgheader := xmpp.Header{To: to, + From: botdata.my_jid_, + Id: <-xmpp.Id, + Type: "chat", + Lang: "", + Innerxml: "", + Error: nil, + Nested: make([]interface{},0)} + msgsubject := xmpp.Generic{} + if subject != nil { + msgsubject.Chardata = subject.(string) + } + return &xmpp.Message{Header: xmppmsgheader , Subject: &msgsubject, Body: &xmpp.Generic{Chardata:message}, Thread: &xmpp.Generic{}} +} + +func (botdata *XmppBot) makeXMPPPresence(to, ptype string) *xmpp.Presence { + xmppmsgheader := xmpp.Header{To: to, + From: botdata.my_jid_, + Id: <-xmpp.Id, + Type: ptype, + Lang: "", + Innerxml: "", + Error: nil, + Nested: make([]interface{},0)} + return &xmpp.Presence{Header: xmppmsgheader} +} + +type R3JIDDesire int + +const ( + R3NoChange R3JIDDesire = -1 + R3NoInfo R3JIDDesire = iota // ignore first value by assigning to blank identifier + R3NoOfflineInfo + R3AllInfo + R3DebugInfo +) + +type JidData struct { + Online bool + Wants R3JIDDesire +} + +type JabberEvent struct { + JID string + Online bool + Wants R3JIDDesire +} + +type PresenceEvent struct { + Present bool + DoorLock bool + DoorAjar bool +} + +type RealraumXmppNotifierConfig map[string]JidData + +type XmppBot struct { + jid_lastauthtime_ map[string]int64 + realraum_jids_ RealraumXmppNotifierConfig + password_ string + auth_cmd_ string + my_jid_ string + auth_timeout_ int64 + config_file_ string + my_login_password_ string + xmppclient_ *xmpp.Client + presence_events_ *chan interface{} +} + + +func (data RealraumXmppNotifierConfig) saveTo(filepath string) () { + fh, err := os.Create(filepath) + if err != nil { + log.Println(err) + return + } + defer fh.Close() + enc := json.NewEncoder(fh) + if err = enc.Encode(&data); err != nil { + log.Println(err) + return + } +} + +func (data RealraumXmppNotifierConfig) loadFrom(filepath string) () { + fh, err := os.Open(filepath) + if err != nil { + log.Println(err) + return + } + defer fh.Close() + dec := json.NewDecoder(fh) + if err = dec.Decode(&data); err != nil { + log.Println(err) + return + } + for to, jiddata := range data { + jiddata.Online = false + data[to]=jiddata + } +} + + +func init() { + //~ logger := &StdLogger{} + //~ xmpp.Debug = logger + //~ xmpp.Info = logger + //~ xmpp.Warn = logger +} + +func (botdata *XmppBot) handleEventsforXMPP(xmppout chan <- xmpp.Stanza, presence_events <- chan interface{}, jabber_events <- chan JabberEvent) { + var msg, presence_str, lock_str, ajar_str string + last_presence := false + var debug_msg bool + + for { + debug_msg = false + select { + case pe := <-presence_events: + switch pec := pe.(type) { + case xmpp.Stanza: + xmppout <- pec + continue + case string: + msg = pec + case PresenceEvent: + if pec.Present { + presence_str = "Somebody is present !" + } else { + presence_str = "Everybody left." + } + if pec.DoorLock { + lock_str = "locked" + } else { + lock_str = "unlocked" + } + if pec.DoorAjar { + ajar_str = "ajar" + } else { + ajar_str = "shut" + } + msg = fmt.Sprintf("%s (Door is %s and %s)", presence_str, lock_str, ajar_str) + if last_presence == pec.Present { + debug_msg = true + } else { + last_presence = pec.Present + } + default: + break + } + + for to, jiddata := range botdata.realraum_jids_ { + if debug_msg && jiddata.Wants < R3DebugInfo { + continue + } + if (jiddata.Wants == R3NoOfflineInfo && jiddata.Online) || jiddata.Wants > R3NoOfflineInfo { + xmppout <- botdata.makeXMPPMessage(to, msg, nil) + } //else { + //~ fmt.Println("Not sending to ", to, jiddata) + //~ } + } + + case je := <-jabber_events: + jid_data, jid_in_map := botdata.realraum_jids_[je.JID] + if jid_in_map { + jid_data.Online = je.Online + if je.Wants > R3NoChange { + jid_data.Wants = je.Wants + } + botdata.realraum_jids_[je.JID] = jid_data + botdata.realraum_jids_.saveTo(botdata.config_file_) + } else if je.Wants > R3NoChange { + botdata.realraum_jids_[je.JID] = JidData{je.Online, je.Wants} + botdata.realraum_jids_.saveTo(botdata.config_file_) + } + } + } +} + +func removeJIDResource(jid string) string { + var jidjid xmpp.JID + jidjid.Set(jid) + jidjid.Resource = "" + return jidjid.String() +} + +func (botdata *XmppBot) isAuthenticated(jid string) bool { + authtime, in_map := botdata.jid_lastauthtime_[jid] + //~ log.Println("isAuthenticated", in_map, authtime, time.Now().Unix(), auth_timeout_, time.Now().Unix() - authtime > auth_timeout_) + return in_map && time.Now().Unix() - authtime < botdata.auth_timeout_ +} + +const help_text_ string = "\nauth \n .... enables you to use the other commands\non\n .... you will be notified of r3 status changes\noff\n .... you will no longer recieve notifications\non_while_offline\n .... you will be notified of r3 status changes even if you are offline" + +//~ var re_msg_auth_ *regexp.Regexp = regexp.MustCompile("auth\s+(\S+)") + +func (botdata *XmppBot) handleIncomingMessageDialog(inmsg xmpp.Message, xmppout chan<- xmpp.Stanza, jabber_events chan JabberEvent) { + if inmsg.Body == nil || inmsg.GetHeader() == nil { + return + } + bodytext :=inmsg.Body.Chardata + //~ log.Println("Message Body:", bodytext) + if botdata.isAuthenticated(inmsg.GetHeader().From) { + switch bodytext { + case "on": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3NoOfflineInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Receive r3 information while online" , "Your New Status") + case "off": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3NoInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Do not receive r3 information" , "Your New Status") + case "on_while_offline": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3AllInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Receive r3 information even while offline" , "Your New Status") + case "debug": + jabber_events <- JabberEvent{removeJIDResource(inmsg.GetHeader().From), true, R3DebugInfo} + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Debug mode enabled" , "Your New Status") + case "bye", "Bye", "quit", "logout": + botdata.jid_lastauthtime_[inmsg.GetHeader().From] = 0 + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "Bye Bye !" ,nil) + default: + //~ auth_match = re_msg_auth_.FindStringSubmatch(inmsg.Body.Chardata) + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, help_text_, "Available Commands") + } + } else { + switch bodytext { + case "Hilfe","hilfe","help","Help","?","hallo","Hallo","Yes","yes","ja","ja bitte","bitte","sowieso": + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, help_text_, "Available Commands") + case botdata.auth_cmd_: + botdata.jid_lastauthtime_[inmsg.GetHeader().From] = time.Now().Unix() + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "You are now authorized to use commands" , "Authorized") + default: + //~ auth_match = re_msg_auth_.FindStringSubmatch(inmsg.Body.Chardata) + xmppout <- botdata.makeXMPPMessage(inmsg.GetHeader().From, "A nice day to you too !\nDo you need \"help\" ?", nil) + } + } +} + +func (botdata *XmppBot) handleIncomingXMPPStanzas(xmppin <- chan xmpp.Stanza, xmppout chan<- xmpp.Stanza, jabber_events chan JabberEvent) { + var incoming_stanza interface{} + for incoming_stanza = range xmppin { + switch stanza := incoming_stanza.(type) { + case *xmpp.Message: + botdata.handleIncomingMessageDialog(*stanza, xmppout, jabber_events) + case *xmpp.Presence: + if stanza.GetHeader() == nil { + continue + } + if stanza.GetHeader().Type == "subscribe" { + xmppout <- botdata.makeXMPPPresence(stanza.GetHeader().From, "subscribed") + } + jabber_events <- JabberEvent{stanza.GetHeader().From, stanza.GetHeader().Type != "unavailable", R3NoChange} + case *xmpp.Iq: + if stanza.GetHeader() == nil { + continue + } + } + } +} + +var default_state_save_dir_ string = "/flash/var/lib/r3netstatus/" + +func NewStartedBot(loginjid, loginpwd, password string, insecuretls bool) (*XmppBot, chan interface{}, error) { + var err error + botdata := new(XmppBot) + + botdata.realraum_jids_ = make(map[string]JidData, 1) + botdata.jid_lastauthtime_ = make(map[string]int64,1) + botdata.auth_cmd_ = "auth " + password + botdata.my_jid_ = loginjid + botdata.my_login_password_ = loginpwd + botdata.auth_timeout_ = 1200 + + botdata.config_file_ = path.Join(default_state_save_dir_, "r3xmpp."+removeJIDResource(loginjid)+".json") + + //~ log.Println(botdata.config_file_) + + //~ logger := &StdLogger{} + //~ xmpp.Debug = logger + //~ xmpp.Info = logger + //~ xmpp.Warn = logger + + xmpp.TlsConfig = tls.Config{InsecureSkipVerify: insecuretls} + botdata.realraum_jids_.loadFrom(botdata.config_file_) + + client_jid := new(xmpp.JID) + client_jid.Set(botdata.my_jid_) + botdata.xmppclient_, err = xmpp.NewClient(client_jid, botdata.my_login_password_, nil) + if err != nil { + log.Println("Error connecting to xmpp server", err) + return nil, nil, err + } + + err = botdata.xmppclient_.StartSession(false, &xmpp.Presence{}) + if err != nil { + log.Println("'Error StartSession:", err) + return nil, nil, err + } + + presence_events := make(chan interface{},1) + jabber_events := make(chan JabberEvent,1) + + go botdata.handleEventsforXMPP(botdata.xmppclient_.Out, presence_events, jabber_events) + go botdata.handleIncomingXMPPStanzas(botdata.xmppclient_.In, botdata.xmppclient_.Out, jabber_events) + + botdata.presence_events_ = &presence_events + + return botdata, presence_events, nil +} + +func (botdata *XmppBot) StopBot() { + if botdata.xmppclient_ != nil { + close(botdata.xmppclient_.Out) + } + if botdata.presence_events_ != nil { + *botdata.presence_events_ <- false + close(*botdata.presence_events_) + } +} + +//~ func main() { + //~ bot, presence_events, err := NewStartedBot("realrauminfo@realraum.at/Tuer", "d7ynC6Dg", "r3alraumOLGAXMPPInfos", true) + + //~ if err != nil { + //~ log.Println(err) + //~ os.Exit(1) + //~ } + + //~ presence_events <- PresenceEvent{true, true, true} + //~ presence_events <- PresenceEvent{true, true, false} + //~ presence_events <- PresenceEvent{true, false, false} + + //~ p := make([]byte, 1024) + //~ for + //~ { + //~ nr, _ := os.Stdin.Read(p) + //~ if nr == 0 { + //~ break + //~ } + //~ } + + //~ bot.StopBot() +//~ }