Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telegram webhook #4227

Merged
merged 41 commits into from
Apr 19, 2019
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
90c51a8
add telegram webhooks
techknowlogick Jun 11, 2018
f72631e
add telegram webhooks
techknowlogick Jun 11, 2018
9a0cc9c
set as HTML mode
techknowlogick Jun 11, 2018
e6ff368
make fmt
techknowlogick Jun 11, 2018
b140525
Merge branch 'master' into telegram_webhook
techknowlogick Jun 11, 2018
f4ef346
fix lint error
techknowlogick Jun 11, 2018
81a5a62
Merge branch 'master' into telegram_webhook
techknowlogick Jun 11, 2018
9fdbf51
Merge branch 'master' into telegram_webhook
techknowlogick Jun 25, 2018
d5c4ab5
Update tests for telegram
techknowlogick Jun 25, 2018
ff6dce9
Merge branch 'master' into telegram_webhook
techknowlogick Jul 9, 2018
6924128
Merge branch 'master' into telegram_webhook
techknowlogick Jul 13, 2018
2e6d5b5
Merge branch 'master' into telegram_webhook
techknowlogick Jul 17, 2018
b6c7ce3
Merge branch 'master' into telegram_webhook
techknowlogick Jul 21, 2018
42b058c
Merge branch 'master' into telegram_webhook
techknowlogick Aug 15, 2018
43e027f
Merge branch 'master' into telegram_webhook
lunny Oct 18, 2018
09f6dfa
Merge branch 'master' into telegram_webhook
techknowlogick Dec 12, 2018
92a6e55
split out chatid and bottoken
techknowlogick Dec 12, 2018
4516eb5
add missing marshal
techknowlogick Dec 12, 2018
9ade5d1
fix make lint
techknowlogick Dec 12, 2018
5a54346
Merge branch 'master' into telegram_webhook
techknowlogick Dec 12, 2018
a7627a8
escape HTML comments
techknowlogick Dec 12, 2018
b915900
Merge branch 'telegram_webhook' of github.com:techknowlogick/gitea in…
techknowlogick Dec 12, 2018
d571cf1
try to escape a different way
techknowlogick Dec 12, 2018
d89c372
Merge branch 'master' into telegram_webhook
techknowlogick Dec 13, 2018
8424d9d
Merge branch 'master' into telegram_webhook
techknowlogick Dec 16, 2018
5a00c15
Merge branch 'master' into telegram_webhook
techknowlogick Dec 17, 2018
9223209
fix text of webhook
techknowlogick Jan 16, 2019
78d18fa
Add translations
techknowlogick Jan 16, 2019
cfc8042
Merge branch 'master' into telegram_webhook
techknowlogick Jan 16, 2019
e3ead87
update year
techknowlogick Jan 23, 2019
8aa3b56
Merge branch 'master' into telegram_webhook
techknowlogick Jan 23, 2019
6bee16d
Merge branch 'master' into telegram_webhook
techknowlogick Feb 6, 2019
da18334
Merge branch 'master' into telegram_webhook
techknowlogick Mar 28, 2019
b16c6b2
Update webhook_telegram.go
techknowlogick Mar 28, 2019
8f382aa
Merge branch 'master' into telegram_webhook
techknowlogick Apr 15, 2019
985294f
resolve conflicts, sanitize webhook
techknowlogick Apr 15, 2019
28c496c
Merge branch 'master' into telegram_webhook
techknowlogick Apr 15, 2019
1d98271
Merge branch 'master' into telegram_webhook
techknowlogick Apr 16, 2019
6785c39
Merge branch 'master' into telegram_webhook
techknowlogick Apr 16, 2019
06d9f73
Merge branch 'master' into telegram_webhook
lunny Apr 18, 2019
14712ec
Merge branch 'master' into telegram_webhook
techknowlogick Apr 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions models/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ func (w *Webhook) GetDiscordHook() *DiscordMeta {
return s
}

// GetTelegramHook returns telegram metadata
func (w *Webhook) GetTelegramHook() *TelegramMeta {
s := &TelegramMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error(4, "webhook.GetTelegramHook(%d): %v", w.ID, err)
}
return s
}

// History returns history of webhook by given conditions.
func (w *Webhook) History(page int) ([]*HookTask, error) {
return HookTasks(w.ID, page)
Expand Down Expand Up @@ -381,6 +390,7 @@ const (
GITEA
DISCORD
DINGTALK
TELEGRAM
)

var hookTaskTypes = map[string]HookTaskType{
Expand All @@ -389,6 +399,7 @@ var hookTaskTypes = map[string]HookTaskType{
"slack": SLACK,
"discord": DISCORD,
"dingtalk": DINGTALK,
"telegram": TELEGRAM,
}

// ToHookTaskType returns HookTaskType by given name.
Expand All @@ -409,6 +420,8 @@ func (t HookTaskType) Name() string {
return "discord"
case DINGTALK:
return "dingtalk"
case TELEGRAM:
return "telegram"
}
return ""
}
Expand Down Expand Up @@ -581,6 +594,11 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType,
if err != nil {
return fmt.Errorf("GetDingtalkPayload: %v", err)
}
case TELEGRAM:
payloader, err = GetTelegramPayload(p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetTelegramPayload: %v", err)
}
default:
p.SetSecret(w.Secret)
payloader = p
Expand Down
321 changes: 321 additions & 0 deletions models/webhook_telegram.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
"encoding/json"
"fmt"
"strings"

"code.gitea.io/git"
api "code.gitea.io/sdk/gitea"
)

type (
// TelegramPayload represents
TelegramPayload struct {
Message string `json:"text"`
ParseMode string `json:"parse_mode"`
}

// TelegramMeta contains the telegram metadata
TelegramMeta struct {
BotToken string `json:"bot_token"`
ChatID string `json:"chat_id"`
}
)

// SetSecret sets the telegram secret
func (p *TelegramPayload) SetSecret(_ string) {}

// JSONPayload Marshals the TelegramPayload to json
func (p *TelegramPayload) JSONPayload() ([]byte, error) {
p.ParseMode = "HTML"
p.Message = strings.Replace(p.Message, "<!--", "\\<!--", -1)
data, err := json.MarshalIndent(p, "", " ")
if err != nil {
return []byte{}, err
}
return data, nil
}

func getTelegramCreatePayload(p *api.CreatePayload) (*TelegramPayload, error) {
// created tag/branch
refName := git.RefEndName(p.Ref)
title := fmt.Sprintf(`[<a href="%s">%s</a>] %s <a href="%s">%s</a> created`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
p.Repo.HTMLURL+"/src/"+refName, refName)

return &TelegramPayload{
Message: title,
}, nil
}

func getTelegramDeletePayload(p *api.DeletePayload) (*TelegramPayload, error) {
// created tag/branch
refName := git.RefEndName(p.Ref)
title := fmt.Sprintf(`[<a href="%s">%s</a>] %s <a href="%s">%s</a> deleted`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
p.Repo.HTMLURL+"/src/"+refName, refName)

return &TelegramPayload{
Message: title,
}, nil
}

func getTelegramForkPayload(p *api.ForkPayload) (*TelegramPayload, error) {
title := fmt.Sprintf(`%s is forked to <a href="%s">%s</a>`, p.Forkee.FullName, p.Repo.HTMLURL, p.Repo.FullName)

return &TelegramPayload{
Message: title,
}, nil
}

func getTelegramPushPayload(p *api.PushPayload) (*TelegramPayload, error) {
var (
branchName = git.RefEndName(p.Ref)
commitDesc string
)

var titleLink string
if len(p.Commits) == 1 {
commitDesc = "1 new commit"
titleLink = p.Commits[0].URL
} else {
commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
titleLink = p.CompareURL
}
if titleLink == "" {
titleLink = p.Repo.HTMLURL + "/src/" + branchName
}
title := fmt.Sprintf(`[<a href="%s">%s</a>:<a href="%s">%s</a>] %s`, p.Repo.HTMLURL, p.Repo.FullName, titleLink, branchName, commitDesc)

var text string
// for each commit, generate attachment text
for i, commit := range p.Commits {
var authorName string
if commit.Author != nil {
authorName = " - " + commit.Author.Name
}
text += fmt.Sprintf(`[<a href="%s">%s</a>] %s`, commit.URL, commit.ID[:7],
strings.TrimRight(commit.Message, "\r\n")) + authorName
// add linebreak to each commit but the last
if i < len(p.Commits)-1 {
text += "\n"
}
}

return &TelegramPayload{
Message: title + "\n" + text,
}, nil
}

func getTelegramIssuesPayload(p *api.IssuePayload) (*TelegramPayload, error) {
var text, title string
switch p.Action {
case api.HookIssueOpened:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueClosed:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue closed: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueReOpened:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue re-opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueEdited:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue edited: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueAssigned:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue assigned to %s: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.Assignee.UserName, p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueUnassigned:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue unassigned: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueLabelUpdated:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue labels updated: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueLabelCleared:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue labels cleared: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueSynchronized:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue synchronized: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueMilestoned:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
case api.HookIssueDemilestoned:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue clear milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.Issue.URL, p.Index, p.Issue.Title)
text = p.Issue.Body
}

return &TelegramPayload{
Message: title + "\n\n" + text,
}, nil
}

func getTelegramIssueCommentPayload(p *api.IssueCommentPayload) (*TelegramPayload, error) {
url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
title := fmt.Sprintf(`<a href="%s">#%d %s</a>`, url, p.Issue.Index, p.Issue.Title)
var text string
switch p.Action {
case api.HookIssueCommentCreated:
text = "New comment: " + title
text += p.Comment.Body
case api.HookIssueCommentEdited:
text = "Comment edited: " + title
text += p.Comment.Body
case api.HookIssueCommentDeleted:
text = "Comment deleted: " + title
text += p.Comment.Body
}

return &TelegramPayload{
Message: title + "\n" + text,
}, nil
}

func getTelegramPullRequestPayload(p *api.PullRequestPayload) (*TelegramPayload, error) {
var text, title string
switch p.Action {
case api.HookIssueOpened:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueClosed:
if p.PullRequest.HasMerged {
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request merged: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
} else {
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request closed: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
}
text = p.PullRequest.Body
case api.HookIssueReOpened:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request re-opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueEdited:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request edited: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueAssigned:
list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID})
if err != nil {
return &TelegramPayload{}, err
}
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request assigned to %s: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
list, p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueUnassigned:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request unassigned: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueLabelUpdated:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request labels updated: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueLabelCleared:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request labels cleared: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueSynchronized:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request synchronized: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueMilestoned:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
case api.HookIssueDemilestoned:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request clear milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
text = p.PullRequest.Body
}

return &TelegramPayload{
Message: title + "\n" + text,
}, nil
}

func getTelegramRepositoryPayload(p *api.RepositoryPayload) (*TelegramPayload, error) {
var title string
switch p.Action {
case api.HookRepoCreated:
title = fmt.Sprintf(`[<a href="%s">%s</a>] Repository created`, p.Repository.HTMLURL, p.Repository.FullName)
return &TelegramPayload{
Message: title,
}, nil
case api.HookRepoDeleted:
title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
return &TelegramPayload{
Message: title,
}, nil
}
return nil, nil
}

func getTelegramReleasePayload(p *api.ReleasePayload) (*TelegramPayload, error) {
var title, url string
switch p.Action {
case api.HookReleasePublished:
title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
url = p.Release.URL
return &TelegramPayload{
Message: title + "\n" + url,
}, nil
case api.HookReleaseUpdated:
title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
url = p.Release.URL
return &TelegramPayload{
Message: title + "\n" + url,
}, nil

case api.HookReleaseDeleted:
title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
url = p.Release.URL
return &TelegramPayload{
Message: title + "\n" + url,
}, nil
}

return nil, nil
}

// GetTelegramPayload converts a telegram webhook into a TelegramPayload
func GetTelegramPayload(p api.Payloader, event HookEventType, meta string) (*TelegramPayload, error) {
s := new(TelegramPayload)

switch event {
case HookEventCreate:
return getTelegramCreatePayload(p.(*api.CreatePayload))
case HookEventDelete:
return getTelegramDeletePayload(p.(*api.DeletePayload))
case HookEventFork:
return getTelegramForkPayload(p.(*api.ForkPayload))
case HookEventIssues:
return getTelegramIssuesPayload(p.(*api.IssuePayload))
case HookEventIssueComment:
return getTelegramIssueCommentPayload(p.(*api.IssueCommentPayload))
case HookEventPush:
return getTelegramPushPayload(p.(*api.PushPayload))
case HookEventPullRequest:
return getTelegramPullRequestPayload(p.(*api.PullRequestPayload))
case HookEventRepository:
return getTelegramRepositoryPayload(p.(*api.RepositoryPayload))
case HookEventRelease:
return getTelegramReleasePayload(p.(*api.ReleasePayload))
}

return s, nil
}
3 changes: 3 additions & 0 deletions models/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,18 +197,21 @@ func TestToHookTaskType(t *testing.T) {
assert.Equal(t, GOGS, ToHookTaskType("gogs"))
assert.Equal(t, SLACK, ToHookTaskType("slack"))
assert.Equal(t, GITEA, ToHookTaskType("gitea"))
assert.Equal(t, TELEGRAM, ToHookTaskType("telegram"))
}

func TestHookTaskType_Name(t *testing.T) {
assert.Equal(t, "gogs", GOGS.Name())
assert.Equal(t, "slack", SLACK.Name())
assert.Equal(t, "gitea", GITEA.Name())
assert.Equal(t, "telegram", TELEGRAM.Name())
}

func TestIsValidHookTaskType(t *testing.T) {
assert.True(t, IsValidHookTaskType("gogs"))
assert.True(t, IsValidHookTaskType("slack"))
assert.True(t, IsValidHookTaskType("gitea"))
assert.True(t, IsValidHookTaskType("telegram"))
assert.False(t, IsValidHookTaskType("invalid"))
}

Expand Down
Loading