mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-27 12:53:57 +01:00
Add support for corporate WeChat webhooks (#15910)
* 企业微信webhook * 企业微信webhook * 企业微信webhook * Update templates/admin/hook_new.tmpl Co-authored-by: a1012112796 <1012112796@qq.com> * Update services/webhook/wechatwork.go Co-authored-by: a1012112796 <1012112796@qq.com> * 修善wechatwork * 修善wechatwork * fix * Update locale_cs-CZ.ini fix * fix build * fix * fix build * make webhooks.zh-cn.md * delet unnecessary blank line * delet unnecessary blank line * 企业微信webhook * 企业微信webhook * 企业微信webhook * Update templates/admin/hook_new.tmpl Co-authored-by: a1012112796 <1012112796@qq.com> * Update services/webhook/wechatwork.go Co-authored-by: a1012112796 <1012112796@qq.com> * 修善wechatwork * 修善wechatwork * fix * fix build * fix * fix build * make webhooks.zh-cn.md * delet unnecessary blank line * delet unnecessary blank line * 企业微信webhook * 企业微信webhook * 企业微信webhook * 企业微信webhook * 企业微信webhook * fix * fix * 企业微信webhook * 企业微信webhook * 企业微信webhook * fix wechat * fix wechat * fix wechat * fix wechat * Fix invalid params and typo of email templates (#16394) Signed-off-by: Meano <meanocat@gmail.com> * Add LRU mem cache implementation (#16226) The current default memory cache implementation is unbounded in size and number of objects cached. This is hardly ideal. This PR proposes creating a TwoQueue LRU cache as the underlying cache for Gitea. The cache is limited by the number of objects stored in the cache (rather than size) for simplicity. The default number of objects is 50000 - which is perhaps too small as most of our objects cached are going to be much less than 1kB. It may be worth considering using a different LRU implementation that actively limits sizes or avoids GC - however, this is just a beginning implementation. Signed-off-by: Andrew Thornton <art27@cantab.net> * [skip ci] Updated translations via Crowdin * Replace `plugins/docker` with `techknowlogick/drone-docker`in ci (#16407) * plugins/docker -> techknowlogick/drone-docker * It is multi-arch * docs: rewrite email setup (#16404) * Add intro for both the docs page and mailer methods * Fix numbering level in SMTP section * Recommends implicit TLS Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com> * Validate Issue Index before querying DB (#16406) * Fix external renderer (#16401) * fix external renderer * use GBackground context as fallback * no fallback, return error Co-authored-by: Lauris BH <lauris@nix.lv> * Add checkbox to delete pull branch after successful merge (#16049) * Add checkbox to delete pull branch after successful merge * Omit DeleteBranchAfterMerge field in json * Log a warning instead of error when PR head branch deleted * Add DefaultDeleteBranchAfterMerge to PullRequestConfig * Add support for delete_branch_after_merge via API * Fix for API: the branch should be deleted from the HEAD repo If head and base repo are the same, reuse the already opened ctx.Repo.GitRepo * Don't delegate to CleanupBranch, only reuse branch deletion code CleanupBranch contains too much logic that has already been performed by the Merge * Reuse gitrepo in MergePullRequest Co-authored-by: Andrew Thornton <art27@cantab.net> * [skip ci] Updated translations via Crowdin * Detect encoding changes while parsing diff (#16330) * Detect encoding changes while parsing diff * Let branch/tag name be a valid ref to get CI status (#16400) * fix #16384# * refactor: move shared helper func to utils package * extend Tests * use ctx.Repo.GitRepo if not nil * fix * fix * 企业微信webhook * 企业微信webhook * 企业微信webhook * fix build * fix build * Apply suggestions from code review Co-authored-by: a1012112796 <1012112796@qq.com> Co-authored-by: myheavily <myheavily> Co-authored-by: zhaoxin <gitea@fake.local> Co-authored-by: Meano <Meano@foxmail.com> Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: GiteaBot <teabot@gitea.io> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Bagas Sanjaya <bagasdotme@gmail.com> Co-authored-by: Norwin <noerw@users.noreply.github.com> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: Jimmy Praet <jimmy.praet@telenet.be> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
afb040e021
commit
078e2b2c39
24 changed files with 364 additions and 30 deletions
|
@ -27,6 +27,7 @@ All event pushes are POST requests. The methods currently supported are:
|
||||||
- Telegram
|
- Telegram
|
||||||
- Microsoft Teams
|
- Microsoft Teams
|
||||||
- Feishu
|
- Feishu
|
||||||
|
- Wechatwork
|
||||||
|
|
||||||
### Event information
|
### Event information
|
||||||
|
|
||||||
|
|
|
@ -15,4 +15,17 @@ menu:
|
||||||
|
|
||||||
# Webhooks
|
# Webhooks
|
||||||
|
|
||||||
|
Gitea 的存储 webhook。这可以有存储库管路设定页 `/:username/:reponame/settings/hooks` 中的。Webhook 也可以按照组织调整或全系统调整,所有时间的推送都是POST请求
|
||||||
|
。此方法目前被下列服务支援:
|
||||||
|
|
||||||
|
- Gitea (也可以是 GET 請求)
|
||||||
|
- Gogs
|
||||||
|
- Slack
|
||||||
|
- Discord
|
||||||
|
- Dingtalk
|
||||||
|
- Telegram
|
||||||
|
- Microsoft Teams
|
||||||
|
- Feishu
|
||||||
|
- Wechatwork
|
||||||
|
|
||||||
## TBD
|
## TBD
|
||||||
|
|
|
@ -26,6 +26,7 @@ Gitea 的儲存庫事件支援 web hook。這可以有儲存庫管理員在設
|
||||||
- Telegram
|
- Telegram
|
||||||
- Microsoft Teams
|
- Microsoft Teams
|
||||||
- Feishu
|
- Feishu
|
||||||
|
- Wechatwork
|
||||||
|
|
||||||
### 事件資訊
|
### 事件資訊
|
||||||
|
|
||||||
|
|
|
@ -19,18 +19,20 @@ func convertTaskTypeToString(x *xorm.Engine) error {
|
||||||
MSTEAMS
|
MSTEAMS
|
||||||
FEISHU
|
FEISHU
|
||||||
MATRIX
|
MATRIX
|
||||||
|
WECHATWORK
|
||||||
)
|
)
|
||||||
|
|
||||||
hookTaskTypes := map[int]string{
|
hookTaskTypes := map[int]string{
|
||||||
GITEA: "gitea",
|
GITEA: "gitea",
|
||||||
GOGS: "gogs",
|
GOGS: "gogs",
|
||||||
SLACK: "slack",
|
SLACK: "slack",
|
||||||
DISCORD: "discord",
|
DISCORD: "discord",
|
||||||
DINGTALK: "dingtalk",
|
DINGTALK: "dingtalk",
|
||||||
TELEGRAM: "telegram",
|
TELEGRAM: "telegram",
|
||||||
MSTEAMS: "msteams",
|
MSTEAMS: "msteams",
|
||||||
FEISHU: "feishu",
|
FEISHU: "feishu",
|
||||||
MATRIX: "matrix",
|
MATRIX: "matrix",
|
||||||
|
WECHATWORK: "wechatwork",
|
||||||
}
|
}
|
||||||
|
|
||||||
type HookTask struct {
|
type HookTask struct {
|
||||||
|
|
|
@ -19,18 +19,20 @@ func convertWebhookTaskTypeToString(x *xorm.Engine) error {
|
||||||
MSTEAMS
|
MSTEAMS
|
||||||
FEISHU
|
FEISHU
|
||||||
MATRIX
|
MATRIX
|
||||||
|
WECHATWORK
|
||||||
)
|
)
|
||||||
|
|
||||||
hookTaskTypes := map[int]string{
|
hookTaskTypes := map[int]string{
|
||||||
GITEA: "gitea",
|
GITEA: "gitea",
|
||||||
GOGS: "gogs",
|
GOGS: "gogs",
|
||||||
SLACK: "slack",
|
SLACK: "slack",
|
||||||
DISCORD: "discord",
|
DISCORD: "discord",
|
||||||
DINGTALK: "dingtalk",
|
DINGTALK: "dingtalk",
|
||||||
TELEGRAM: "telegram",
|
TELEGRAM: "telegram",
|
||||||
MSTEAMS: "msteams",
|
MSTEAMS: "msteams",
|
||||||
FEISHU: "feishu",
|
FEISHU: "feishu",
|
||||||
MATRIX: "matrix",
|
MATRIX: "matrix",
|
||||||
|
WECHATWORK: "wechatwork",
|
||||||
}
|
}
|
||||||
|
|
||||||
type Webhook struct {
|
type Webhook struct {
|
||||||
|
|
|
@ -114,15 +114,16 @@ type HookType = string
|
||||||
|
|
||||||
// Types of webhooks
|
// Types of webhooks
|
||||||
const (
|
const (
|
||||||
GITEA HookType = "gitea"
|
GITEA HookType = "gitea"
|
||||||
GOGS HookType = "gogs"
|
GOGS HookType = "gogs"
|
||||||
SLACK HookType = "slack"
|
SLACK HookType = "slack"
|
||||||
DISCORD HookType = "discord"
|
DISCORD HookType = "discord"
|
||||||
DINGTALK HookType = "dingtalk"
|
DINGTALK HookType = "dingtalk"
|
||||||
TELEGRAM HookType = "telegram"
|
TELEGRAM HookType = "telegram"
|
||||||
MSTEAMS HookType = "msteams"
|
MSTEAMS HookType = "msteams"
|
||||||
FEISHU HookType = "feishu"
|
FEISHU HookType = "feishu"
|
||||||
MATRIX HookType = "matrix"
|
MATRIX HookType = "matrix"
|
||||||
|
WECHATWORK HookType = "wechatwork"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HookStatus is the status of a web hook
|
// HookStatus is the status of a web hook
|
||||||
|
|
|
@ -36,7 +36,7 @@ func newWebhookService() {
|
||||||
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
|
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
|
||||||
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
|
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
|
||||||
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
|
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
|
||||||
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix"}
|
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork"}
|
||||||
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
|
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
|
||||||
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
|
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
|
||||||
if Webhook.ProxyURL != "" {
|
if Webhook.ProxyURL != "" {
|
||||||
|
|
|
@ -42,7 +42,7 @@ type CreateHookOptionConfig map[string]string
|
||||||
// CreateHookOption options when create a hook
|
// CreateHookOption options when create a hook
|
||||||
type CreateHookOption struct {
|
type CreateHookOption struct {
|
||||||
// required: true
|
// required: true
|
||||||
// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu
|
// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork
|
||||||
Type string `json:"type" binding:"Required"`
|
Type string `json:"type" binding:"Required"`
|
||||||
// required: true
|
// required: true
|
||||||
Config CreateHookOptionConfig `json:"config" binding:"Required"`
|
Config CreateHookOptionConfig `json:"config" binding:"Required"`
|
||||||
|
|
|
@ -1758,6 +1758,7 @@ settings.add_telegram_hook_desc=Integrovat <a href="%s">Telegram</a> do vašeho
|
||||||
settings.add_matrix_hook_desc=Integrovat <a href="%s">Matrix</a> do vašeho repozitáře.
|
settings.add_matrix_hook_desc=Integrovat <a href="%s">Matrix</a> do vašeho repozitáře.
|
||||||
settings.add_msteams_hook_desc=Integrovat <a href="%s">Microsoft Teams</a> do vašeho repozitáře.
|
settings.add_msteams_hook_desc=Integrovat <a href="%s">Microsoft Teams</a> do vašeho repozitáře.
|
||||||
settings.add_feishu_hook_desc=Integrovat <a href="%s">Feishu</a> do vašeho repozitáře.
|
settings.add_feishu_hook_desc=Integrovat <a href="%s">Feishu</a> do vašeho repozitáře.
|
||||||
|
settings.add_wechatwork_hook_desc=Integrovat <a href="%s">Wechatwork</a> do vašeho repozitáře.
|
||||||
settings.deploy_keys=Klíče pro nasazení
|
settings.deploy_keys=Klíče pro nasazení
|
||||||
settings.add_deploy_key=Přidat klíč pro nasazení
|
settings.add_deploy_key=Přidat klíč pro nasazení
|
||||||
settings.deploy_key_desc=Klíče pro nasazení mají k tomuto repozitáři přístup pouze pro čtení.
|
settings.deploy_key_desc=Klíče pro nasazení mají k tomuto repozitáři přístup pouze pro čtení.
|
||||||
|
|
|
@ -1848,6 +1848,7 @@ settings.add_telegram_hook_desc = Integrate <a href="%s">Telegram</a> into your
|
||||||
settings.add_matrix_hook_desc = Integrate <a href="%s">Matrix</a> into your repository.
|
settings.add_matrix_hook_desc = Integrate <a href="%s">Matrix</a> into your repository.
|
||||||
settings.add_msteams_hook_desc = Integrate <a href="%s">Microsoft Teams</a> into your repository.
|
settings.add_msteams_hook_desc = Integrate <a href="%s">Microsoft Teams</a> into your repository.
|
||||||
settings.add_feishu_hook_desc = Integrate <a href="%s">Feishu</a> into your repository.
|
settings.add_feishu_hook_desc = Integrate <a href="%s">Feishu</a> into your repository.
|
||||||
|
settings.add_Wechat_hook_desc = Integrate <a href="%s">Wechatwork</a> into your repository.
|
||||||
settings.deploy_keys = Deploy Keys
|
settings.deploy_keys = Deploy Keys
|
||||||
settings.add_deploy_key = Add Deploy Key
|
settings.add_deploy_key = Add Deploy Key
|
||||||
settings.deploy_key_desc = Deploy keys have read-only pull access to the repository.
|
settings.deploy_key_desc = Deploy keys have read-only pull access to the repository.
|
||||||
|
|
|
@ -1848,6 +1848,7 @@ settings.add_telegram_hook_desc=将 <a href="%s">Telegram</a> 集成到您的仓
|
||||||
settings.add_matrix_hook_desc=将 <a href="%s">Matrix</a> 集成到您的仓库中。
|
settings.add_matrix_hook_desc=将 <a href="%s">Matrix</a> 集成到您的仓库中。
|
||||||
settings.add_msteams_hook_desc=将 <a href="%s">Microsoft Teams</a> 集成到您的仓库中。
|
settings.add_msteams_hook_desc=将 <a href="%s">Microsoft Teams</a> 集成到您的仓库中。
|
||||||
settings.add_feishu_hook_desc=将 <a href="%s">Feishu</a> 集成到您的仓库中。
|
settings.add_feishu_hook_desc=将 <a href="%s">Feishu</a> 集成到您的仓库中。
|
||||||
|
settings.add_wechatwork_hook_desc=将 <a href="%s">Wechatwork</a> 集成到您的仓库中。
|
||||||
settings.deploy_keys=部署密钥
|
settings.deploy_keys=部署密钥
|
||||||
settings.add_deploy_key=添加部署密钥
|
settings.add_deploy_key=添加部署密钥
|
||||||
settings.deploy_key_desc=部署密钥具有对仓库的只读拉取权限。
|
settings.deploy_key_desc=部署密钥具有对仓库的只读拉取权限。
|
||||||
|
|
|
@ -1833,6 +1833,7 @@ settings.add_telegram_hook_desc=將 <a href="%s">Telegram</a> 整合到您的儲
|
||||||
settings.add_matrix_hook_desc=將 <a href="%s">Matrix</a> 整合到您的儲存庫。
|
settings.add_matrix_hook_desc=將 <a href="%s">Matrix</a> 整合到您的儲存庫。
|
||||||
settings.add_msteams_hook_desc=將 <a href="%s">Microsoft Teams</a> 整合到您的儲存庫。
|
settings.add_msteams_hook_desc=將 <a href="%s">Microsoft Teams</a> 整合到您的儲存庫。
|
||||||
settings.add_feishu_hook_desc=將 <a href="%s">Feishu</a> 整合到您的儲存庫。
|
settings.add_feishu_hook_desc=將 <a href="%s">Feishu</a> 整合到您的儲存庫。
|
||||||
|
settings.add_wechatwork_hook_desc=将 <a href="%s">Wechatwork</a> 整合到您的儲存庫。
|
||||||
settings.deploy_keys=部署金鑰
|
settings.deploy_keys=部署金鑰
|
||||||
settings.add_deploy_key=新增部署金鑰
|
settings.add_deploy_key=新增部署金鑰
|
||||||
settings.deploy_key_desc=部署金鑰具有唯讀權限,可拉取此儲存庫。
|
settings.deploy_key_desc=部署金鑰具有唯讀權限,可拉取此儲存庫。
|
||||||
|
|
BIN
public/img/wechatwork.png
Normal file
BIN
public/img/wechatwork.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
|
@ -638,6 +638,50 @@ func FeishuHooksNewPost(ctx *context.Context) {
|
||||||
ctx.Redirect(orCtx.Link)
|
ctx.Redirect(orCtx.Link)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WechatworkHooksNewPost response for creating wechatwork hook
|
||||||
|
func WechatworkHooksNewPost(ctx *context.Context) {
|
||||||
|
form := web.GetForm(ctx).(*forms.NewWechatWorkHookForm)
|
||||||
|
|
||||||
|
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||||
|
ctx.Data["PageIsSettingsHooks"] = true
|
||||||
|
ctx.Data["PageIsSettingsHooksNew"] = true
|
||||||
|
ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
|
||||||
|
ctx.Data["HookType"] = models.WECHATWORK
|
||||||
|
|
||||||
|
orCtx, err := getOrgRepoCtx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("getOrgRepoCtx", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.HasError() {
|
||||||
|
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &models.Webhook{
|
||||||
|
RepoID: orCtx.RepoID,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: models.ContentTypeJSON,
|
||||||
|
HookEvent: ParseHookEvent(form.WebhookForm),
|
||||||
|
IsActive: form.Active,
|
||||||
|
Type: models.WECHATWORK,
|
||||||
|
Meta: "",
|
||||||
|
OrgID: orCtx.OrgID,
|
||||||
|
IsSystemWebhook: orCtx.IsSystemWebhook,
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
|
func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
|
||||||
ctx.Data["RequireHighlightJS"] = true
|
ctx.Data["RequireHighlightJS"] = true
|
||||||
|
|
||||||
|
@ -1062,6 +1106,39 @@ func FeishuHooksEditPost(ctx *context.Context) {
|
||||||
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
|
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WechatworkHooksEditPost response for editing wechatwork hook
|
||||||
|
func WechatworkHooksEditPost(ctx *context.Context) {
|
||||||
|
form := web.GetForm(ctx).(*forms.NewWechatWorkHookForm)
|
||||||
|
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(http.StatusOK, 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/%d", orCtx.Link, w.ID))
|
||||||
|
}
|
||||||
|
|
||||||
// TestWebhook test if web hook is work fine
|
// TestWebhook test if web hook is work fine
|
||||||
func TestWebhook(ctx *context.Context) {
|
func TestWebhook(ctx *context.Context) {
|
||||||
hookID := ctx.ParamsInt64(":id")
|
hookID := ctx.ParamsInt64(":id")
|
||||||
|
|
|
@ -425,6 +425,7 @@ func RegisterRoutes(m *web.Route) {
|
||||||
m.Post("/matrix/{id}", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
m.Post("/matrix/{id}", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
||||||
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
||||||
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
||||||
|
m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
|
||||||
}, webhooksEnabled)
|
}, webhooksEnabled)
|
||||||
|
|
||||||
m.Group("/{configType:default-hooks|system-hooks}", func() {
|
m.Group("/{configType:default-hooks|system-hooks}", func() {
|
||||||
|
@ -438,6 +439,8 @@ func RegisterRoutes(m *web.Route) {
|
||||||
m.Post("/matrix/new", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
m.Post("/matrix/new", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
||||||
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
||||||
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
||||||
|
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Group("/auths", func() {
|
m.Group("/auths", func() {
|
||||||
|
@ -628,6 +631,7 @@ func RegisterRoutes(m *web.Route) {
|
||||||
m.Post("/matrix/new", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
m.Post("/matrix/new", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost)
|
||||||
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
||||||
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
|
||||||
|
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
|
||||||
m.Get("/{id}", repo.WebHooksEdit)
|
m.Get("/{id}", repo.WebHooksEdit)
|
||||||
m.Post("/{id}/test", repo.TestWebhook)
|
m.Post("/{id}/test", repo.TestWebhook)
|
||||||
m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
|
m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
|
||||||
|
@ -639,6 +643,7 @@ func RegisterRoutes(m *web.Route) {
|
||||||
m.Post("/matrix/{id}", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
m.Post("/matrix/{id}", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
||||||
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
||||||
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
||||||
|
m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
|
||||||
}, webhooksEnabled)
|
}, webhooksEnabled)
|
||||||
|
|
||||||
m.Group("/keys", func() {
|
m.Group("/keys", func() {
|
||||||
|
|
|
@ -382,6 +382,18 @@ func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) bin
|
||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewWechatWorkHookForm form for creating wechatwork hook
|
||||||
|
type NewWechatWorkHookForm struct {
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
WebhookForm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the fields
|
||||||
|
func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
|
ctx := context.GetContext(req)
|
||||||
|
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
// .___
|
// .___
|
||||||
// | | ______ ________ __ ____
|
// | | ______ ________ __ ____
|
||||||
// | |/ ___// ___/ | \_/ __ \
|
// | |/ ___// ___/ | \_/ __ \
|
||||||
|
|
|
@ -52,6 +52,10 @@ var (
|
||||||
name: models.MATRIX,
|
name: models.MATRIX,
|
||||||
payloadCreator: GetMatrixPayload,
|
payloadCreator: GetMatrixPayload,
|
||||||
},
|
},
|
||||||
|
models.WECHATWORK: {
|
||||||
|
name: models.WECHATWORK,
|
||||||
|
payloadCreator: GetWechatworkPayload,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
188
services/webhook/wechatwork.go
Normal file
188
services/webhook/wechatwork.go
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
// Copyright 2021 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 webhook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// WechatworkPayload represents
|
||||||
|
WechatworkPayload struct {
|
||||||
|
Msgtype string `json:"msgtype"`
|
||||||
|
Text struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
MentionedList []string `json:"mentioned_list"`
|
||||||
|
MentionedMobileList []string `json:"mentioned_mobile_list"`
|
||||||
|
} `json:"text"`
|
||||||
|
Markdown struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
} `json:"markdown"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetSecret sets the Wechatwork secret
|
||||||
|
func (f *WechatworkPayload) SetSecret(_ string) {}
|
||||||
|
|
||||||
|
// JSONPayload Marshals the WechatworkPayload to json
|
||||||
|
func (f *WechatworkPayload) JSONPayload() ([]byte, error) {
|
||||||
|
data, err := json.MarshalIndent(f, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWechatworkMarkdownPayload(title string) *WechatworkPayload {
|
||||||
|
return &WechatworkPayload{
|
||||||
|
Msgtype: "markdown",
|
||||||
|
Markdown: struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}{
|
||||||
|
Content: title,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ PayloadConvertor = &WechatworkPayload{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create implements PayloadConvertor Create method
|
||||||
|
func (f *WechatworkPayload) Create(p *api.CreatePayload) (api.Payloader, error) {
|
||||||
|
// created tag/branch
|
||||||
|
refName := git.RefEndName(p.Ref)
|
||||||
|
title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload(title), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete implements PayloadConvertor Delete method
|
||||||
|
func (f *WechatworkPayload) Delete(p *api.DeletePayload) (api.Payloader, error) {
|
||||||
|
// created tag/branch
|
||||||
|
refName := git.RefEndName(p.Ref)
|
||||||
|
title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload(title), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fork implements PayloadConvertor Fork method
|
||||||
|
func (f *WechatworkPayload) Fork(p *api.ForkPayload) (api.Payloader, error) {
|
||||||
|
|
||||||
|
title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload(title), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push implements PayloadConvertor Push method
|
||||||
|
func (f *WechatworkPayload) Push(p *api.PushPayload) (api.Payloader, error) {
|
||||||
|
var (
|
||||||
|
branchName = git.RefEndName(p.Ref)
|
||||||
|
commitDesc string
|
||||||
|
)
|
||||||
|
|
||||||
|
title := fmt.Sprintf("# %s:%s <font color=\"warning\"> %s </font>", p.Repo.FullName, 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 = "Author:" + commit.Author.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
message := strings.ReplaceAll(commit.Message, "\n\n", "\r\n")
|
||||||
|
text += fmt.Sprintf(" > [%s](%s) \r\n ><font color=\"info\">%s</font> \n ><font color=\"warning\">%s</font>", commit.ID[:7], commit.URL,
|
||||||
|
message, authorName)
|
||||||
|
|
||||||
|
// add linebreak to each commit but the last
|
||||||
|
if i < len(p.Commits)-1 {
|
||||||
|
text += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newWechatworkMarkdownPayload(title + "\r\n\r\n" + text), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue implements PayloadConvertor Issue method
|
||||||
|
func (f *WechatworkPayload) Issue(p *api.IssuePayload) (api.Payloader, error) {
|
||||||
|
text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true)
|
||||||
|
var content string
|
||||||
|
content += fmt.Sprintf(" ><font color=\"info\">%s</font>\n >%s \n ><font color=\"warning\"> %s</font>", text, attachmentText, issueTitle)
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload(content), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueComment implements PayloadConvertor IssueComment method
|
||||||
|
func (f *WechatworkPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) {
|
||||||
|
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true)
|
||||||
|
var content string
|
||||||
|
content += fmt.Sprintf(" ><font color=\"info\">%s</font>\n >%s \n ><font color=\"warning\">%s</font>", text, p.Comment.Body, issueTitle)
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload(content), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullRequest implements PayloadConvertor PullRequest method
|
||||||
|
func (f *WechatworkPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) {
|
||||||
|
text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true)
|
||||||
|
pr := fmt.Sprintf("> <font color=\"info\"> %s </font> \r\n > <font color=\"comment\">%s </font> \r\n > <font color=\"comment\">%s </font> \r\n",
|
||||||
|
text, issueTitle, attachmentText)
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload(pr), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Review implements PayloadConvertor Review method
|
||||||
|
func (f *WechatworkPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) {
|
||||||
|
var text, title string
|
||||||
|
switch p.Action {
|
||||||
|
case api.HookIssueSynchronized:
|
||||||
|
action, err := parseHookPullRequestEventType(event)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
|
||||||
|
text = p.Review.Content
|
||||||
|
}
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload("# " + title + "\r\n\r\n >" + text), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository implements PayloadConvertor Repository method
|
||||||
|
func (f *WechatworkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) {
|
||||||
|
var title string
|
||||||
|
switch p.Action {
|
||||||
|
case api.HookRepoCreated:
|
||||||
|
title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
|
||||||
|
return newWechatworkMarkdownPayload(title), nil
|
||||||
|
case api.HookRepoDeleted:
|
||||||
|
title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
|
||||||
|
return newWechatworkMarkdownPayload(title), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release implements PayloadConvertor Release method
|
||||||
|
func (f *WechatworkPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
|
||||||
|
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
|
||||||
|
|
||||||
|
return newWechatworkMarkdownPayload(text), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWechatworkPayload GetWechatworkPayload converts a ding talk webhook into a WechatworkPayload
|
||||||
|
func GetWechatworkPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) {
|
||||||
|
return convertPayloader(new(WechatworkPayload), p, event)
|
||||||
|
}
|
|
@ -32,6 +32,8 @@
|
||||||
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/feishu.png">
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/feishu.png">
|
||||||
{{else if eq .HookType "matrix"}}
|
{{else if eq .HookType "matrix"}}
|
||||||
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/matrix.svg">
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/matrix.svg">
|
||||||
|
{{else if eq .HookType "wechatwork"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/wechatwork.png">
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
|
@ -45,6 +47,7 @@
|
||||||
{{template "repo/settings/webhook/msteams" .}}
|
{{template "repo/settings/webhook/msteams" .}}
|
||||||
{{template "repo/settings/webhook/feishu" .}}
|
{{template "repo/settings/webhook/feishu" .}}
|
||||||
{{template "repo/settings/webhook/matrix" .}}
|
{{template "repo/settings/webhook/matrix" .}}
|
||||||
|
{{template "repo/settings/webhook/wechatwork" .}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{template "repo/settings/webhook/history" .}}
|
{{template "repo/settings/webhook/history" .}}
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/feishu.png">
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/feishu.png">
|
||||||
{{else if eq .HookType "matrix"}}
|
{{else if eq .HookType "matrix"}}
|
||||||
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/matrix.svg">
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/matrix.svg">
|
||||||
|
{{else if eq .HookType "wechatwork"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/wechatwork.png">
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
|
@ -40,6 +42,7 @@
|
||||||
{{template "repo/settings/webhook/msteams" .}}
|
{{template "repo/settings/webhook/msteams" .}}
|
||||||
{{template "repo/settings/webhook/feishu" .}}
|
{{template "repo/settings/webhook/feishu" .}}
|
||||||
{{template "repo/settings/webhook/matrix" .}}
|
{{template "repo/settings/webhook/matrix" .}}
|
||||||
|
{{template "repo/settings/webhook/wechatwork" .}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{template "repo/settings/webhook/history" .}}
|
{{template "repo/settings/webhook/history" .}}
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
<a class="item" href="{{.BaseLinkNew}}/matrix/new">
|
<a class="item" href="{{.BaseLinkNew}}/matrix/new">
|
||||||
<img width="20" height="20" src="{{AssetUrlPrefix}}/img/matrix.svg">Matrix
|
<img width="20" height="20" src="{{AssetUrlPrefix}}/img/matrix.svg">Matrix
|
||||||
</a>
|
</a>
|
||||||
|
<a class="item" href="{{.BaseLinkNew}}/wechatwork/new">
|
||||||
|
<img width="20" height="20" src="{{AssetUrlPrefix}}/img/wechatwork.png">Wechatwork
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/feishu.png">
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/feishu.png">
|
||||||
{{else if eq .HookType "matrix"}}
|
{{else if eq .HookType "matrix"}}
|
||||||
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/matrix.svg">
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/matrix.svg">
|
||||||
|
{{else if eq .HookType "wechatwork"}}
|
||||||
|
<img width="26" height="26" src="{{AssetUrlPrefix}}/img/wechatwork.png">
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
|
@ -38,6 +40,7 @@
|
||||||
{{template "repo/settings/webhook/msteams" .}}
|
{{template "repo/settings/webhook/msteams" .}}
|
||||||
{{template "repo/settings/webhook/feishu" .}}
|
{{template "repo/settings/webhook/feishu" .}}
|
||||||
{{template "repo/settings/webhook/matrix" .}}
|
{{template "repo/settings/webhook/matrix" .}}
|
||||||
|
{{template "repo/settings/webhook/wechatwork" .}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{template "repo/settings/webhook/history" .}}
|
{{template "repo/settings/webhook/history" .}}
|
||||||
|
|
11
templates/repo/settings/webhook/wechatwork.tmpl
Normal file
11
templates/repo/settings/webhook/wechatwork.tmpl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{{if eq .HookType "wechatwork"}}
|
||||||
|
<p>{{.i18n.Tr "repo.settings.add_wechatwork_hook_desc" "https://work.weixin.qq.com" | Str2html}}</p>
|
||||||
|
<form class="ui form" action="{{.BaseLink}}/wechatwork/{{or .Webhook.ID "new"}}" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
|
||||||
|
<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
|
||||||
|
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
|
||||||
|
</div>
|
||||||
|
{{template "repo/settings/webhook/settings" .}}
|
||||||
|
</form>
|
||||||
|
{{end}}
|
|
@ -12917,7 +12917,8 @@
|
||||||
"msteams",
|
"msteams",
|
||||||
"slack",
|
"slack",
|
||||||
"telegram",
|
"telegram",
|
||||||
"feishu"
|
"feishu",
|
||||||
|
"wechatwork"
|
||||||
],
|
],
|
||||||
"x-go-name": "Type"
|
"x-go-name": "Type"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue