diff --git a/models/webhook.go b/models/webhook.go
index a764455f5f39a..957de248fec29 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -381,6 +381,7 @@ const (
GITEA
DISCORD
DINGTALK
+ TYPETALK
)
var hookTaskTypes = map[string]HookTaskType{
@@ -389,6 +390,7 @@ var hookTaskTypes = map[string]HookTaskType{
"slack": SLACK,
"discord": DISCORD,
"dingtalk": DINGTALK,
+ "typetalk": TYPETALK,
}
// ToHookTaskType returns HookTaskType by given name.
@@ -409,6 +411,8 @@ func (t HookTaskType) Name() string {
return "discord"
case DINGTALK:
return "dingtalk"
+ case TYPETALK:
+ return "typetalk"
}
return ""
}
@@ -581,6 +585,11 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType,
if err != nil {
return fmt.Errorf("GetDingtalkPayload: %v", err)
}
+ case TYPETALK:
+ payloader, err = GetTypetalkPayload(p, event, w.Meta)
+ if err != nil {
+ return fmt.Errorf("GetTypetalkPayload: %v", err)
+ }
default:
p.SetSecret(w.Secret)
payloader = p
diff --git a/models/webhook_typetalk.go b/models/webhook_typetalk.go
new file mode 100644
index 0000000000000..276585c3bd197
--- /dev/null
+++ b/models/webhook_typetalk.go
@@ -0,0 +1,302 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// 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"
+ "unicode/utf8"
+
+ "code.gitea.io/git"
+ api "code.gitea.io/sdk/gitea"
+)
+
+// TypetalkPayload contains information for posting messages on Typetalk
+type TypetalkPayload struct {
+ Message string `json:"message"`
+}
+
+// SetSecret sets the Typetalk secret
+func (p *TypetalkPayload) SetSecret(_ string) {}
+
+// JSONPayload Marshals TypetalkPayload to json
+func (p *TypetalkPayload) JSONPayload() ([]byte, error) {
+ data, err := json.MarshalIndent(p, "", " ")
+ if err != nil {
+ return []byte{}, err
+ }
+ return data, nil
+}
+
+func formatRepositoryLinkForTypetalk(name, url string) string {
+ return fmt.Sprintf("[%s](%s)", name, url)
+}
+
+func formatIssueLinkForTypetalk(issueTitle, repositoryURL string, issueNumber int64) string {
+ return fmt.Sprintf("[#%d %s](%s/issues/%d)", issueNumber, issueTitle, repositoryURL, issueNumber)
+}
+
+func formatIssueCommentLinkForTypetalk(repositoryURL string, issueID, commentID int64) string {
+ return fmt.Sprintf("[#%d](%s/issues/%d#%s)", commentID, repositoryURL, issueID, CommentHashTag(commentID))
+}
+
+func formatPullRequestLinkForTypetalk(pullRequestTitle, pullRequestyURL string, pullRequestID int64) string {
+ return fmt.Sprintf("[#%d %s](%s)", pullRequestID, pullRequestTitle, pullRequestyURL)
+}
+
+func formatTagLinkForTypetalk(name, url, tag string) string {
+ return fmt.Sprintf("[%s:%s](%s/src/tag/%s)", name, tag, url, tag)
+}
+
+func getTypetalkCreatePayload(p *api.CreatePayload) (*TypetalkPayload, error) {
+ repoLink := formatRepositoryLinkForTypetalk(p.Repo.FullName, p.Repo.HTMLURL)
+ refName := git.RefEndName(p.Ref)
+ message := fmt.Sprintf("[%s] %s %s created", repoLink, p.RefType, refName)
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkDeletePayload(p *api.DeletePayload) (*TypetalkPayload, error) {
+ repoLink := formatRepositoryLinkForTypetalk(p.Repo.FullName, p.Repo.HTMLURL)
+ refName := git.RefEndName(p.Ref)
+ message := fmt.Sprintf("[%s] %s %s deleted", repoLink, p.RefType, refName)
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkForkPayload(p *api.ForkPayload) (*TypetalkPayload, error) {
+ origin := fmt.Sprintf("[%s](%s)", p.Forkee.FullName, p.Forkee.HTMLURL)
+ forked := fmt.Sprintf("[%s](%s)", p.Repo.FullName, p.Repo.HTMLURL)
+ message := fmt.Sprintf("%s is forked to %s", origin, forked)
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkIssuesPayload(p *api.IssuePayload) (*TypetalkPayload, error) {
+
+ repoLink := formatRepositoryLinkForTypetalk(p.Repository.FullName, p.Repository.HTMLURL)
+ issueLink := formatIssueLinkForTypetalk(p.Issue.Title, p.Repository.HTMLURL, p.Index)
+
+ var title, text string
+ switch p.Action {
+ case api.HookIssueOpened:
+ title = fmt.Sprintf("[%s] %s Issue opened by %s", repoLink, issueLink, p.Sender.UserName)
+ text = p.Issue.Body
+ case api.HookIssueClosed:
+ title = fmt.Sprintf("[%s] %s Issue closed by %s", repoLink, issueLink, p.Sender.UserName)
+ case api.HookIssueReOpened:
+ title = fmt.Sprintf("[%s] %s Issue re-opened by %s", repoLink, issueLink, p.Sender.UserName)
+ case api.HookIssueEdited:
+ title = fmt.Sprintf("[%s] %s Issue edited by %s", repoLink, issueLink, p.Sender.UserName)
+ text = p.Issue.Body
+ case api.HookIssueAssigned:
+ title = fmt.Sprintf("[%s] %s Issue assigned to %s", repoLink, issueLink, p.Issue.Assignee.UserName)
+ case api.HookIssueUnassigned:
+ title = fmt.Sprintf("[%s] %s Issue unassigned by %s", repoLink, issueLink, p.Sender.UserName)
+ case api.HookIssueLabelUpdated:
+ title = fmt.Sprintf("[%s] %s Issue labels updated by %s", repoLink, issueLink, p.Sender.UserName)
+ case api.HookIssueLabelCleared:
+ title = fmt.Sprintf("[%s] %s Issue labels cleared by %s", repoLink, issueLink, p.Sender.UserName)
+ case api.HookIssueSynchronized:
+ title = fmt.Sprintf("[%s] %s Issue synchronized by %s", repoLink, issueLink, p.Sender.UserName)
+ case api.HookIssueMilestoned:
+ title = fmt.Sprintf("[%s] %s Issue milestones updated by %s", repoLink, issueLink, p.Sender.UserName)
+ case api.HookIssueDemilestoned:
+ title = fmt.Sprintf("[%s] %s Issue milestone cleared by %s", repoLink, issueLink, p.Sender.UserName)
+ }
+ message := fmt.Sprintf("%s\n%s", title, text)
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkIssueCommentPayload(p *api.IssueCommentPayload) (*TypetalkPayload, error) {
+
+ repoLink := formatRepositoryLinkForTypetalk(p.Repository.FullName, p.Repository.HTMLURL)
+ issueLink := formatIssueLinkForTypetalk(p.Issue.Title, p.Repository.HTMLURL, p.Issue.Index)
+ issueCommentLink := formatIssueCommentLinkForTypetalk(p.Repository.HTMLURL, p.Issue.Index, p.Comment.ID)
+
+ var title, text string
+ switch p.Action {
+ case api.HookIssueCommentCreated:
+ title = fmt.Sprintf("[%s] %s New comment %s created by %s ", repoLink, issueLink, issueCommentLink, p.Sender.UserName)
+ text = p.Comment.Body
+ case api.HookIssueCommentEdited:
+ title = fmt.Sprintf("[%s] %s Comment %s edited by %s", repoLink, issueLink, issueCommentLink, p.Sender.UserName)
+ text = p.Comment.Body
+ case api.HookIssueCommentDeleted:
+ title = fmt.Sprintf("[%s] %s Comment #%d deleted by %s", repoLink, issueLink, p.Comment.ID, p.Sender.UserName)
+ }
+
+ message := fmt.Sprintf("%s\n%s", title, text)
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkPushPayload(p *api.PushPayload) (*TypetalkPayload, error) {
+
+ branchName := git.RefEndName(p.Ref)
+
+ var titleLink, commitDesc 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("[[%s:%s] %s](%s)", p.Repo.FullName, branchName, commitDesc, titleLink)
+
+ var text string
+ for i, commit := range p.Commits {
+ var authorName string
+ if commit.Author != nil {
+ authorName = " - " + commit.Author.Name
+ }
+ t := fmt.Sprintf("[%s](%s) %s", commit.ID[:7], commit.URL,
+ strings.TrimRight(commit.Message, "\r\n")) + authorName
+
+ // Typetalk accepts message shorter than 4000 characters.
+ if utf8.RuneCountInString(text)+utf8.RuneCountInString(t) < 4000 {
+ text += t
+ // Add linebreak to each commit but the last
+ if i < len(p.Commits)-1 {
+ text += "\n"
+ }
+ } else {
+ text += "..."
+ break
+ }
+ }
+
+ message := fmt.Sprintf("%s\n%s", title, text)
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkPullRequestPayload(p *api.PullRequestPayload) (*TypetalkPayload, error) {
+
+ repoLink := formatRepositoryLinkForTypetalk(p.Repository.FullName, p.Repository.HTMLURL)
+ pullRequestLink := formatPullRequestLinkForTypetalk(p.PullRequest.Title, p.PullRequest.HTMLURL, p.Index)
+
+ var text, title string
+ switch p.Action {
+ case api.HookIssueOpened:
+ title = fmt.Sprintf("[%s] %s Pull request opened by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ text = p.PullRequest.Body
+ case api.HookIssueClosed:
+ if p.PullRequest.HasMerged {
+ title = fmt.Sprintf("[%s] %s Pull request merged by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ } else {
+ title = fmt.Sprintf("[%s] %s Pull request closed by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ }
+ case api.HookIssueReOpened:
+ title = fmt.Sprintf("[%s] %s Pull request re-opened by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ case api.HookIssueEdited:
+ title = fmt.Sprintf("[%s] %s Pull request edited by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ text = p.PullRequest.Body
+ case api.HookIssueAssigned:
+ list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID})
+ if err != nil {
+ return &TypetalkPayload{}, err
+ }
+ title = fmt.Sprintf("[%s] %s Pull request assigned to %s", repoLink, pullRequestLink, list)
+ case api.HookIssueUnassigned:
+ title = fmt.Sprintf("[%s] %s Pull request unassigned by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ case api.HookIssueLabelUpdated:
+ title = fmt.Sprintf("[%s] %s Pull request labels updated by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ case api.HookIssueLabelCleared:
+ title = fmt.Sprintf("[%s] %s Pull request labels cleared by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ case api.HookIssueSynchronized:
+ title = fmt.Sprintf("[%s] %s Pull request synchronized by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ case api.HookIssueMilestoned:
+ title = fmt.Sprintf("[%s] %s Pull request milestones updated by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ case api.HookIssueDemilestoned:
+ title = fmt.Sprintf("[%s] %s Pull request milestones cleared by %s", repoLink, pullRequestLink, p.Sender.UserName)
+ }
+
+ message := fmt.Sprintf("%s\n%s", title, text)
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkRepositoryPayload(p *api.RepositoryPayload) (*TypetalkPayload, error) {
+
+ var message string
+ switch p.Action {
+ case api.HookRepoCreated:
+ message = fmt.Sprintf("[%s] Repository created", formatRepositoryLinkForTypetalk(p.Repository.FullName, p.Repository.HTMLURL))
+ case api.HookRepoDeleted:
+ message = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
+ }
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+func getTypetalkReleasePayload(p *api.ReleasePayload) (*TypetalkPayload, error) {
+
+ tagLink := formatTagLinkForTypetalk(p.Repository.FullName, p.Repository.HTMLURL, p.Release.TagName)
+
+ var message string
+ switch p.Action {
+ case api.HookReleasePublished:
+ message = fmt.Sprintf("[%s] Release created", tagLink)
+ case api.HookReleaseUpdated:
+ message = fmt.Sprintf("[%s] Release updated", tagLink)
+ case api.HookReleaseDeleted:
+ message = fmt.Sprintf("[%s:%s] Release deleted", p.Repository.FullName, p.Release.TagName)
+ }
+
+ return &TypetalkPayload{
+ Message: message,
+ }, nil
+}
+
+// GetTypetalkPayload converts a Typetalk webhook into a TypetalkPayload
+func GetTypetalkPayload(p api.Payloader, event HookEventType, meta string) (*TypetalkPayload, error) {
+ s := new(TypetalkPayload)
+
+ switch event {
+ case HookEventCreate:
+ return getTypetalkCreatePayload(p.(*api.CreatePayload))
+ case HookEventDelete:
+ return getTypetalkDeletePayload(p.(*api.DeletePayload))
+ case HookEventFork:
+ return getTypetalkForkPayload(p.(*api.ForkPayload))
+ case HookEventIssues:
+ return getTypetalkIssuesPayload(p.(*api.IssuePayload))
+ case HookEventIssueComment:
+ return getTypetalkIssueCommentPayload(p.(*api.IssueCommentPayload))
+ case HookEventPush:
+ return getTypetalkPushPayload(p.(*api.PushPayload))
+ case HookEventPullRequest:
+ return getTypetalkPullRequestPayload(p.(*api.PullRequestPayload))
+ case HookEventRepository:
+ return getTypetalkRepositoryPayload(p.(*api.RepositoryPayload))
+ case HookEventRelease:
+ return getTypetalkReleasePayload(p.(*api.ReleasePayload))
+ }
+
+ return s, nil
+}
diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go
index a600ecc8fe831..e676f874fcab5 100644
--- a/modules/auth/repo_form.go
+++ b/modules/auth/repo_form.go
@@ -260,6 +260,17 @@ func (f *NewDingtalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors
return validate(errs, ctx.Data, f, ctx.Locale)
}
+// NewTypetalkHookForm form for creating typetalk hook
+type NewTypetalkHookForm struct {
+ PayloadURL string `binding:"Required;ValidUrl"`
+ WebhookForm
+}
+
+// Validate validates the fields
+func (f *NewTypetalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
+ return validate(errs, ctx.Data, f, ctx.Locale)
+}
+
// .___
// | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 2560c091073ee..9a24f9c298bc6 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -1614,7 +1614,7 @@ func newWebhookService() {
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
- Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk"}
+ Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "typetalk"}
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index fd51ca678e192..51c6a73b41ebd 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1127,6 +1127,7 @@ settings.slack_domain = Domain
settings.slack_channel = Channel
settings.add_discord_hook_desc = Integrate Discord into your repository.
settings.add_dingtalk_hook_desc = Integrate Dingtalk into your repository.
+settings.add_typetalk_hook_desc = Integrate Typetalk into your repository.
settings.deploy_keys = Deploy Keys
settings.add_deploy_key = Add Deploy Key
settings.deploy_key_desc = Deploy keys have read-only pull access to the repository.
diff --git a/public/img/typetalk.ico b/public/img/typetalk.ico
new file mode 100644
index 0000000000000..b9d9624ad2ce5
Binary files /dev/null and b/public/img/typetalk.ico differ
diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go
index 6c69354c700eb..a7aa9de0640cb 100644
--- a/routers/repo/webhook.go
+++ b/routers/repo/webhook.go
@@ -314,6 +314,46 @@ func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) {
ctx.Redirect(orCtx.Link + "/settings/hooks")
}
+// TypetalkHooksNewPost handles request for creating typetalk hook
+func TypetalkHooksNewPost(ctx *context.Context, form auth.NewTypetalkHookForm) {
+ ctx.Data["Title"] = ctx.Tr("repo.settings")
+ ctx.Data["PageIsSettingsHooks"] = true
+ ctx.Data["PageIsSettingsHooksNew"] = true
+ ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
+
+ orCtx, err := getOrgRepoCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getOrgRepoCtx", err)
+ return
+ }
+
+ if ctx.HasError() {
+ ctx.HTML(200, orCtx.NewTemplate)
+ return
+ }
+
+ w := &models.Webhook{
+ RepoID: orCtx.RepoID,
+ URL: form.PayloadURL,
+ ContentType: models.ContentTypeJSON,
+ HookEvent: ParseHookEvent(form.WebhookForm),
+ IsActive: form.Active,
+ HookTaskType: models.TYPETALK,
+ Meta: "",
+ OrgID: orCtx.OrgID,
+ }
+ if err := w.UpdateEvent(); err != nil {
+ ctx.ServerError("UpdateEvent", err)
+ return
+ } else if err := models.CreateWebhook(w); err != nil {
+ ctx.ServerError("CreateWebhook", err)
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
+ ctx.Redirect(orCtx.Link + "/settings/hooks")
+}
+
// SlackHooksNewPost response for creating slack hook
func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
ctx.Data["Title"] = ctx.Tr("repo.settings")
@@ -628,6 +668,38 @@ func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm)
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
}
+// TypetalkHooksEditPost handles request for editing webhook settings for Typetalk.
+func TypetalkHooksEditPost(ctx *context.Context, form auth.NewTypetalkHookForm) {
+ ctx.Data["Title"] = ctx.Tr("repo.settings")
+ ctx.Data["PageIsSettingsHooks"] = true
+ ctx.Data["PageIsSettingsHooksEdit"] = true
+
+ orCtx, w := checkWebhook(ctx)
+ if ctx.Written() {
+ return
+ }
+ ctx.Data["Webhook"] = w
+
+ if ctx.HasError() {
+ ctx.HTML(200, orCtx.NewTemplate)
+ return
+ }
+
+ w.URL = form.PayloadURL
+ w.HookEvent = ParseHookEvent(form.WebhookForm)
+ w.IsActive = form.Active
+ if err := w.UpdateEvent(); err != nil {
+ ctx.ServerError("UpdateEvent", err)
+ return
+ } else if err := models.UpdateWebhook(w); err != nil {
+ ctx.ServerError("UpdateWebhook", err)
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
+ ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
+}
+
// TestWebhook test if web hook is work fine
func TestWebhook(ctx *context.Context) {
hookID := ctx.ParamsInt64(":id")
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index 6011427321aed..78580f8849651 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -451,12 +451,14 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
+ m.Post("/typetalk/new", bindIgnErr(auth.NewTypetalkHookForm{}), repo.TypetalkHooksNewPost)
m.Get("/:id", repo.WebHooksEdit)
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
+ m.Post("/typetalk/:id", bindIgnErr(auth.NewTypetalkHookForm{}), repo.TypetalkHooksEditPost)
})
m.Route("/delete", "GET,POST", org.SettingsDelete)
@@ -504,6 +506,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
+ m.Post("/typetalk/new", bindIgnErr(auth.NewTypetalkHookForm{}), repo.TypetalkHooksNewPost)
m.Get("/:id", repo.WebHooksEdit)
m.Post("/:id/test", repo.TestWebhook)
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
@@ -511,6 +514,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
+ m.Post("/typetalk/:id", bindIgnErr(auth.NewTypetalkHookForm{}), repo.TypetalkHooksEditPost)
m.Group("/git", func() {
m.Get("", repo.GitHooks)
diff --git a/templates/org/settings/hook_new.tmpl b/templates/org/settings/hook_new.tmpl
index 809009b66b7b4..368a3393f79d2 100644
--- a/templates/org/settings/hook_new.tmpl
+++ b/templates/org/settings/hook_new.tmpl
@@ -19,6 +19,8 @@
{{else if eq .HookType "dingtalk"}}
+ {{else if eq .HookType "typetalk"}}
+
{{end}}
@@ -28,6 +30,7 @@
{{template "repo/settings/webhook/slack" .}}
{{template "repo/settings/webhook/discord" .}}
{{template "repo/settings/webhook/dingtalk" .}}
+ {{template "repo/settings/webhook/typetalk" .}}
{{template "repo/settings/webhook/history" .}}
diff --git a/templates/repo/settings/webhook/list.tmpl b/templates/repo/settings/webhook/list.tmpl
index d98976cf5b400..660f573ae51fd 100644
--- a/templates/repo/settings/webhook/list.tmpl
+++ b/templates/repo/settings/webhook/list.tmpl
@@ -20,6 +20,9 @@
Dingtalk
+
+
Typetalk
+
diff --git a/templates/repo/settings/webhook/new.tmpl b/templates/repo/settings/webhook/new.tmpl
index 1b3d114577a4e..f03393c6751f8 100644
--- a/templates/repo/settings/webhook/new.tmpl
+++ b/templates/repo/settings/webhook/new.tmpl
@@ -17,6 +17,8 @@
{{else if eq .HookType "dingtalk"}}
+ {{else if eq .HookType "typetalk"}}
+
{{end}}
@@ -26,6 +28,7 @@
{{template "repo/settings/webhook/slack" .}}
{{template "repo/settings/webhook/discord" .}}
{{template "repo/settings/webhook/dingtalk" .}}
+ {{template "repo/settings/webhook/typetalk" .}}
{{template "repo/settings/webhook/history" .}}
diff --git a/templates/repo/settings/webhook/typetalk.tmpl b/templates/repo/settings/webhook/typetalk.tmpl
new file mode 100644
index 0000000000000..c690e73e5bc9d
--- /dev/null
+++ b/templates/repo/settings/webhook/typetalk.tmpl
@@ -0,0 +1,11 @@
+{{if eq .HookType "typetalk"}}
+
{{.i18n.Tr "repo.settings.add_typetalk_hook_desc" "https://www.typetalk.com" | Str2html}}
+ +{{end}}