diff --git a/events_alerter/events_alerter.go b/events_alerter/events_alerter.go new file mode 100644 index 0000000..25ef5bb --- /dev/null +++ b/events_alerter/events_alerter.go @@ -0,0 +1,142 @@ +package events_alerter + +import ( + "database/sql" + "fmt" + "strings" + "time" +) +import _ "github.com/go-sql-driver/mysql" + +type MsgStr struct { + remote string + message string +} + +func (msg MsgStr) Remote() string { + return msg.remote +} + +func (msg MsgStr) Message() string { + return msg.message +} + +type MsgInt interface { + Remote() string + Message() string +} + +type DbRequester struct { + login string + pass string + host string + database string +} + +func (dbStruct *DbRequester) ReqPrepare(sqlStr ...string) string { + if len(sqlStr) > 1 { + values := make([]interface{}, len(sqlStr)-1) + for i, _ := range sqlStr[1:] { + k := i + 1 + values[i] = sqlStr[k] + } + return fmt.Sprintf(sqlStr[0], values...) + } else { + return sqlStr[0] + } + +} + +func (dbStruct *DbRequester) Requester(sqlStr string) (allResult [][]string, err error) { + connStr := fmt.Sprintf("%v:%v@tcp(%v)/%v", dbStruct.login, dbStruct.pass, dbStruct.host, dbStruct.database) + db, err := sql.Open("mysql", connStr) + if err != nil { + return + } + defer db.Close() + err = db.Ping() + if err != nil { + return + } + req, err := db.Prepare(sqlStr) + if err != nil { + return + } + defer req.Close() + rows, err := req.Query() //пыщим запросий + if err != nil { + return + } + cols, err := rows.Columns() // получаем слайс стрингов с названиями колонок + if err != nil { + return + } + rawResult := make([][]byte, len(cols)) // слайс слайсов байт, это одна строка выхлопа сырая + + dest := make([]interface{}, len(cols)) // слайс пустых интерфейсов + for i, _ := range rawResult { + dest[i] = &rawResult[i] // Put pointers to each string in the interface slice + } + for rows.Next() { + err = rows.Scan(dest...) + if err != nil { + return + } + result := make([]string, len(cols)) // слайс строк, одна строка выхлопа, которую надо добавить в общий выхлоп + for i, raw := range rawResult { + if raw == nil { + result[i] = "Null" + } else { + result[i] = string(raw) + } + } + allResult = append(allResult, result) + } + return +} + +func BuildAlert() (msgSl []MsgInt) { + msgTpl := `Вспомни! %v время для важного! +Задача '%v' ждет тебя, +Лови ссыль: +https://pm.constanta-smt.ru/index.php/tasksComments?tasks_id=%v&projects_id=%v` + dbuser := DbRequester{ + login: "bot", + pass: "z68puEFJhEBtS", + host: "10.176.120.51", + database: "qdpm", + } + createdFormat := "2006-01-02 15:04" + t := time.Now() + sqlTpl := `select extra_fields_list.value, tasks.projects_id as pid, tasks.id as tid, name tname, tasks.assigned_to as users + from extra_fields_list, tasks + where extra_fields_list.extra_fields_id = 10 and extra_fields_list.value = '%v' and extra_fields_list.bind_id = tasks.id ;` + //sqlTpl := `select extra_fields_list.value, tasks.projects_id as pid, tasks.id as tid, name tname, tasks.assigned_to as users + //from extra_fields_list, tasks + //where extra_fields_list.extra_fields_id = 10 and extra_fields_list.value <> '' and extra_fields_list.bind_id = tasks.id ;` + fmt.Println(dbuser.ReqPrepare(sqlTpl, t.Format(createdFormat))) + res, err := dbuser.Requester(dbuser.ReqPrepare(sqlTpl, t.Format(createdFormat))) + fmt.Printf("%#v, %#v\n", res, err) + + if len(res) > 0 { // обработка запроса, если найдено больше 0 строк + for _, v := range res { + fmt.Println(v) + msgStr := fmt.Sprintf(msgTpl, v[0], v[3], v[2], v[1]) + uidSl := strings.Split(v[4], ",") + jidSl := make([]string, len(uidSl)) + for _, uid := range uidSl { + sqlTpl = "select value from qdpm.extra_fields_list where extra_fields_id = 11 and bind_id = '%v';" + jid, _ := dbuser.Requester(dbuser.ReqPrepare(sqlTpl, uid)) + if len(jid) > 0 { + tmpJid := jid[0][0] + jidSl = append(jidSl, tmpJid) + fmt.Printf("%v\n", tmpJid) + tmpMsg := MsgStr{remote: tmpJid, message: msgStr} + msgSl = append(msgSl, tmpMsg) + } + } + } + fmt.Println(msgSl) + } + return +} diff --git a/goqdpmbot b/goqdpmbot new file mode 100755 index 0000000..2a3e0b0 Binary files /dev/null and b/goqdpmbot differ diff --git a/goqdpmbot.ini b/goqdpmbot.ini new file mode 100644 index 0000000..0b9883b --- /dev/null +++ b/goqdpmbot.ini @@ -0,0 +1,10 @@ +#: +server=jabber.constanta-smt.ru:5222 +# , jabber id +username=zabbix@jabber.constanta-smt.ru +# +password=ciw2kahZ +# . - true +notls=true +# , +dest=grebenkin@jabber.constanta-smt.ru diff --git a/main.go b/main.go index bce8415..f0f7b4c 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,16 @@ import ( "crypto/tls" "flag" "fmt" + "goqdpmbot/events_alerter" + "goqdpmbot/works" "os" "path" "path/filepath" + + // "runtime" + // "strconv" "strings" + "sync" "time" "github.com/mattn/go-xmpp" @@ -22,6 +28,11 @@ type cfgStruct struct { notls bool } +type commandMsg struct { + remote string + message string +} + func serverName(host string) string { return strings.Split(host, ":")[0] } @@ -96,32 +107,100 @@ func jabberClient(cfg cfgStruct) *xmpp.Client { return talk } -func jabberReader(talk *xmpp.Client, in <-chan int) { +// читаем из джаббера и отправляем в канал +func jabberReader(talk *xmpp.Client, command chan<- commandMsg, in <-chan int) { for { - _, err := talk.Recv() + chat, err := talk.Recv() if err != nil { fmt.Println(err) + } + switch v := chat.(type) { + case xmpp.Chat: + com := commandMsg{remote: v.Remote, message: v.Text} + command <- com + //case xmpp.Presence: + // com := commandMsg{remote: v.From, message: v.Show} + // command <- com + } + } +} + +// отправляем ответы из канала +func jabberSender(talk *xmpp.Client, msgChan <-chan commandMsg) { + for { + msg := <-msgChan + remote := strings.Split(msg.remote, "/")[0] + talk.Send(xmpp.Chat{Remote: remote, Type: "chat", Text: msg.message}) + } +} +// воркер +func worker(workerNum int, msgChan <-chan commandMsg, readerClose chan int, ansChan chan<- commandMsg) { + for { + message := <-msgChan + fmt.Println("Worker = ", workerNum, "message= ", message.message) + cmd := strings.Split(message.message, " ") + workMap := works.Works() + _, workExists := workMap[cmd[0]] + if workExists { + work, _ := workMap[cmd[0]] + ans, err := work.(func(...string) (string, error))(cmd[1:]...) + if err != nil { + ansChan <- commandMsg{remote: message.remote, message: err.Error()} + } else { + ansChan <- commandMsg{remote: message.remote, message: ans} + } + } else if len(message.message) > 0 { + ans := "неизвестная комманда" + ansChan <- commandMsg{remote: message.remote, message: ans} } } } -func main() { - readerClose := make(chan int, 1) - cfg := cfgRead("goxmppalert.ini") - var send_remote string - var send_text string - if len(os.Args) == 3 { - send_remote = os.Args[1] - send_text = os.Args[2] - } else { - send_remote = cfg.dest - send_text = os.Args[1] +//нопоминальщик +func reminder(readerClose chan int, ansChan chan<- commandMsg) { + for { + msgSl := events_alerter.BuildAlert() + fmt.Println("alerter works") + fmt.Println(msgSl) + for _, msgInt := range msgSl { + tmpMsg := commandMsg{remote: msgInt.Remote(), message: msgInt.Message()} + fmt.Println(tmpMsg) + ansChan <- tmpMsg + } + time.Sleep(60 * time.Second) } +} + +func main() { + cfg := cfgRead("goqdpmbot.ini") // читаем конфигу + + goroutinesNum := 5 // количество потоков + wg := &sync.WaitGroup{} // ожидаем интеррапта + signal_channel := make(chan os.Signal, 1) + readerClose := make(chan int, 1) // для закрытия всего зоопарка воркеров + // каналы обмена инфой + comChan := make(chan commandMsg, 5) + ansChan := make(chan commandMsg, 5) + // запускаем клиента talk := jabberClient(cfg) - go jabberReader(talk, readerClose) - go talk.Send(xmpp.Chat{Remote: send_remote, Type: "chat", Text: send_text}) - time.Sleep(1 * time.Second) + go jabberReader(talk, comChan, readerClose) + go jabberSender(talk, ansChan) + // и воркеров + for i := 0; i < goroutinesNum; i++ { + go worker(i, comChan, readerClose, ansChan) + fmt.Println("worker started", i) + } + go reminder(readerClose, ansChan) + // ожидаем выхода + wg.Add(1) + go func() { + <-signal_channel + wg.Done() + }() + wg.Wait() close(readerClose) + time.Sleep(1 * time.Second) + fmt.Println("interrupt") talk.Close() } diff --git a/works/works.go b/works/works.go new file mode 100644 index 0000000..bf58a69 --- /dev/null +++ b/works/works.go @@ -0,0 +1,46 @@ +package works + +import ( + "bytes" + "os/exec" + "time" +) + +func Uname(param ...string) (string, error) { + //var cmdParam string + unameCmd := exec.Command("uname") + if len(param) != 0 { + //for _, p := range param { + // cmdParam += p + //} + unameCmd = exec.Command("uname", param...) + } + var out bytes.Buffer + unameCmd.Stdout = &out + err := unameCmd.Run() + if err != nil { + return "", err + } + return out.String(), err +} + +func Wait(param ...string) (string, error) { + time.Sleep(10 * time.Second) + return "worker sleep 10s", nil +} + +func Echo(param ...string) (string, error) { + var out string + for _, v := range param { + out += v + " " + } + return out, nil +} + +func Works() map[string]interface{} { + return map[string]interface{}{ + "uname": Uname, + "wait": Wait, + "echo": Echo, + } +}