This commit is contained in:
Andrey Belvedersky
2021-06-03 01:59:32 +03:00
parent a4838bb973
commit 18b2d4e554
23 changed files with 821 additions and 22 deletions

50
pkg/bot/bot.go Normal file
View File

@ -0,0 +1,50 @@
package bot
import (
"context"
"fmt"
"git/ecom/jira-bot/pkg/bot/middlewares"
"git/ecom/jira-bot/pkg/bot/scenes"
"git/ecom/jira-bot/pkg/config"
"git/ecom/jira-bot/pkg/templates"
"git/ecom/jira-bot/pkg/utils"
"log"
telegram "gopkg.in/tucnak/telebot.v2"
)
// Чатбот
func JiraBot() {
cfg, _ := config.GetConfig()
redis := utils.Redis(cfg.Redis)
ctx := context.Background()
jiraClient, _ := utils.GetClient(&cfg.Jira)
b, err := telegram.NewBot(telegram.Settings{
URL: cfg.Telegram.Url,
Token: cfg.Telegram.Token,
Poller: middlewares.RedisSession(redis, ctx),
})
if err != nil {
log.Fatal(err)
return
}
b.Handle(telegram.OnText, func(m *telegram.Message) {
// spew.Dump(m)
b.Send(m.Sender, m.Text)
})
// Cтартовая сцена
b.Handle("/start", scenes.Start(b, cfg))
b.Handle("/exit", scenes.Exit(b, redis, ctx))
b.Handle("/hello", func(m *telegram.Message) {
b.Send(m.Sender, "Hello World!")
})
fmt.Println(templates.Title(b.Me, cfg.BotVersion,jiraClient.GetBaseURL().Host))
b.Start()
}

View File

@ -0,0 +1,49 @@
package middlewares
import (
"context"
"encoding/json"
"git/ecom/jira-bot/pkg/utils"
"log"
"strconv"
"github.com/fatih/color"
"github.com/go-redis/redis/v8"
telegram "gopkg.in/tucnak/telebot.v2"
)
func RedisSession(redis *redis.Client, ctx context.Context) *telegram.MiddlewarePoller {
poller := utils.NewPooler(10)
session := telegram.NewMiddlewarePoller(poller, func(upd *telegram.Update) bool {
userid := strconv.Itoa(upd.Message.Sender.ID) + ":" + strconv.Itoa(int(upd.Message.Chat.ID))
log.Println(userid, upd.Message.Chat.FirstName, ":", upd.Message.Text)
exist, err := redis.HExists(ctx, "users", userid).Result()
if err != nil {
log.Println("Ошибка проверки пользователя", err.Error())
}
if exist {
return true
}
color.Green("Новый пользователь!")
user, err := json.Marshal(utils.User{
Id: upd.Message.Sender.ID,
Role: utils.Client,
Username: upd.Message.Sender.Username,
Name: upd.Message.Sender.FirstName + " " + upd.Message.Sender.LastName,
})
if err != nil {
log.Println("Ошибка Marshal:", err.Error())
}
err = redis.HSet(ctx, "users", userid, user).Err()
if err != nil {
log.Println("Ошибка:", err.Error())
}
return true
})
return session
}

27
pkg/bot/scenes/exit.go Normal file
View File

@ -0,0 +1,27 @@
package scenes
import (
"context"
"log"
"strconv"
"github.com/go-redis/redis/v8"
telegram "gopkg.in/tucnak/telebot.v2"
)
/*
Выход из чат бота
Удаляем сессию и прощаемся
*/
func Exit(b *telegram.Bot, redis *redis.Client, ctx context.Context) interface{} {
return func(m *telegram.Message) {
userid := strconv.Itoa(m.Sender.ID) + ":" + strconv.Itoa(int(m.Chat.ID))
err := redis.HDel(ctx, "users", userid).Err()
if err != nil {
log.Println("Ошибка удаления пользователя", err.Error())
}
b.Send(m.Sender, "Пока!")
}
}

65
pkg/bot/scenes/start.go Normal file
View File

@ -0,0 +1,65 @@
package scenes
import (
"encoding/base64"
"git/ecom/jira-bot/pkg/config"
"git/ecom/jira-bot/pkg/templates"
"git/ecom/jira-bot/pkg/utils"
"log"
"strings"
"github.com/davecgh/go-spew/spew"
telegram "gopkg.in/tucnak/telebot.v2"
)
/*
артовая команда
Пытаемся достать авторизацию из magic link или base64 токена
Если ничего не нашли то приветствуем пользователя и предлагаем залогинится в чат боте
*/
func Start(b *telegram.Bot, cfg *config.Config) interface{} {
return func(m *telegram.Message) {
// Eсли что то есть в payload
if len(m.Payload) > 0 {
decoded, err := base64.StdEncoding.DecodeString(m.Payload)
if err != nil {
log.Println("decode error:", err)
b.Send(m.Sender, "Ошибка декодирования токена")
return
}
tokenData := strings.Split(string(decoded), ":")
cfg.Jira.Auth.Username, cfg.Jira.Auth.Password = tokenData[0], tokenData[1]
client, err := utils.GetClient(&cfg.Jira)
if err != nil {
log.Println(err)
b.Send(m.Sender, "Ошибка авторизации в jira")
// Отправить template
return
}
usr, userRes, err := client.User.GetSelf()
if err != nil {
log.Println(err)
spew.Dump(userRes)
b.Send(m.Sender, "Ошибка получении пользовательских данных")
// Отправить template
return
}
sendRes, err := b.Send(m.Sender, templates.Hello(usr.DisplayName), telegram.ModeMarkdown)
if err != nil {
log.Println(err)
spew.Dump(sendRes)
b.Send(m.Sender, "Ошибка отправки сообщения")
// Cделать отправку себе
b.Send(m.Sender, err)
}
return
}
// Если ничего не нашли приветствуем пользователя
b.Send(m.Sender, "Hello World!")
}
}

View File

@ -1,4 +1,4 @@
package settings
package config
import (
"errors"
@ -8,10 +8,10 @@ import (
)
// Load config file from given path
func LoadConfig() (*viper.Viper, error) {
func LoadConfig(file string) (*viper.Viper, error) {
v := viper.New()
v.SetConfigName("./settings")
v.SetConfigName("./" + file)
v.AddConfigPath(".")
v.AutomaticEnv()
if err := v.ReadInConfig(); err != nil {
@ -27,19 +27,17 @@ func LoadConfig() (*viper.Viper, error) {
// Parse config file
func ParseConfig(v *viper.Viper) (*Config, error) {
var c Config
err := v.Unmarshal(&c)
if err != nil {
log.Printf("unable to decode into struct, %v", err)
return nil, err
}
return &c, nil
}
// Get config
func GetConfig() (*Config, error) {
cfgFile, err := LoadConfig()
cfgFile, err := LoadConfig("settings")
if err != nil {
return nil, err
}
@ -50,3 +48,17 @@ func GetConfig() (*Config, error) {
}
return cfg, nil
}
func GetReply() (*Reply, error) {
cfgFile, err := LoadConfig("reply")
if err != nil {
return nil, err
}
reply := Reply{}
_err := cfgFile.Unmarshal(&reply)
if _err != nil {
log.Printf("unable to decode into struct, %v", err)
return nil, err
}
return &reply, nil
}

4
pkg/config/reply.go Normal file
View File

@ -0,0 +1,4 @@
package config
type Reply struct {
}

View File

@ -1,16 +1,29 @@
package settings
package config
import "github.com/andygrunwald/go-jira"
type Config struct {
Debug bool
Jira JiraConfig
Debug bool
Developers []int
Telegram TelegramConfig
Jira JiraConfig
BotVersion float64
Redis ReidsConfig
}
type TelegramConfig struct {
Token string
Url string
}
type JiraConfig struct {
Url string
Auth AuthConfig
Auth jira.BasicAuthTransport
}
type AuthConfig struct {
Username string
type ReidsConfig struct {
Host string
Port string
Password string
Db int
}

6
pkg/templates/error.qtpl Normal file
View File

@ -0,0 +1,6 @@
Hello is a simple template function.
{% func Error(err string) %}
***Ошибка:***
`{%s err %}`
{% endfunc %}

View File

@ -0,0 +1,66 @@
// Code generated by qtc from "error.qtpl". DO NOT EDIT.
// See https://github.com/valyala/quicktemplate for details.
// Hello is a simple template function.
//line pkg/templates/error.qtpl:3
package templates
//line pkg/templates/error.qtpl:3
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line pkg/templates/error.qtpl:3
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line pkg/templates/error.qtpl:3
func StreamError(qw422016 *qt422016.Writer, err string) {
//line pkg/templates/error.qtpl:3
qw422016.N().S(`
***Ошибка:***
`)
//line pkg/templates/error.qtpl:3
qw422016.N().S("`")
//line pkg/templates/error.qtpl:5
qw422016.E().S(err)
//line pkg/templates/error.qtpl:5
qw422016.N().S(``)
//line pkg/templates/error.qtpl:5
qw422016.N().S("`")
//line pkg/templates/error.qtpl:5
qw422016.N().S(`
`)
//line pkg/templates/error.qtpl:6
}
//line pkg/templates/error.qtpl:6
func WriteError(qq422016 qtio422016.Writer, err string) {
//line pkg/templates/error.qtpl:6
qw422016 := qt422016.AcquireWriter(qq422016)
//line pkg/templates/error.qtpl:6
StreamError(qw422016, err)
//line pkg/templates/error.qtpl:6
qt422016.ReleaseWriter(qw422016)
//line pkg/templates/error.qtpl:6
}
//line pkg/templates/error.qtpl:6
func Error(err string) string {
//line pkg/templates/error.qtpl:6
qb422016 := qt422016.AcquireByteBuffer()
//line pkg/templates/error.qtpl:6
WriteError(qb422016, err)
//line pkg/templates/error.qtpl:6
qs422016 := string(qb422016.B)
//line pkg/templates/error.qtpl:6
qt422016.ReleaseByteBuffer(qb422016)
//line pkg/templates/error.qtpl:6
return qs422016
//line pkg/templates/error.qtpl:6
}

37
pkg/templates/hello.qtpl Normal file
View File

@ -0,0 +1,37 @@
https://github.com/valyala/quicktemplate
https://github.com/valyala/quicktemplate
{% import (
"fmt"
"github.com/fatih/color"
telegram "gopkg.in/tucnak/telebot.v2"
)
%}
Приветствие пользователя
{% func Hello(name string) %}
Добрый день, *** {%s name %}!***
{% endfunc %}
Тайтл бота
{%func Title(bot *telegram.User,botVer float64, jira string) %}
{% code
d := color.New(color.FgCyan, color.Bold).SprintFunc()
v := fmt.Sprintf("version: ",)
v += d("v.",botVer ,"\n")
v += fmt.Sprintf("jira: ",)
v += d("https://",jira ,"\n")
v += fmt.Sprintf("url: " + d(" https://t.me/"+ bot.Username))
%}
_ _ _ _
(_|_)_ __ __ _ | |__ ___ | |_
| | | '__/ _` | | '_ \ / _ \| __|
| | | | | (_| | | |_) | (_) | |_
_/ |_|_| \__,_| |_.__/ \___/ \__|
|__/
{%s= v %}
{% endfunc %}

134
pkg/templates/hello.qtpl.go Normal file
View File

@ -0,0 +1,134 @@
// Code generated by qtc from "hello.qtpl". DO NOT EDIT.
// See https://github.com/valyala/quicktemplate for details.
// https://github.com/valyala/quicktemplate
// https://github.com/valyala/quicktemplate
//
//line pkg/templates/hello.qtpl:4
package templates
//line pkg/templates/hello.qtpl:4
import (
"fmt"
"github.com/fatih/color"
telegram "gopkg.in/tucnak/telebot.v2"
)
// Приветствие пользователя
//line pkg/templates/hello.qtpl:12
import (
qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate"
)
//line pkg/templates/hello.qtpl:12
var (
_ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer
)
//line pkg/templates/hello.qtpl:12
func StreamHello(qw422016 *qt422016.Writer, name string) {
//line pkg/templates/hello.qtpl:12
qw422016.N().S(`
Добрый день, *** `)
//line pkg/templates/hello.qtpl:13
qw422016.E().S(name)
//line pkg/templates/hello.qtpl:13
qw422016.N().S(`!***
`)
//line pkg/templates/hello.qtpl:14
}
//line pkg/templates/hello.qtpl:14
func WriteHello(qq422016 qtio422016.Writer, name string) {
//line pkg/templates/hello.qtpl:14
qw422016 := qt422016.AcquireWriter(qq422016)
//line pkg/templates/hello.qtpl:14
StreamHello(qw422016, name)
//line pkg/templates/hello.qtpl:14
qt422016.ReleaseWriter(qw422016)
//line pkg/templates/hello.qtpl:14
}
//line pkg/templates/hello.qtpl:14
func Hello(name string) string {
//line pkg/templates/hello.qtpl:14
qb422016 := qt422016.AcquireByteBuffer()
//line pkg/templates/hello.qtpl:14
WriteHello(qb422016, name)
//line pkg/templates/hello.qtpl:14
qs422016 := string(qb422016.B)
//line pkg/templates/hello.qtpl:14
qt422016.ReleaseByteBuffer(qb422016)
//line pkg/templates/hello.qtpl:14
return qs422016
//line pkg/templates/hello.qtpl:14
}
// Тайтл бота
//line pkg/templates/hello.qtpl:17
func StreamTitle(qw422016 *qt422016.Writer, bot *telegram.User, botVer float64, jira string) {
//line pkg/templates/hello.qtpl:17
qw422016.N().S(`
`)
//line pkg/templates/hello.qtpl:19
d := color.New(color.FgCyan, color.Bold).SprintFunc()
v := fmt.Sprintf("version: ")
v += d("v.", botVer, "\n")
v += fmt.Sprintf("jira: ")
v += d("https://", jira, "\n")
v += fmt.Sprintf("url: " + d(" https://t.me/"+bot.Username))
//line pkg/templates/hello.qtpl:26
qw422016.N().S(`
_ _ _ _
(_|_)_ __ __ _ | |__ ___ | |_
| | | '__/ _`)
//line pkg/templates/hello.qtpl:26
qw422016.N().S("`")
//line pkg/templates/hello.qtpl:26
qw422016.N().S(` | | '_ \ / _ \| __|
| | | | | (_| | | |_) | (_) | |_
_/ |_|_| \__,_| |_.__/ \___/ \__|
|__/
`)
//line pkg/templates/hello.qtpl:34
qw422016.N().S(v)
//line pkg/templates/hello.qtpl:34
qw422016.N().S(`
`)
//line pkg/templates/hello.qtpl:36
}
//line pkg/templates/hello.qtpl:36
func WriteTitle(qq422016 qtio422016.Writer, bot *telegram.User, botVer float64, jira string) {
//line pkg/templates/hello.qtpl:36
qw422016 := qt422016.AcquireWriter(qq422016)
//line pkg/templates/hello.qtpl:36
StreamTitle(qw422016, bot, botVer, jira)
//line pkg/templates/hello.qtpl:36
qt422016.ReleaseWriter(qw422016)
//line pkg/templates/hello.qtpl:36
}
//line pkg/templates/hello.qtpl:36
func Title(bot *telegram.User, botVer float64, jira string) string {
//line pkg/templates/hello.qtpl:36
qb422016 := qt422016.AcquireByteBuffer()
//line pkg/templates/hello.qtpl:36
WriteTitle(qb422016, bot, botVer, jira)
//line pkg/templates/hello.qtpl:36
qs422016 := string(qb422016.B)
//line pkg/templates/hello.qtpl:36
qt422016.ReleaseByteBuffer(qb422016)
//line pkg/templates/hello.qtpl:36
return qs422016
//line pkg/templates/hello.qtpl:36
}

11
pkg/utils/errors.go Normal file
View File

@ -0,0 +1,11 @@
package utils
import "log"
func SendError(devs []int) {
for _, dev := range devs {
log.Println(dev)
}
}
// 169837862

138
pkg/utils/hack.go Normal file
View File

@ -0,0 +1,138 @@
package utils
import (
"log"
"math/rand"
"time"
settings "git/ecom/jira-bot/pkg/config"
"github.com/andygrunwald/go-jira"
)
func Hack() {
cfg, _ := settings.GetConfig()
tp := jira.BasicAuthTransport{
Username: cfg.Jira.Auth.Username,
Password: cfg.Jira.Auth.Password,
}
jiraClient, _ := jira.NewClient(tp.Client(), cfg.Jira.Url)
me, _, _ := jiraClient.User.GetSelf()
timeNeed := 165 // Количество часов
tm := time.Hour * time.Duration(timeNeed)
log.Println("Необходимо поставить время:", tm)
issue := map[string][]string{ // Задачи
"SA-3000": {
"Конфигрурация nginx",
"Загрузка данных в elastic search",
"Проксирование запросов grpc_pass",
"Работа с вм msk-api601",
},
"ECOM-251": {
"Конфигрурация nginx",
"Проксирование запросов c микросервисов",
"Проксирование galamart.ru/api/v1",
"Настройка вебхуков",
"Авторизация и закрытие некоторых роутов",
},
"ECOM-252": {
"Работа с каталогом",
"Запросы в mysql за характеристиками",
"bulk импорт по городам из yml",
"Добавление изображений из папки /images/1000",
"Добавление комментариев",
},
"ECOM-253": {
"Разработка njs скрипта",
"Конфигрурация nginx",
},
"ECOM-254": {
"Создание 5млн карт",
"Загрузка карт в mindbox",
"Настройка вебхука на выдачу карт",
"Формирование сообщения в шину с картой",
},
"ECOM-255": {
"Добавление кронера для загрузки в mindbox",
"Сохранение и сравненение chk c сdp сообщением",
"Формирование csv",
"Создание бд и структуры храненения чеков",
},
}
// Дней
md := 31
// День с какого числа необходимо заполнить жиру
startDay := time.Date(2021, 05, 0, 9, 0, 0, 0, time.FixedZone(me.TimeZone, 0))
// Мап дней с часами работы
mr := make(map[time.Time]time.Duration)
// Тут должна быть проверка на правздники и выходные дни
for i := 0; i < md; i++ {
day := startDay.AddDate(0, 0, i)
if day.Weekday() != time.Saturday && day.Weekday() != time.Sunday && day.Day() != 9 && day.Day() != 1 && day.Day() != 2 {
mr[day] = 0
}
}
// Кол-во часов в день (сделать рандомно)
hoursDay := tm / time.Duration(len(mr))
for d := range mr {
mr[d] = hoursDay
}
// Задачи и сколько время на них потратить (сделать рандомно от приоритета)
type IssueToWork struct {
WorkTime time.Duration
Issue *jira.Issue
}
issues := []IssueToWork{}
timeToIssue := tm / time.Duration(len(issue))
for i := range issue {
isWork := IssueToWork{}
issue, _, _ := jiraClient.Issue.Get(i, nil)
isWork.Issue = issue
isWork.WorkTime = timeToIssue
issues = append(issues, isWork)
}
all := time.Hour * 0
// Сделать что бы комменты были тоже рандомнее
//usedComments := make(map[string]int)
for d, v := range mr {
randomIs := issues[rand.Intn(len(issues))]
// если в задаче осталось время то
if randomIs.WorkTime > time.Hour {
t := jira.Time(d)
work := jira.WorklogRecord{
TimeSpentSeconds: int(v.Seconds()),
Comment: issue[randomIs.Issue.Key][rand.Intn(len(issue[randomIs.Issue.Key]))],
Started: &t,
IssueID: randomIs.Issue.Key,
}
// Добавление отметки в задачу
if !cfg.Debug {
_, _, err := jiraClient.Issue.AddWorklogRecord(randomIs.Issue.Key, &work)
if err != nil {
log.Fatal(err)
}
}
log.Println("Отметил время на задаче:", work.IssueID)
log.Println("Осталось: ", tm-all)
//usedComments[randomComment] =+ 1
randomIs.WorkTime -= v
all += v
}
}
log.Println("Поставлено ", all.String(), "часов")
// for cm, v := range usedComments {
// log.Println(cm, v)
// }
}

16
pkg/utils/jira.go Normal file
View File

@ -0,0 +1,16 @@
package utils
import (
"git/ecom/jira-bot/pkg/config"
"github.com/andygrunwald/go-jira"
)
func GetClient(cfg *config.JiraConfig) (*jira.Client, error) {
jiraClient, err := jira.NewClient(cfg.Auth.Client(), cfg.Url)
if err != nil {
return nil, err
}
return jiraClient, nil
}

13
pkg/utils/pool.go Normal file
View File

@ -0,0 +1,13 @@
package utils
import (
"time"
telegram "gopkg.in/tucnak/telebot.v2"
)
func NewPooler(t int) *telegram.LongPoller {
tm := time.Second * time.Duration(t)
poller := &telegram.LongPoller{Timeout: tm}
return poller
}

16
pkg/utils/redis.go Normal file
View File

@ -0,0 +1,16 @@
package utils
import (
"git/ecom/jira-bot/pkg/config"
"github.com/go-redis/redis/v8"
)
// Возвращает клиент редиса
func Redis(config config.ReidsConfig) *redis.Client {
return redis.NewClient(&redis.Options{
Addr: config.Host + ":" + config.Port,
Password: config.Password,
DB: config.Db,
})
}

15
pkg/utils/user.go Normal file
View File

@ -0,0 +1,15 @@
package utils
type Role int32
const (
Client Role = 0
Manager Role = 1
)
type User struct {
Id int `json:"id"`
Username string `json:"username"`
Name string `json:"name"`
Role Role `json:"role"`
}