├── config └── event_templates │ ├── note.tpl │ ├── push.tpl │ ├── tag_push.tpl │ ├── job.tpl │ ├── merge_request.tpl │ └── pipeline.tpl ├── koyote.jpg ├── main.go ├── Dockerfile ├── .gitignore ├── pkg ├── config │ └── config.go ├── api │ └── api.go ├── events │ ├── events.go │ ├── template.go │ └── models.go └── telegram │ └── bot.go ├── LICENSE ├── go.mod ├── README.md └── go.sum /config/event_templates/note.tpl: -------------------------------------------------------------------------------- 1 | 📓 NOTE -------------------------------------------------------------------------------- /config/event_templates/push.tpl: -------------------------------------------------------------------------------- 1 | 🧑‍💻 CODE PUSHED -------------------------------------------------------------------------------- /config/event_templates/tag_push.tpl: -------------------------------------------------------------------------------- 1 | 🧑‍💻 TAG PUSHED -------------------------------------------------------------------------------- /koyote.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solyard/koyote/HEAD/koyote.jpg -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/koyote/pkg/api" 5 | "github.com/koyote/pkg/config" 6 | "github.com/koyote/pkg/telegram" 7 | ) 8 | 9 | func main() { 10 | config.LoadConfig() 11 | go telegram.StartBot() 12 | api.StartPolling() 13 | } 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine as build-env 2 | LABEL maintainer="dizstorm@gmail.com" 3 | COPY . /app 4 | WORKDIR /app 5 | RUN go mod download && CGO_ENABLED=0 go build -o /usr/bin/koyote . 6 | 7 | FROM alpine as final 8 | COPY --from=build-env /usr/bin/koyote /koyote 9 | ENTRYPOINT ["/koyote"] -------------------------------------------------------------------------------- /config/event_templates/job.tpl: -------------------------------------------------------------------------------- 1 | 🛠 BUILD 2 | {{ $status := .BuildStatus }} 3 | {{- if eq $status "success" -}} 🟢 SUCCESS {{ else }} 🔴 FAILURE {{- end }} {{ $status := .BuildStatus}} 4 | Status: {{- if eq $status "success" -}} 🟢 Success {{ else }} 🔴 Build Failed {{- end }} 5 | Project: {{ .ProjectName }} 6 | Initiator: {{ .User.Name }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | *test/ 17 | *prepeare_binaries/ -------------------------------------------------------------------------------- /config/event_templates/merge_request.tpl: -------------------------------------------------------------------------------- 1 | 🟡 MERGE REQUEST 2 | Initiator: {{ .User.Name }} 3 | Project: {{ .Project.Name }} 4 | MR Link: {{ .ObjectAttributes.URL }} 5 | MR Status: {{ if eq .ObjectAttributes.MergeStatus "can_be_merged" }}💚 CAN BE MERGE{{ else }}💔 CANNOT BE MERGED{{- end }} 6 | 7 | Please review and close merge request if you can do so. -------------------------------------------------------------------------------- /config/event_templates/pipeline.tpl: -------------------------------------------------------------------------------- 1 | 🛠 PIPELINE 2 | {{- $var := "" }} 3 | Stages: 4 | {{- range $buildStages := .Builds }} 5 | {{- if ne "success" $buildStages.Status }} {{ $var = "🔴 FAILURE"}} {{- else }} {{ $var = "🟢 SUCCESS"}} {{- end }} 6 | {{ if ne "success" $buildStages.Status }} ⛔️ {{ else }} ✅ {{ end }} {{ $buildStages.Stage | ToUpper }} 7 | {{- end}} 8 | Status: {{ $var }} 9 | Project: {{ .Project.Name }} -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/caarlos0/env/v6" 5 | log "github.com/gookit/slog" 6 | ) 7 | 8 | type ApplicationConfig struct { 9 | Global struct { 10 | ListenPort string `env:"KOYOTE_API_PORT" envDefault:"8081"` 11 | TelegramBotToken string `env:"KOYOTE_TELEGRAM_BOT_TOKEN,required"` 12 | } 13 | Events struct { 14 | Job bool `env:"KOYOTE_ENABLE_JOB_NOTIFICATION"` 15 | MergeRequest bool `env:"KOYOTE_ENABLE_MR_NOTIFICATION" envDefault:true` 16 | Note bool `env:"KOYOTE_ENABLE_NOTE_NOTIFICATION"` 17 | Pipeline bool `env:"KOYOTE_ENABLE_PIPELINE_NOTIFICATION" envDefault:true` 18 | Push bool `env:"KOYOTE_ENABLE_PUSH_NOTIFICATION"` 19 | TagPush bool `env:"KOYOTE_ENABLE_TAG_PUSH_NOTIFICATION"` 20 | } 21 | } 22 | 23 | var GlobalAppConfig ApplicationConfig 24 | 25 | func LoadConfig() { 26 | if err := env.Parse(&GlobalAppConfig); err != nil { 27 | log.Fatal("Error while parse envs for config to struct. Error: ", err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Vladislav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/koyote 2 | 3 | go 1.21.5 4 | 5 | require ( 6 | github.com/caarlos0/env/v6 v6.10.1 7 | github.com/gookit/slog v0.5.5 8 | github.com/gorilla/mux v1.8.1 9 | github.com/mymmrac/telego v0.29.1 10 | github.com/pkg/errors v0.9.1 11 | ) 12 | 13 | require ( 14 | github.com/andybalholm/brotli v1.1.0 // indirect 15 | github.com/bytedance/sonic v1.10.2 // indirect 16 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 17 | github.com/chenzhuoyu/iasm v0.9.1 // indirect 18 | github.com/fasthttp/router v1.4.22 // indirect 19 | github.com/gookit/color v1.5.4 // indirect 20 | github.com/gookit/goutil v0.6.15 // indirect 21 | github.com/gookit/gsr v0.1.0 // indirect 22 | github.com/grbit/go-json v0.11.0 // indirect 23 | github.com/klauspost/compress v1.17.6 // indirect 24 | github.com/klauspost/cpuid/v2 v2.2.6 // indirect 25 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect 26 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 27 | github.com/valyala/bytebufferpool v1.0.0 // indirect 28 | github.com/valyala/fasthttp v1.52.0 // indirect 29 | github.com/valyala/fastjson v1.6.4 // indirect 30 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 31 | golang.org/x/arch v0.6.0 // indirect 32 | golang.org/x/sync v0.5.0 // indirect 33 | golang.org/x/sys v0.17.0 // indirect 34 | golang.org/x/text v0.14.0 // indirect 35 | ) 36 | -------------------------------------------------------------------------------- /pkg/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | 8 | log "github.com/gookit/slog" 9 | "github.com/gorilla/mux" 10 | "github.com/koyote/pkg/config" 11 | "github.com/koyote/pkg/events" 12 | ) 13 | 14 | func returnError(w http.ResponseWriter, r *http.Request) { 15 | http.Error(w, "Please add chatID like in example: /notify/123123123123", http.StatusNotFound) 16 | } 17 | 18 | func receiveEventJSON(w http.ResponseWriter, r *http.Request) { 19 | w.Header().Set("Content-Type", "application/json") 20 | vars := mux.Vars(r) 21 | 22 | body, err := ioutil.ReadAll(r.Body) 23 | if err != nil { 24 | log.Error("Error while read payload from request to Koyote. Error: ", err) 25 | } 26 | 27 | err = events.EventMatcher(body, vars["chat_id"], vars["thread_id"]) 28 | if err != nil { 29 | http.Error(w, fmt.Sprint("Error while compare Event to template. Error: ", err), http.StatusBadRequest) 30 | } 31 | } 32 | 33 | func StartPolling() { 34 | router := mux.NewRouter().StrictSlash(true) 35 | router.HandleFunc("/notify/{chat_id}", receiveEventJSON).Methods("POST") 36 | router.HandleFunc("/notify/{chat_id}/{thread_id}", receiveEventJSON).Methods("POST") 37 | router.HandleFunc("/notify", returnError) 38 | 39 | log.Info("Starting API on port", config.GlobalAppConfig.Global.ListenPort) 40 | log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", config.GlobalAppConfig.Global.ListenPort), router)) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/events/events.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/koyote/pkg/telegram" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | type Event interface { 12 | TemplateMessage() (string, error) 13 | } 14 | 15 | func (e GitlabJobEvent) TemplateMessage() (string, error) { 16 | result, err := templateJobEventMessage(e, "job.tpl") 17 | if err != nil { 18 | return "", errors.Wrap(err, "Error while templating message!") 19 | } 20 | 21 | return result, nil 22 | } 23 | 24 | func (e GitlabMergeRequestEvent) TemplateMessage() (string, error) { 25 | result, err := templateMREventMessage(e, "merge_request.tpl") 26 | if err != nil { 27 | return "", errors.Wrap(err, "Error while templating message!") 28 | } 29 | 30 | return result, nil 31 | } 32 | 33 | func (e GitlabNoteEvent) TemplateMessage() (string, error) { 34 | result, err := templateNoteEventMessage(e, "note.tpl") 35 | if err != nil { 36 | return "", errors.Wrap(err, "Error while templating message!") 37 | } 38 | 39 | return result, nil 40 | } 41 | 42 | func (e GitlabPipelineEvent) TemplateMessage() (string, error) { 43 | result, err := templatePipelineEventMessage(e, "pipeline.tpl") 44 | if err != nil { 45 | return "", errors.Wrap(err, "Error while templating message!") 46 | } 47 | 48 | return result, nil 49 | } 50 | 51 | func (e GitlabPushEvent) TemplateMessage() (string, error) { 52 | result, err := templatePushEventMessage(e, "push.tpl") 53 | if err != nil { 54 | return "", errors.Wrap(err, "Error while templating message!") 55 | } 56 | 57 | return result, nil 58 | } 59 | 60 | func EventMatcher(eventJSON []byte, chatID, threadID string) error { 61 | var receivedEventType GitlabEventTypeDetector 62 | err := json.Unmarshal(eventJSON, &receivedEventType) 63 | if err != nil { 64 | return errors.Wrap(err, "Error while templating message!") 65 | } 66 | 67 | event, err := eventComparator(receivedEventType.ObjectKind, eventJSON) 68 | if err != nil { 69 | return errors.Wrap(err, "Error while templating message!") 70 | } 71 | 72 | eventMessage, err := event.TemplateMessage() 73 | if err != nil { 74 | return errors.Wrap(err, "Error while templating message!") 75 | } 76 | 77 | if threadID == "" { 78 | err = telegram.SendEventMessage(chatID, eventMessage) 79 | } else { 80 | err = telegram.SendEventMessageToThread(chatID, threadID, eventMessage) 81 | } 82 | 83 | if err != nil { 84 | return errors.Wrap(err, "Error while send event to Telegram. Event may be lost :( ") 85 | } 86 | 87 | return nil 88 | } 89 | 90 | func eventComparator(eventType string, data []byte) (Event, error) { 91 | var event Event 92 | switch eventType { 93 | case "build": 94 | event = &GitlabJobEvent{} 95 | case "merge_request": 96 | event = &GitlabMergeRequestEvent{} 97 | case "note": 98 | event = &GitlabNoteEvent{} 99 | case "pipeline": 100 | event = &GitlabPipelineEvent{} 101 | case "push": 102 | event = &GitlabPushEvent{} 103 | default: 104 | return nil, fmt.Errorf("Unknown event type: %s", eventType) 105 | } 106 | 107 | return event, json.Unmarshal(data, &event) 108 | } 109 | -------------------------------------------------------------------------------- /pkg/events/template.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "strings" 8 | "text/template" 9 | 10 | log "github.com/gookit/slog" 11 | ) 12 | 13 | func prepareTemplate(eventType, templateFilePath string) (*template.Template, error) { 14 | templatefuncMap := template.FuncMap{ 15 | "ToUpper": strings.ToUpper, 16 | } 17 | 18 | tplfile, err := os.ReadFile(fmt.Sprintf("config/event_templates/%v", templateFilePath)) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | template, err := template.New(eventType).Funcs(templatefuncMap).Parse(string(tplfile)) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | return template, nil 29 | } 30 | 31 | func templateJobEventMessage(gitlabEvent GitlabJobEvent, fileName string) (string, error) { 32 | template, err := prepareTemplate(gitlabEvent.ObjectKind, fileName) 33 | if err != nil { 34 | return "", err 35 | } 36 | 37 | var message bytes.Buffer 38 | err = template.Execute(&message, gitlabEvent) 39 | if err != nil { 40 | log.Error("Error while executing template. Error: ", err) 41 | return "", err 42 | } 43 | 44 | response := fmt.Sprintf("%v", &message) 45 | return response, nil 46 | } 47 | 48 | func templateMREventMessage(gitlabEvent GitlabMergeRequestEvent, fileName string) (string, error) { 49 | template, err := prepareTemplate(gitlabEvent.ObjectKind, fileName) 50 | if err != nil { 51 | return "", err 52 | } 53 | 54 | var message bytes.Buffer 55 | err = template.Execute(&message, gitlabEvent) 56 | if err != nil { 57 | log.Error("Error while executing template. Error: ", err) 58 | return "", err 59 | } 60 | 61 | response := fmt.Sprintf("%v", &message) 62 | return response, nil 63 | } 64 | 65 | func templateNoteEventMessage(gitlabEvent GitlabNoteEvent, fileName string) (string, error) { 66 | template, err := prepareTemplate(gitlabEvent.ObjectKind, fileName) 67 | if err != nil { 68 | return "", err 69 | } 70 | 71 | var message bytes.Buffer 72 | err = template.Execute(&message, gitlabEvent) 73 | if err != nil { 74 | log.Error("Error while executing template. Error: ", err) 75 | return "", err 76 | } 77 | 78 | response := fmt.Sprintf("%v", &message) 79 | return response, nil 80 | } 81 | 82 | func templatePipelineEventMessage(gitlabEvent GitlabPipelineEvent, fileName string) (string, error) { 83 | template, err := prepareTemplate(gitlabEvent.ObjectKind, fileName) 84 | if err != nil { 85 | return "", err 86 | } 87 | 88 | var message bytes.Buffer 89 | err = template.Execute(&message, gitlabEvent) 90 | if err != nil { 91 | return "", err 92 | } 93 | 94 | response := fmt.Sprintf("%v", &message) 95 | return response, nil 96 | } 97 | 98 | func templatePushEventMessage(gitlabEvent GitlabPushEvent, fileName string) (string, error) { 99 | template, err := prepareTemplate(gitlabEvent.ObjectKind, fileName) 100 | if err != nil { 101 | return "", err 102 | } 103 | 104 | var message bytes.Buffer 105 | err = template.Execute(&message, gitlabEvent) 106 | if err != nil { 107 | log.Error("Error while executing template. Error: ", err) 108 | return "", err 109 | } 110 | 111 | response := fmt.Sprintf("%v", &message) 112 | return response, nil 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

Koyote - Fast GitLab Event Notifications for Telegram

6 | 7 |
8 |

9 | 10 | 11 | # FYI 12 | 🦊 Koyote (no, it's not a misspelling) is a simple and fast Telegram bot that integrates with your GitLab (Cloud or Self-Hosted) to notify you about events that happen in your project. 13 | 14 | # TL;DR 15 | Run Koyote as Binary 16 | ``` 17 | KOYOTE_API_PORT=8081 KOYOTE_TELEGRAM_BOT_TOKEN=abc:11223344 ./koyote 18 | ``` 19 | 20 | Run Koyote with Docker: 21 | ``` 22 | docker run -p 8081:8081 koyote:v0.1 -e KOYOTE_API_PORT=8081 -e KOYOTE_TELEGRAM_BOT_TOKEN=abc:11223344 23 | ``` 24 | 25 | # Chat Utils 26 | 27 | Bot now supports some commands: 28 | ``` 29 | /chatID - Return current chatID 30 | /threadID - Return current threadID (If 0 then you are in General Thread or chat without Threads support) 31 | ``` 32 | 33 | # How Koyote works 34 | 35 | 1. Koyote receives an event from GitLab 36 | 2. Koyote tries to parse the response from the API to known models 37 | 3. Koyote templates a message for a Telegram notification 38 | 4. Koyote sends the message to a Telegram chat or channel depending on the ID that is received from the WebHook URL 39 | 40 | 41 | # Koyote parameters 42 | |Parameter|Description|Default Value| 43 | |--|--|--| 44 | |`KOYOTE_API_PORT`|Koyote web-server port| 8081| 45 | |`KOYOTE_TELEGRAM_BOT_TOKEN`|Telegram bot token from @BotFather| empty (required)| 46 | |`KOYOTE_ENABLE_JOB_NOTIFICATION`|Enable Telegram notification for JOB event|false| 47 | |`KOYOTE_ENABLE_MR_NOTIFICATION`|Enable Telegram notification for MERGE REQUEST event|true| 48 | |`KOYOTE_ENABLE_NOTE_NOTIFICATION`|Enable Telegram notification for NOTE event|false| 49 | |`KOYOTE_ENABLE_PIPELINE_NOTIFICATION`|Enable Telegram notification for PIPELINE event|true| 50 | |`KOYOTE_ENABLE_PUSH_NOTIFICATION`|Enable Telegram notification for PUSH event|false| 51 | |`KOYOTE_ENABLE_TAG_PUSH_NOTIFICATION`|Enable Telegram notification for TAG PUSH event|false| 52 | 53 | # GitLab configuration 54 | 55 | 1. Open your project in GitLab and go to Settings -> Webhooks 56 | 2. Check the triggers that you want to receive in Telegram 57 | 3. Insert the URL (e.g. `http://koyote/notify//`) (topic_id optional field if you are using Telegram Topics) 58 | 4. Press the "Add Webhook" button at the bottom of the page 59 | 5. At the bottom of the page, you can see the new webhook. Try sending a test event with the "Test" button and select the event you want to receive 60 | 61 | # Telegram configuration 62 | 1. Go to the @BotFather 63 | 2. Follow the instructions to create the bot 64 | 3. Get the Bot API token and forward it to Koyote with the environment variable `KOYOTE_TELEGRAM_BOT_TOKEN` 65 | 4. Enjoy :) 66 | 67 | # Roadmap 68 | - [ ] Improve stability and fix the codestyle 69 | - [ ] Implement a normal logic for taskpooler 70 | - [ ] ... 71 | 72 | # What is this bot for? 73 | 74 | We faced the problem of developers spending a lot of time notifying the team about new MR requests or failed builds and pipelines in the development chat. So I created this bot to make notifications faster and more automated. This bot helps our developers save time by not having to notify others manually and resend requests for new MRs to other developers. That's all. 75 | 76 | After testing this bot in my projects for a month and not finding a normal realization of a bot like mine, I decided to create a community version for developers who are looking for something to automate the notification process. 77 | 78 |

Logo by MJ 5.2 :)

79 | 80 | -------------------------------------------------------------------------------- /pkg/telegram/bot.go: -------------------------------------------------------------------------------- 1 | package telegram 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | log "github.com/gookit/slog" 8 | "github.com/koyote/pkg/config" 9 | "github.com/mymmrac/telego" 10 | th "github.com/mymmrac/telego/telegohandler" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | var Bot *telego.Bot 15 | 16 | func StartBot() { 17 | // Initialise BOT 18 | botToken := config.GlobalAppConfig.Global.TelegramBotToken 19 | bot, err := telego.NewBot(botToken) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | log.Info("Telegram bot started!") 25 | Bot = bot 26 | // Start Polling 27 | updates, _ := bot.UpdatesViaLongPolling(nil) 28 | defer bot.StopLongPolling() 29 | 30 | // Create bot handler and specify from where to get updates 31 | bh, _ := th.NewBotHandler(bot, updates) 32 | 33 | // Stop handling updates 34 | defer bh.Stop() 35 | 36 | bh.Handle(func(bot *telego.Bot, update telego.Update) { 37 | _, _ = bot.SendMessage( 38 | &telego.SendMessageParams{ 39 | ChatID: update.Message.Chat.ChatID(), 40 | Text: fmt.Sprintf("Hello %s! Use /help command to get available commands!", update.Message.From.FirstName), 41 | ParseMode: "HTML", 42 | LinkPreviewOptions: &telego.LinkPreviewOptions{IsDisabled: true}, 43 | ReplyParameters: &telego.ReplyParameters{MessageID: update.Message.MessageID}, 44 | }, 45 | ) 46 | }, th.CommandEqual("start")) 47 | 48 | bh.Handle(func(bot *telego.Bot, update telego.Update) { 49 | _, _ = bot.SendMessage( 50 | &telego.SendMessageParams{ 51 | ChatID: update.Message.Chat.ChatID(), 52 | Text: fmt.Sprintf(` 53 | Hello %s! Have a look on supported commands: 54 | 55 | /chatID - Return current chatID 56 | /threadID - Return current threadID (If 0 then you are in General Thread or chat without Threads support)`, update.Message.From.FirstName), 57 | ParseMode: "HTML", 58 | LinkPreviewOptions: &telego.LinkPreviewOptions{IsDisabled: true}, 59 | ReplyParameters: &telego.ReplyParameters{MessageID: update.Message.MessageID}, 60 | }, 61 | ) 62 | }, th.CommandEqual("help")) 63 | 64 | bh.Handle(func(bot *telego.Bot, update telego.Update) { 65 | _, _ = bot.SendMessage( 66 | &telego.SendMessageParams{ 67 | ChatID: update.Message.Chat.ChatID(), 68 | Text: fmt.Sprintf("Current Thread ID: %v", update.Message.MessageThreadID), 69 | ParseMode: "HTML", 70 | LinkPreviewOptions: &telego.LinkPreviewOptions{IsDisabled: true}, 71 | ReplyParameters: &telego.ReplyParameters{MessageID: update.Message.MessageID}, 72 | }, 73 | ) 74 | }, th.CommandEqual("threadID")) 75 | 76 | bh.Handle(func(bot *telego.Bot, update telego.Update) { 77 | _, _ = bot.SendMessage( 78 | &telego.SendMessageParams{ 79 | ChatID: update.Message.Chat.ChatID(), 80 | Text: fmt.Sprintf("Current chat ID: %v", update.Message.Chat.ID), 81 | ParseMode: "HTML", 82 | LinkPreviewOptions: &telego.LinkPreviewOptions{IsDisabled: true}, 83 | ReplyParameters: &telego.ReplyParameters{MessageID: update.Message.MessageID}, 84 | }, 85 | ) 86 | }, th.CommandEqual("chatID")) 87 | 88 | bh.Handle(func(bot *telego.Bot, update telego.Update) { 89 | _, _ = bot.SendMessage( 90 | &telego.SendMessageParams{ 91 | ChatID: update.Message.Chat.ChatID(), 92 | Text: "Unknown command, use /help to get additional info about commands", 93 | ParseMode: "HTML", 94 | LinkPreviewOptions: &telego.LinkPreviewOptions{IsDisabled: true}, 95 | ReplyParameters: &telego.ReplyParameters{MessageID: update.Message.MessageID}, 96 | }, 97 | ) 98 | }, th.AnyCommand()) 99 | 100 | // Start handling updates 101 | bh.Start() 102 | } 103 | 104 | func SendEventMessage(chatID string, eventMessage string) error { 105 | chatIDInt, err := strconv.Atoi(chatID) 106 | if err != nil { 107 | return err 108 | } 109 | _, err = Bot.SendMessage( 110 | &telego.SendMessageParams{ 111 | ChatID: telego.ChatID{ID: int64(chatIDInt)}, 112 | Text: eventMessage, 113 | ParseMode: "HTML", 114 | LinkPreviewOptions: &telego.LinkPreviewOptions{IsDisabled: true}, 115 | }, 116 | ) 117 | 118 | return errors.Wrap(err, "Error while sending message to Telegram") 119 | } 120 | 121 | func SendEventMessageToThread(chatID, threadID, eventMessage string) error { 122 | chatIDInt, err := strconv.Atoi(chatID) 123 | if err != nil { 124 | return err 125 | } 126 | 127 | threadIDInt, err := strconv.Atoi(threadID) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | _, err = Bot.SendMessage( 133 | &telego.SendMessageParams{ 134 | ChatID: telego.ChatID{ID: int64(chatIDInt)}, 135 | MessageThreadID: int(threadIDInt), 136 | Text: eventMessage, 137 | ParseMode: "HTML", 138 | LinkPreviewOptions: &telego.LinkPreviewOptions{IsDisabled: true}, 139 | }, 140 | ) 141 | 142 | return errors.Wrap(err, "Error while sending message to Telegram") 143 | } 144 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 2 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 3 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 4 | github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= 5 | github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= 6 | github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= 7 | github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= 8 | github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= 9 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 10 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 11 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= 12 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= 13 | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 14 | github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= 15 | github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 16 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 17 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 18 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/fasthttp/router v1.4.22 h1:qwWcYBbndVDwts4dKaz+A2ehsnbKilmiP6pUhXBfYKo= 20 | github.com/fasthttp/router v1.4.22/go.mod h1:KeMvHLqhlB9vyDWD5TSvTccl9qeWrjSSiTJrJALHKV0= 21 | github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= 22 | github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= 23 | github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo= 24 | github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY= 25 | github.com/gookit/gsr v0.1.0 h1:0gadWaYGU4phMs0bma38t+Do5OZowRMEVlHv31p0Zig= 26 | github.com/gookit/gsr v0.1.0/go.mod h1:7wv4Y4WCnil8+DlDYHBjidzrEzfHhXEoFjEA0pPPWpI= 27 | github.com/gookit/slog v0.5.5 h1:XoyK3NilKzuC/umvnqTQDHTOnpC8R6pvlr/ht9PyfgU= 28 | github.com/gookit/slog v0.5.5/go.mod h1:RfIwzoaQ8wZbKdcqG7+3EzbkMqcp2TUn3mcaSZAw2EQ= 29 | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= 30 | github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 31 | github.com/grbit/go-json v0.11.0 h1:bAbyMdYrYl/OjYsSqLH99N2DyQ291mHy726Mx+sYrnc= 32 | github.com/grbit/go-json v0.11.0/go.mod h1:IYpHsdybQ386+6g3VE6AXQ3uTGa5mquBme5/ZWmtzek= 33 | github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= 34 | github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 35 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 36 | github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= 37 | github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 38 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 39 | github.com/mymmrac/telego v0.29.1 h1:nsNnK0mS18OL+unoDjDI6BVfafJBbT8Wtj7rCzEWoM8= 40 | github.com/mymmrac/telego v0.29.1/go.mod h1:ZLD1+L2TQRr97NPOCoN1V2w8y9kmFov33OfZ3qT8cF4= 41 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 42 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 43 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 44 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 45 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= 46 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= 47 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 48 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 49 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 50 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 51 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 52 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 53 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 54 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 55 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 56 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 57 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 58 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 59 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 60 | github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= 61 | github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= 62 | github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= 63 | github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= 64 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 65 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 66 | go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= 67 | go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 68 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 69 | golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= 70 | golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 71 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= 72 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= 73 | golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= 74 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 75 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 76 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= 77 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 78 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 79 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 80 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 81 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 82 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 83 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 84 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 85 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 86 | -------------------------------------------------------------------------------- /pkg/events/models.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import "time" 4 | 5 | type GitlabEventTypeDetector struct { 6 | ObjectKind string `json:"object_kind"` 7 | } 8 | 9 | type GitlabPushEvent struct { 10 | ObjectKind string `json:"object_kind"` 11 | EventName string `json:"event_name"` 12 | Before string `json:"before"` 13 | After string `json:"after"` 14 | Ref string `json:"ref"` 15 | CheckoutSha string `json:"checkout_sha"` 16 | Message interface{} `json:"message"` 17 | UserID int `json:"user_id"` 18 | UserName string `json:"user_name"` 19 | UserUsername string `json:"user_username"` 20 | UserEmail string `json:"user_email"` 21 | UserAvatar string `json:"user_avatar"` 22 | ProjectID int `json:"project_id"` 23 | Project struct { 24 | ID int `json:"id"` 25 | Name string `json:"name"` 26 | Description string `json:"description"` 27 | WebURL string `json:"web_url"` 28 | AvatarURL interface{} `json:"avatar_url"` 29 | GitSSHURL string `json:"git_ssh_url"` 30 | GitHTTPURL string `json:"git_http_url"` 31 | Namespace string `json:"namespace"` 32 | VisibilityLevel int `json:"visibility_level"` 33 | PathWithNamespace string `json:"path_with_namespace"` 34 | DefaultBranch string `json:"default_branch"` 35 | CiConfigPath string `json:"ci_config_path"` 36 | Homepage string `json:"homepage"` 37 | URL string `json:"url"` 38 | SSHURL string `json:"ssh_url"` 39 | HTTPURL string `json:"http_url"` 40 | } `json:"project"` 41 | Commits []struct { 42 | ID string `json:"id"` 43 | Message string `json:"message"` 44 | Title string `json:"title"` 45 | Timestamp time.Time `json:"timestamp"` 46 | URL string `json:"url"` 47 | Author struct { 48 | Name string `json:"name"` 49 | Email string `json:"email"` 50 | } `json:"author"` 51 | Added []interface{} `json:"added"` 52 | Modified []string `json:"modified"` 53 | Removed []interface{} `json:"removed"` 54 | } `json:"commits"` 55 | TotalCommitsCount int `json:"total_commits_count"` 56 | PushOptions struct { 57 | } `json:"push_options"` 58 | Repository struct { 59 | Name string `json:"name"` 60 | URL string `json:"url"` 61 | Description string `json:"description"` 62 | Homepage string `json:"homepage"` 63 | GitHTTPURL string `json:"git_http_url"` 64 | GitSSHURL string `json:"git_ssh_url"` 65 | VisibilityLevel int `json:"visibility_level"` 66 | } `json:"repository"` 67 | } 68 | 69 | type GitlabTagPushEvent struct { 70 | ObjectKind string `json:"object_kind"` 71 | EventName string `json:"event_name"` 72 | Before string `json:"before"` 73 | After string `json:"after"` 74 | Ref string `json:"ref"` 75 | CheckoutSha string `json:"checkout_sha"` 76 | Message interface{} `json:"message"` 77 | UserID int `json:"user_id"` 78 | UserName string `json:"user_name"` 79 | UserUsername string `json:"user_username"` 80 | UserEmail string `json:"user_email"` 81 | UserAvatar string `json:"user_avatar"` 82 | ProjectID int `json:"project_id"` 83 | Project struct { 84 | ID int `json:"id"` 85 | Name string `json:"name"` 86 | Description string `json:"description"` 87 | WebURL string `json:"web_url"` 88 | AvatarURL interface{} `json:"avatar_url"` 89 | GitSSHURL string `json:"git_ssh_url"` 90 | GitHTTPURL string `json:"git_http_url"` 91 | Namespace string `json:"namespace"` 92 | VisibilityLevel int `json:"visibility_level"` 93 | PathWithNamespace string `json:"path_with_namespace"` 94 | DefaultBranch string `json:"default_branch"` 95 | CiConfigPath string `json:"ci_config_path"` 96 | Homepage string `json:"homepage"` 97 | URL string `json:"url"` 98 | SSHURL string `json:"ssh_url"` 99 | HTTPURL string `json:"http_url"` 100 | } `json:"project"` 101 | Commits []struct { 102 | ID string `json:"id"` 103 | Message string `json:"message"` 104 | Title string `json:"title"` 105 | Timestamp time.Time `json:"timestamp"` 106 | URL string `json:"url"` 107 | Author struct { 108 | Name string `json:"name"` 109 | Email string `json:"email"` 110 | } `json:"author"` 111 | Added []interface{} `json:"added"` 112 | Modified []string `json:"modified"` 113 | Removed []interface{} `json:"removed"` 114 | } `json:"commits"` 115 | TotalCommitsCount int `json:"total_commits_count"` 116 | PushOptions struct { 117 | } `json:"push_options"` 118 | Repository struct { 119 | Name string `json:"name"` 120 | URL string `json:"url"` 121 | Description string `json:"description"` 122 | Homepage string `json:"homepage"` 123 | GitHTTPURL string `json:"git_http_url"` 124 | GitSSHURL string `json:"git_ssh_url"` 125 | VisibilityLevel int `json:"visibility_level"` 126 | } `json:"repository"` 127 | } 128 | 129 | type GitlabNoteEvent struct { 130 | ObjectKind string `json:"object_kind"` 131 | EventType string `json:"event_type"` 132 | User struct { 133 | ID int `json:"id"` 134 | Name string `json:"name"` 135 | Username string `json:"username"` 136 | AvatarURL string `json:"avatar_url"` 137 | Email string `json:"email"` 138 | } `json:"user"` 139 | ProjectID int `json:"project_id"` 140 | Project struct { 141 | ID int `json:"id"` 142 | Name string `json:"name"` 143 | Description string `json:"description"` 144 | WebURL string `json:"web_url"` 145 | AvatarURL interface{} `json:"avatar_url"` 146 | GitSSHURL string `json:"git_ssh_url"` 147 | GitHTTPURL string `json:"git_http_url"` 148 | Namespace string `json:"namespace"` 149 | VisibilityLevel int `json:"visibility_level"` 150 | PathWithNamespace string `json:"path_with_namespace"` 151 | DefaultBranch string `json:"default_branch"` 152 | CiConfigPath string `json:"ci_config_path"` 153 | Homepage string `json:"homepage"` 154 | URL string `json:"url"` 155 | SSHURL string `json:"ssh_url"` 156 | HTTPURL string `json:"http_url"` 157 | } `json:"project"` 158 | ObjectAttributes struct { 159 | Attachment interface{} `json:"attachment"` 160 | AuthorID int `json:"author_id"` 161 | ChangePosition interface{} `json:"change_position"` 162 | CommitID string `json:"commit_id"` 163 | CreatedAt time.Time `json:"created_at"` 164 | DiscussionID string `json:"discussion_id"` 165 | ID int `json:"id"` 166 | LineCode interface{} `json:"line_code"` 167 | Note string `json:"note"` 168 | NoteableID interface{} `json:"noteable_id"` 169 | NoteableType string `json:"noteable_type"` 170 | OriginalPosition interface{} `json:"original_position"` 171 | Position interface{} `json:"position"` 172 | ProjectID int `json:"project_id"` 173 | ResolvedAt interface{} `json:"resolved_at"` 174 | ResolvedByID interface{} `json:"resolved_by_id"` 175 | ResolvedByPush interface{} `json:"resolved_by_push"` 176 | StDiff interface{} `json:"st_diff"` 177 | System bool `json:"system"` 178 | Type interface{} `json:"type"` 179 | UpdatedAt time.Time `json:"updated_at"` 180 | UpdatedByID interface{} `json:"updated_by_id"` 181 | Description string `json:"description"` 182 | URL string `json:"url"` 183 | } `json:"object_attributes"` 184 | Repository struct { 185 | Name string `json:"name"` 186 | URL string `json:"url"` 187 | Description string `json:"description"` 188 | Homepage string `json:"homepage"` 189 | } `json:"repository"` 190 | Commit struct { 191 | ID string `json:"id"` 192 | Message string `json:"message"` 193 | Title string `json:"title"` 194 | Timestamp time.Time `json:"timestamp"` 195 | URL string `json:"url"` 196 | Author struct { 197 | Name string `json:"name"` 198 | Email string `json:"email"` 199 | } `json:"author"` 200 | } `json:"commit"` 201 | } 202 | 203 | type AutoGenerated struct { 204 | ObjectKind string `json:"object_kind"` 205 | EventType string `json:"event_type"` 206 | User struct { 207 | ID int `json:"id"` 208 | Name string `json:"name"` 209 | Username string `json:"username"` 210 | AvatarURL string `json:"avatar_url"` 211 | Email string `json:"email"` 212 | } `json:"user"` 213 | Project struct { 214 | ID int `json:"id"` 215 | Name string `json:"name"` 216 | Description string `json:"description"` 217 | WebURL string `json:"web_url"` 218 | AvatarURL interface{} `json:"avatar_url"` 219 | GitSSHURL string `json:"git_ssh_url"` 220 | GitHTTPURL string `json:"git_http_url"` 221 | Namespace string `json:"namespace"` 222 | VisibilityLevel int `json:"visibility_level"` 223 | PathWithNamespace string `json:"path_with_namespace"` 224 | DefaultBranch string `json:"default_branch"` 225 | CiConfigPath string `json:"ci_config_path"` 226 | Homepage string `json:"homepage"` 227 | URL string `json:"url"` 228 | SSHURL string `json:"ssh_url"` 229 | HTTPURL string `json:"http_url"` 230 | } `json:"project"` 231 | ObjectAttributes struct { 232 | AssigneeID interface{} `json:"assignee_id"` 233 | AuthorID int `json:"author_id"` 234 | CreatedAt time.Time `json:"created_at"` 235 | Description string `json:"description"` 236 | HeadPipelineID interface{} `json:"head_pipeline_id"` 237 | ID int `json:"id"` 238 | Iid int `json:"iid"` 239 | LastEditedAt interface{} `json:"last_edited_at"` 240 | LastEditedByID interface{} `json:"last_edited_by_id"` 241 | MergeCommitSha interface{} `json:"merge_commit_sha"` 242 | MergeError interface{} `json:"merge_error"` 243 | MergeParams struct { 244 | ForceRemoveSourceBranch string `json:"force_remove_source_branch"` 245 | } `json:"merge_params"` 246 | MergeStatus string `json:"merge_status"` 247 | MergeUserID interface{} `json:"merge_user_id"` 248 | MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` 249 | MilestoneID interface{} `json:"milestone_id"` 250 | SourceBranch string `json:"source_branch"` 251 | SourceProjectID int `json:"source_project_id"` 252 | StateID int `json:"state_id"` 253 | TargetBranch string `json:"target_branch"` 254 | TargetProjectID int `json:"target_project_id"` 255 | TimeEstimate int `json:"time_estimate"` 256 | Title string `json:"title"` 257 | UpdatedAt time.Time `json:"updated_at"` 258 | UpdatedByID interface{} `json:"updated_by_id"` 259 | URL string `json:"url"` 260 | Source struct { 261 | ID int `json:"id"` 262 | Name string `json:"name"` 263 | Description string `json:"description"` 264 | WebURL string `json:"web_url"` 265 | AvatarURL interface{} `json:"avatar_url"` 266 | GitSSHURL string `json:"git_ssh_url"` 267 | GitHTTPURL string `json:"git_http_url"` 268 | Namespace string `json:"namespace"` 269 | VisibilityLevel int `json:"visibility_level"` 270 | PathWithNamespace string `json:"path_with_namespace"` 271 | DefaultBranch string `json:"default_branch"` 272 | CiConfigPath string `json:"ci_config_path"` 273 | Homepage string `json:"homepage"` 274 | URL string `json:"url"` 275 | SSHURL string `json:"ssh_url"` 276 | HTTPURL string `json:"http_url"` 277 | } `json:"source"` 278 | Target struct { 279 | ID int `json:"id"` 280 | Name string `json:"name"` 281 | Description string `json:"description"` 282 | WebURL string `json:"web_url"` 283 | AvatarURL interface{} `json:"avatar_url"` 284 | GitSSHURL string `json:"git_ssh_url"` 285 | GitHTTPURL string `json:"git_http_url"` 286 | Namespace string `json:"namespace"` 287 | VisibilityLevel int `json:"visibility_level"` 288 | PathWithNamespace string `json:"path_with_namespace"` 289 | DefaultBranch string `json:"default_branch"` 290 | CiConfigPath string `json:"ci_config_path"` 291 | Homepage string `json:"homepage"` 292 | URL string `json:"url"` 293 | SSHURL string `json:"ssh_url"` 294 | HTTPURL string `json:"http_url"` 295 | } `json:"target"` 296 | LastCommit struct { 297 | ID string `json:"id"` 298 | Message string `json:"message"` 299 | Title string `json:"title"` 300 | Timestamp time.Time `json:"timestamp"` 301 | URL string `json:"url"` 302 | Author struct { 303 | Name string `json:"name"` 304 | Email string `json:"email"` 305 | } `json:"author"` 306 | } `json:"last_commit"` 307 | WorkInProgress bool `json:"work_in_progress"` 308 | TotalTimeSpent int `json:"total_time_spent"` 309 | HumanTotalTimeSpent interface{} `json:"human_total_time_spent"` 310 | HumanTimeEstimate interface{} `json:"human_time_estimate"` 311 | AssigneeIds []interface{} `json:"assignee_ids"` 312 | State string `json:"state"` 313 | } `json:"object_attributes"` 314 | Labels []interface{} `json:"labels"` 315 | Changes struct { 316 | } `json:"changes"` 317 | Repository struct { 318 | Name string `json:"name"` 319 | URL string `json:"url"` 320 | Description string `json:"description"` 321 | Homepage string `json:"homepage"` 322 | } `json:"repository"` 323 | } 324 | 325 | type GitlabMergeRequestEvent struct { 326 | ObjectKind string `json:"object_kind"` 327 | EventType string `json:"event_type"` 328 | User struct { 329 | ID int `json:"id"` 330 | Name string `json:"name"` 331 | Username string `json:"username"` 332 | AvatarURL string `json:"avatar_url"` 333 | Email string `json:"email"` 334 | } `json:"user"` 335 | Project struct { 336 | ID int `json:"id"` 337 | Name string `json:"name"` 338 | Description string `json:"description"` 339 | WebURL string `json:"web_url"` 340 | AvatarURL interface{} `json:"avatar_url"` 341 | GitSSHURL string `json:"git_ssh_url"` 342 | GitHTTPURL string `json:"git_http_url"` 343 | Namespace string `json:"namespace"` 344 | VisibilityLevel int `json:"visibility_level"` 345 | PathWithNamespace string `json:"path_with_namespace"` 346 | DefaultBranch string `json:"default_branch"` 347 | CiConfigPath string `json:"ci_config_path"` 348 | Homepage string `json:"homepage"` 349 | URL string `json:"url"` 350 | SSHURL string `json:"ssh_url"` 351 | HTTPURL string `json:"http_url"` 352 | } `json:"project"` 353 | ObjectAttributes struct { 354 | AssigneeID interface{} `json:"assignee_id"` 355 | AuthorID int `json:"author_id"` 356 | CreatedAt time.Time `json:"created_at"` 357 | Description string `json:"description"` 358 | HeadPipelineID interface{} `json:"head_pipeline_id"` 359 | ID int `json:"id"` 360 | Iid int `json:"iid"` 361 | LastEditedAt interface{} `json:"last_edited_at"` 362 | LastEditedByID interface{} `json:"last_edited_by_id"` 363 | MergeCommitSha interface{} `json:"merge_commit_sha"` 364 | MergeError interface{} `json:"merge_error"` 365 | MergeParams struct { 366 | ForceRemoveSourceBranch string `json:"force_remove_source_branch"` 367 | } `json:"merge_params"` 368 | MergeStatus string `json:"merge_status"` 369 | MergeUserID interface{} `json:"merge_user_id"` 370 | MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` 371 | MilestoneID interface{} `json:"milestone_id"` 372 | SourceBranch string `json:"source_branch"` 373 | SourceProjectID int `json:"source_project_id"` 374 | StateID int `json:"state_id"` 375 | TargetBranch string `json:"target_branch"` 376 | TargetProjectID int `json:"target_project_id"` 377 | TimeEstimate int `json:"time_estimate"` 378 | Title string `json:"title"` 379 | UpdatedAt time.Time `json:"updated_at"` 380 | UpdatedByID interface{} `json:"updated_by_id"` 381 | URL string `json:"url"` 382 | Source struct { 383 | ID int `json:"id"` 384 | Name string `json:"name"` 385 | Description string `json:"description"` 386 | WebURL string `json:"web_url"` 387 | AvatarURL interface{} `json:"avatar_url"` 388 | GitSSHURL string `json:"git_ssh_url"` 389 | GitHTTPURL string `json:"git_http_url"` 390 | Namespace string `json:"namespace"` 391 | VisibilityLevel int `json:"visibility_level"` 392 | PathWithNamespace string `json:"path_with_namespace"` 393 | DefaultBranch string `json:"default_branch"` 394 | CiConfigPath string `json:"ci_config_path"` 395 | Homepage string `json:"homepage"` 396 | URL string `json:"url"` 397 | SSHURL string `json:"ssh_url"` 398 | HTTPURL string `json:"http_url"` 399 | } `json:"source"` 400 | Target struct { 401 | ID int `json:"id"` 402 | Name string `json:"name"` 403 | Description string `json:"description"` 404 | WebURL string `json:"web_url"` 405 | AvatarURL interface{} `json:"avatar_url"` 406 | GitSSHURL string `json:"git_ssh_url"` 407 | GitHTTPURL string `json:"git_http_url"` 408 | Namespace string `json:"namespace"` 409 | VisibilityLevel int `json:"visibility_level"` 410 | PathWithNamespace string `json:"path_with_namespace"` 411 | DefaultBranch string `json:"default_branch"` 412 | CiConfigPath string `json:"ci_config_path"` 413 | Homepage string `json:"homepage"` 414 | URL string `json:"url"` 415 | SSHURL string `json:"ssh_url"` 416 | HTTPURL string `json:"http_url"` 417 | } `json:"target"` 418 | LastCommit struct { 419 | ID string `json:"id"` 420 | Message string `json:"message"` 421 | Title string `json:"title"` 422 | Timestamp time.Time `json:"timestamp"` 423 | URL string `json:"url"` 424 | Author struct { 425 | Name string `json:"name"` 426 | Email string `json:"email"` 427 | } `json:"author"` 428 | } `json:"last_commit"` 429 | WorkInProgress bool `json:"work_in_progress"` 430 | TotalTimeSpent int `json:"total_time_spent"` 431 | HumanTotalTimeSpent interface{} `json:"human_total_time_spent"` 432 | HumanTimeEstimate interface{} `json:"human_time_estimate"` 433 | AssigneeIds []interface{} `json:"assignee_ids"` 434 | State string `json:"state"` 435 | } `json:"object_attributes"` 436 | Labels []interface{} `json:"labels"` 437 | Changes struct { 438 | } `json:"changes"` 439 | Repository struct { 440 | Name string `json:"name"` 441 | URL string `json:"url"` 442 | Description string `json:"description"` 443 | Homepage string `json:"homepage"` 444 | } `json:"repository"` 445 | } 446 | 447 | type GitlabJobEvent struct { 448 | ObjectKind string `json:"object_kind"` 449 | Ref string `json:"ref"` 450 | Tag bool `json:"tag"` 451 | BeforeSha string `json:"before_sha"` 452 | Sha string `json:"sha"` 453 | BuildID int `json:"build_id"` 454 | BuildName string `json:"build_name"` 455 | BuildStage string `json:"build_stage"` 456 | BuildStatus string `json:"build_status"` 457 | BuildStartedAt time.Time `json:"build_started_at"` 458 | BuildFinishedAt time.Time `json:"build_finished_at"` 459 | BuildDuration float64 `json:"build_duration"` 460 | BuildAllowFailure bool `json:"build_allow_failure"` 461 | BuildFailureReason string `json:"build_failure_reason"` 462 | PipelineID int `json:"pipeline_id"` 463 | Runner interface{} `json:"runner"` 464 | ProjectID int `json:"project_id"` 465 | ProjectName string `json:"project_name"` 466 | User struct { 467 | ID int `json:"id"` 468 | Name string `json:"name"` 469 | Username string `json:"username"` 470 | AvatarURL string `json:"avatar_url"` 471 | Email string `json:"email"` 472 | } `json:"user"` 473 | Commit struct { 474 | ID int `json:"id"` 475 | Sha string `json:"sha"` 476 | Message string `json:"message"` 477 | AuthorName string `json:"author_name"` 478 | AuthorEmail string `json:"author_email"` 479 | AuthorURL string `json:"author_url"` 480 | Status string `json:"status"` 481 | Duration int `json:"duration"` 482 | StartedAt time.Time `json:"started_at"` 483 | FinishedAt time.Time `json:"finished_at"` 484 | } `json:"commit"` 485 | Repository struct { 486 | Name string `json:"name"` 487 | URL string `json:"url"` 488 | Description string `json:"description"` 489 | Homepage string `json:"homepage"` 490 | GitHTTPURL string `json:"git_http_url"` 491 | GitSSHURL string `json:"git_ssh_url"` 492 | VisibilityLevel int `json:"visibility_level"` 493 | } `json:"repository"` 494 | } 495 | 496 | type GitlabPipelineEvent struct { 497 | ObjectKind string `json:"object_kind"` 498 | ObjectAttributes struct { 499 | ID int `json:"id"` 500 | Ref string `json:"ref"` 501 | Tag bool `json:"tag"` 502 | Sha string `json:"sha"` 503 | BeforeSha string `json:"before_sha"` 504 | Source string `json:"source"` 505 | Status string `json:"status"` 506 | DetailedStatus string `json:"detailed_status"` 507 | Stages []string `json:"stages"` 508 | CreatedAt time.Time `json:"created_at"` 509 | FinishedAt time.Time `json:"finished_at"` 510 | Duration int `json:"duration"` 511 | Variables []interface{} `json:"variables"` 512 | } `json:"object_attributes"` 513 | MergeRequest interface{} `json:"merge_request"` 514 | User struct { 515 | ID int `json:"id"` 516 | Name string `json:"name"` 517 | Username string `json:"username"` 518 | AvatarURL string `json:"avatar_url"` 519 | Email string `json:"email"` 520 | } `json:"user"` 521 | Project struct { 522 | ID int `json:"id"` 523 | Name string `json:"name"` 524 | Description string `json:"description"` 525 | WebURL string `json:"web_url"` 526 | AvatarURL interface{} `json:"avatar_url"` 527 | GitSSHURL string `json:"git_ssh_url"` 528 | GitHTTPURL string `json:"git_http_url"` 529 | Namespace string `json:"namespace"` 530 | VisibilityLevel int `json:"visibility_level"` 531 | PathWithNamespace string `json:"path_with_namespace"` 532 | DefaultBranch string `json:"default_branch"` 533 | CiConfigPath string `json:"ci_config_path"` 534 | } `json:"project"` 535 | Commit struct { 536 | ID string `json:"id"` 537 | Message string `json:"message"` 538 | Title string `json:"title"` 539 | Timestamp time.Time `json:"timestamp"` 540 | URL string `json:"url"` 541 | Author struct { 542 | Name string `json:"name"` 543 | Email string `json:"email"` 544 | } `json:"author"` 545 | } `json:"commit"` 546 | Builds []struct { 547 | ID int `json:"id"` 548 | Stage string `json:"stage"` 549 | Name string `json:"name"` 550 | Status string `json:"status"` 551 | CreatedAt time.Time `json:"created_at"` 552 | StartedAt time.Time `json:"started_at"` 553 | FinishedAt time.Time `json:"finished_at"` 554 | When string `json:"when"` 555 | Manual bool `json:"manual"` 556 | AllowFailure bool `json:"allow_failure"` 557 | User struct { 558 | ID int `json:"id"` 559 | Name string `json:"name"` 560 | Username string `json:"username"` 561 | AvatarURL string `json:"avatar_url"` 562 | Email string `json:"email"` 563 | } `json:"user"` 564 | Runner interface{} `json:"runner"` 565 | ArtifactsFile struct { 566 | Filename interface{} `json:"filename"` 567 | Size interface{} `json:"size"` 568 | } `json:"artifacts_file"` 569 | } `json:"builds"` 570 | } 571 | --------------------------------------------------------------------------------