This is an automated email from the ASF dual-hosted git repository.

linkinstar pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-answer-plugins.git

commit 9d75869e3d3748de367e9ce8768b45414f3595dd
Author: lihui <[email protected]>
AuthorDate: Fri Jan 3 00:53:32 2025 +0800

    feat: add wecom notification
---
 notification-wecom/README.md             |  33 +++++++
 notification-wecom/README_CN.md          |  33 +++++++
 notification-wecom/config.go             |  53 +++++++++++
 notification-wecom/docs/wecom-config.png | Bin 0 -> 104028 bytes
 notification-wecom/go.mod                |  47 ++++++++++
 notification-wecom/go.sum                | 147 ++++++++++++++++++++++++++++++
 notification-wecom/i18n/en_US.yaml       |  93 +++++++++++++++++++
 notification-wecom/i18n/translation.go   |  52 +++++++++++
 notification-wecom/i18n/zh_CN.yaml       |  92 +++++++++++++++++++
 notification-wecom/info.yaml             |   5 ++
 notification-wecom/notification_wecom.go | 149 +++++++++++++++++++++++++++++++
 notification-wecom/schema.go             |  38 ++++++++
 notification-wecom/user_config.go        | 137 ++++++++++++++++++++++++++++
 13 files changed, 879 insertions(+)

diff --git a/notification-wecom/README.md b/notification-wecom/README.md
new file mode 100644
index 0000000..5006009
--- /dev/null
+++ b/notification-wecom/README.md
@@ -0,0 +1,33 @@
+# Wecom Notification Plugin
+
+## How to use
+
+To use the notification-wecom plugin with your application, install it using 
the following command:
+
+```bash
+./answer build --with 
github.com/lhui/incubator-answer-plugins/notification-wecom
+```
+
+
+## Feature
+
+- Send message to Wecom
+
+## Config
+
+> Config Webhook URL and open the notification
+
+- Webhook URL: such as 
`https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
+
+## Preview
+
+![Wecom Config](./docs/wecom-config.png)
+
+## Document
+
+https://open.work.weixin.qq.com/help2/pc/14931
+https://developer.work.weixin.qq.com/document/path/91770
+
+## Thanks
+
+Thanks for sharing the DingTalk notification plugin, it's a helpful reference 
for this plugin development.
diff --git a/notification-wecom/README_CN.md b/notification-wecom/README_CN.md
new file mode 100644
index 0000000..607f225
--- /dev/null
+++ b/notification-wecom/README_CN.md
@@ -0,0 +1,33 @@
+# 企业微信通知插件
+
+## 使用指南
+
+要使用通知企业微信通知插件,请使用以下命令进行安装:
+
+```bash
+./answer build --with 
github.com/lhui/incubator-answer-plugins/notification-wecom
+```
+
+
+## 功能
+
+- 通过企业微信群机器人发送信息到企业微信
+
+## 配置
+
+> 配置Webhook URL并打开通知
+
+- Webhook 地址: 类似于 
`https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
+
+## 预览
+
+![企业微信配置](./docs/wecom-config.png)
+
+## 开发文档
+
+https://open.work.weixin.qq.com/help2/pc/14931
+https://developer.work.weixin.qq.com/document/path/91770
+
+## 致谢
+
+感谢钉钉通知插件的开发,本插件很多代码参考壶或者直接沿用了钉钉通知插件的代码。
\ No newline at end of file
diff --git a/notification-wecom/config.go b/notification-wecom/config.go
new file mode 100644
index 0000000..0bdcc81
--- /dev/null
+++ b/notification-wecom/config.go
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package wecom
+
+import (
+       "encoding/json"
+
+       "github.com/apache/incubator-answer-plugins/notification-wecom/i18n"
+       "github.com/apache/incubator-answer/plugin"
+)
+
+type NotificationConfig struct {
+       Notification bool `json:"notification"`
+}
+
+func (n *Notification) ConfigFields() []plugin.ConfigField {
+       return []plugin.ConfigField{
+               {
+                       Name:        "notification",
+                       Type:        plugin.ConfigTypeSwitch,
+                       Title:       
plugin.MakeTranslator(i18n.ConfigNotificationTitle),
+                       Description: 
plugin.MakeTranslator(i18n.ConfigNotificationDescription),
+                       UIOptions: plugin.ConfigFieldUIOptions{
+                               Label: 
plugin.MakeTranslator(i18n.ConfigNotificationLabel),
+                       },
+                       Value: n.Config.Notification,
+               },
+       }
+}
+
+func (n *Notification) ConfigReceiver(config []byte) error {
+       c := &NotificationConfig{}
+       _ = json.Unmarshal(config, c)
+       n.Config = c
+       return nil
+}
diff --git a/notification-wecom/docs/wecom-config.png 
b/notification-wecom/docs/wecom-config.png
new file mode 100644
index 0000000..be534f1
Binary files /dev/null and b/notification-wecom/docs/wecom-config.png differ
diff --git a/notification-wecom/go.mod b/notification-wecom/go.mod
new file mode 100644
index 0000000..2465589
--- /dev/null
+++ b/notification-wecom/go.mod
@@ -0,0 +1,47 @@
+module github.com/apache/incubator-answer-plugins/notification-wecom
+
+go 1.23.4
+
+require (
+       github.com/apache/incubator-answer v1.4.1
+       github.com/apache/incubator-answer-plugins/util v1.0.2
+)
+
+require (
+       github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect
+       github.com/aymerick/douceur v0.2.0 // indirect
+       github.com/bytedance/sonic v1.12.2 // indirect
+       github.com/bytedance/sonic/loader v0.2.0 // indirect
+       github.com/cloudwego/base64x v0.1.4 // indirect
+       github.com/cloudwego/iasm v0.2.0 // indirect
+       github.com/gabriel-vasile/mimetype v1.4.5 // indirect
+       github.com/gin-contrib/sse v0.1.0 // indirect
+       github.com/gin-gonic/gin v1.10.0 // indirect
+       github.com/go-playground/locales v0.14.1 // indirect
+       github.com/go-playground/universal-translator v0.18.1 // indirect
+       github.com/go-playground/validator/v10 v10.22.1 // indirect
+       github.com/goccy/go-json v0.10.3 // indirect
+       github.com/google/wire v0.5.0 // indirect
+       github.com/gorilla/css v1.0.1 // indirect
+       github.com/json-iterator/go v1.1.12 // indirect
+       github.com/klauspost/cpuid/v2 v2.2.8 // indirect
+       github.com/kr/text v0.2.0 // indirect
+       github.com/leodido/go-urn v1.4.0 // indirect
+       github.com/mattn/go-isatty v0.0.20 // indirect
+       github.com/microcosm-cc/bluemonday v1.0.27 // indirect
+       github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 
indirect
+       github.com/modern-go/reflect2 v1.0.2 // indirect
+       github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+       github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f // 
indirect
+       github.com/segmentfault/pacman/contrib/i18n 
v0.0.0-20230822083413-c0075a2d401f // indirect
+       github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+       github.com/ugorji/go/codec v1.2.12 // indirect
+       golang.org/x/arch v0.10.0 // indirect
+       golang.org/x/crypto v0.27.0 // indirect
+       golang.org/x/net v0.29.0 // indirect
+       golang.org/x/sys v0.25.0 // indirect
+       golang.org/x/text v0.18.0 // indirect
+       google.golang.org/protobuf v1.34.2 // indirect
+       gopkg.in/yaml.v3 v3.0.1 // indirect
+       sigs.k8s.io/yaml v1.4.0 // indirect
+)
diff --git a/notification-wecom/go.sum b/notification-wecom/go.sum
new file mode 100644
index 0000000..b08ce5b
--- /dev/null
+++ b/notification-wecom/go.sum
@@ -0,0 +1,147 @@
+github.com/BurntSushi/toml v1.0.0 
h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
+github.com/BurntSushi/toml v1.0.0/go.mod 
h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/LinkinStars/go-i18n/v2 v2.2.2 
h1:ZfjpzbW13dv6btv3RALKZkpN9A+7K1JA//2QcNeWaxU=
+github.com/LinkinStars/go-i18n/v2 v2.2.2/go.mod 
h1:hLglSJ4/3M0Y7ZVcoEJI+OwqkglHCA32DdjuJJR2LbM=
+github.com/apache/incubator-answer v1.4.1 
h1:O7BJyhLmc0JxYgjBz/vkapN2vtI0ylFx+PdOzqROM0c=
+github.com/apache/incubator-answer v1.4.1/go.mod 
h1:L0GTnyO7ykalUVJkApwtbWGLfsknwSyTKdJG5V4x/d8=
+github.com/apache/incubator-answer-plugins/util v1.0.2 
h1:PontocVaiEm+oTj+4aDonwWDZnxywUeHsaTwlQgclfA=
+github.com/apache/incubator-answer-plugins/util v1.0.2/go.mod 
h1:KPMSiM4ec4uEl2njaGINYuSl6zVmHdvPB2nHUxVcQDo=
+github.com/aymerick/douceur v0.2.0 
h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod 
h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
+github.com/bytedance/sonic v1.12.2 
h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
+github.com/bytedance/sonic v1.12.2/go.mod 
h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
+github.com/bytedance/sonic/loader v0.1.1/go.mod 
h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.0 
h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
+github.com/bytedance/sonic/loader v0.2.0/go.mod 
h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/cloudwego/base64x v0.1.4 
h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
+github.com/cloudwego/base64x v0.1.4/go.mod 
h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0 
h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/iasm v0.2.0/go.mod 
h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/creack/pty v1.1.9/go.mod 
h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc 
h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.5 
h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
+github.com/gabriel-vasile/mimetype v1.4.5/go.mod 
h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
+github.com/gin-contrib/sse v0.1.0 
h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod 
h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.10.0 
h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod 
h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-playground/assert/v2 v2.2.0 
h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod 
h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 
h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod 
h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 
h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod 
h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.22.1 
h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
+github.com/go-playground/validator/v10 v10.22.1/go.mod 
h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/goccy/go-json v0.10.3 
h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod 
h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/google/go-cmp v0.2.0/go.mod 
h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/subcommands v1.0.1/go.mod 
h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
+github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
+github.com/google/wire v0.5.0/go.mod 
h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
+github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
+github.com/gorilla/css v1.0.1/go.mod 
h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
+github.com/json-iterator/go v1.1.12 
h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod 
h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod 
h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.8 
h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
+github.com/klauspost/cpuid/v2 v2.2.8/go.mod 
h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod 
h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod 
h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod 
h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.4.0 
h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod 
h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-isatty v0.0.20 
h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod 
h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/microcosm-cc/bluemonday v1.0.27 
h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
+github.com/microcosm-cc/bluemonday v1.0.27/go.mod 
h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd 
h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 
h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod 
h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.3 
h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod 
h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 
h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.8.0 
h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod 
h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f 
h1:9f2Bjf6bdMvNyUop32wAGJCdp+Jdm/d6nKBYvFvkRo0=
+github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f/go.mod 
h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs=
+github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230822083413-c0075a2d401f 
h1:xia6AXJor4UV4T6htmHlfN7CGXZ04vlWwybVtFKJ/mA=
+github.com/segmentfault/pacman/contrib/i18n 
v0.0.0-20230822083413-c0075a2d401f/go.mod 
h1:7QcRmnV7OYq4hNOOCWXT5HXnN/u756JUsqIW0Bw8n9E=
+github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod 
h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod 
h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod 
h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod 
h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.9.0 
h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/twitchyliquid64/golang-asm v0.15.1 
h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod 
h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.12 
h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod 
h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/yuin/goldmark v1.4.13/go.mod 
h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
+golang.org/x/arch v0.10.0/go.mod 
h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
+golang.org/x/crypto v0.27.0/go.mod 
h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod 
h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod 
h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod 
h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
+golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
+golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod 
h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
+golang.org/x/text v0.18.0/go.mod 
h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod 
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod 
h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.34.2 
h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod 
h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod 
h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod 
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+nullprogram.com/x/optparse v1.0.0/go.mod 
h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
diff --git a/notification-wecom/i18n/en_US.yaml 
b/notification-wecom/i18n/en_US.yaml
new file mode 100644
index 0000000..ba01cdc
--- /dev/null
+++ b/notification-wecom/i18n/en_US.yaml
@@ -0,0 +1,93 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+plugin:
+  wecom_notification:
+    backend:
+      info:
+        name:
+          other: Wecom Notification
+        description:
+          other: Send notifications to Wecom
+      config:
+        tip:
+          title:
+            other: Push notification service has been turned off.
+        notification:
+          label:
+            other: Turn on push notifications
+          title:
+            other: Notifications
+          description:
+            other: Users will receive notifications on Wecom.
+      user_config:
+        webhook_url:
+          title:
+            other: Webhook URL
+        inbox_notifications:
+          title:
+            other: Inbox Notifications
+          label:
+            other: Turn on inbox notifications
+          description:
+            other: Answers to your questions, comments, invites, and more.
+        all_new_questions:
+          title:
+            other: All New Questions
+          label:
+            other: Turn on all new questions
+          description:
+            other: Get notified of all new questions. Up to 50 questions per 
week.
+        new_questions_for_following_tags:
+          title:
+            other: New Questions for Following Tags
+          label:
+            other: Turn on new questions for following tags
+          description:
+            other: Get notified of new questions for following tags.
+      tpl:
+        update_question:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) updated 
question [{{.QuestionTitle}}]({{.QuestionUrl}})"
+        answer_the_question:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 
answered the question [{{.QuestionTitle}}]({{.AnswerUrl}})"
+        update_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) updated 
answer [{{.QuestionTitle}}]({{.AnswerUrl}})"
+        accept_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 
accepted answer [{{.QuestionTitle}}]({{.AnswerUrl}})"
+        comment_question:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 
commented question [{{.QuestionTitle}}]({{.CommentUrl}})"
+        comment_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 
commented answer [{{.QuestionTitle}}]({{.CommentUrl}})"
+        reply_to_you:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) replied 
you [{{.QuestionTitle}}]({{.CommentUrl}})"
+        mention_you:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 
mentioned you [{{.QuestionTitle}}]({{.CommentUrl}})"
+        invited_you_to_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) invited 
you to answer [{{.QuestionTitle}}]({{.QuestionUrl}})"
+        new_question:
+          text:
+            other: "New question:\n[{{.QuestionTitle}}]({{.QuestionUrl}})\n 
Tag: {{.QuestionTags}}"
diff --git a/notification-wecom/i18n/translation.go 
b/notification-wecom/i18n/translation.go
new file mode 100644
index 0000000..80db37e
--- /dev/null
+++ b/notification-wecom/i18n/translation.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package i18n
+
+const (
+       InfoName                      = 
"plugin.wecom_notification.backend.info.name"
+       InfoDescription               = 
"plugin.wecom_notification.backend.info.description"
+       ConfigTipTitle                = 
"plugin.wecom_notification.backend.config.tip.title"
+       ConfigNotificationLabel       = 
"plugin.wecom_notification.backend.config.notification.label"
+       ConfigNotificationTitle       = 
"plugin.wecom_notification.backend.config.notification.title"
+       ConfigNotificationDescription = 
"plugin.wecom_notification.backend.config.notification.description"
+
+       UserConfigWebhookURLTitle               = 
"plugin.wecom_notification.backend.user_config.webhook_url.title"
+       UserConfigInboxNotificationsTitle       = 
"plugin.wecom_notification.backend.user_config.inbox_notifications.title"
+       UserConfigInboxNotificationsLabel       = 
"plugin.wecom_notification.backend.user_config.inbox_notifications.label"
+       UserConfigInboxNotificationsDescription = 
"plugin.wecom_notification.backend.user_config.inbox_notifications.description"
+
+       UserConfigAllNewQuestionsNotificationsTitle       = 
"plugin.wecom_notification.backend.user_config.all_new_questions.title"
+       UserConfigAllNewQuestionsNotificationsLabel       = 
"plugin.wecom_notification.backend.user_config.all_new_questions.label"
+       UserConfigAllNewQuestionsNotificationsDescription = 
"plugin.wecom_notification.backend.user_config.all_new_questions.description"
+
+       UserConfigNewQuestionsForFollowingTagsTitle       = 
"plugin.wecom_notification.backend.user_config.new_questions_for_following_tags.title"
+       UserConfigNewQuestionsForFollowingTagsLabel       = 
"plugin.wecom_notification.backend.user_config.new_questions_for_following_tags.label"
+       UserConfigNewQuestionsForFollowingTagsDescription = 
"plugin.wecom_notification.backend.user_config.new_questions_for_following_tags.description"
+
+       TplUpdateQuestion          = 
"plugin.wecom_notification.backend.tpl.update_question.text"
+       TplAnswerTheQuestion       = 
"plugin.wecom_notification.backend.tpl.answer_the_question.text"
+       TplUpdateAnswer            = 
"plugin.wecom_notification.backend.tpl.update_answer.text"
+       TplAcceptAnswer            = 
"plugin.wecom_notification.backend.tpl.accept_answer.text"
+       TplCommentQuestion         = 
"plugin.wecom_notification.backend.tpl.comment_question.text"
+       TplCommentAnswer           = 
"plugin.wecom_notification.backend.tpl.comment_answer.text"
+       TplReplyToYou              = 
"plugin.wecom_notification.backend.tpl.reply_to_you.text"
+       TplMentionYou              = 
"plugin.wecom_notification.backend.tpl.mention_you.text"
+       TplInvitedYouToAnswer      = 
"plugin.wecom_notification.backend.tpl.invited_you_to_answer.text"
+       TplNewQuestion             = 
"plugin.wecom_notification.backend.tpl.new_question.text"
+)
diff --git a/notification-wecom/i18n/zh_CN.yaml 
b/notification-wecom/i18n/zh_CN.yaml
new file mode 100644
index 0000000..2354703
--- /dev/null
+++ b/notification-wecom/i18n/zh_CN.yaml
@@ -0,0 +1,92 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+plugin:
+  wecom_notification:
+    backend:
+      info:
+        name:
+          other: 企业微信通知
+        description:
+          other: 发送通知到企业微信
+      config:
+        tip:
+          title:
+            other: 推送通知服务已关闭。
+        notification:
+          label:
+            other: 打开通知
+          title:
+            other: 通知
+          description:
+            other: 用户将在企业微信上收到通知。
+      user_config:
+        webhook_url:
+          title:
+            other: Webhook URL
+        inbox_notifications:
+          title:
+            other: 收件箱通知
+          label:
+            other: 打开收件箱通知
+          description:
+            other: 问题的答案、评论、邀请等。
+        all_new_questions:
+          title:
+            other: 所有新问题通知
+          label:
+            other: 打开所有新问题通知
+          description:
+            other: 收到所有新问题的通知。每周最多 50 个问题。
+        new_questions_for_following_tags:
+          title:
+            other: 关注标签的新问题通知
+          label:
+            other: 打开关注标签的新问题通知
+          description:
+            other: 收到以下标签的新问题通知。
+      tpl:
+        update_question:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 更新问题 
[{{.QuestionTitle}}]({{.QuestionUrl}})"
+        answer_the_question:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 回答了问题 
[{{.QuestionTitle}}]({{.AnswerUrl}})"
+        update_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 更新答案 
[{{.QuestionTitle}}]({{.AnswerUrl}})"
+        accept_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 接受答案 
[{{.QuestionTitle}}]({{.AnswerUrl}})"
+        comment_question:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 评论提问 
[{{.QuestionTitle}}]({{.CommentUrl}})"
+        comment_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 评论回答 
[{{.QuestionTitle}}]({{.CommentUrl}})"
+        reply_to_you:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 回复了问题 
[{{.QuestionTitle}}]({{.CommentUrl}})"
+        mention_you:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 提到了你 
[{{.QuestionTitle}}]({{.CommentUrl}})"
+        invited_you_to_answer:
+          text:
+            other: "[{{.TriggerUserDisplayName}}]({{.TriggerUserUrl}}) 邀请你回答 
[{{.QuestionTitle}}]({{.QuestionUrl}})"
+        new_question:
+          text:
+            other: "新问题:\n [{{.QuestionTitle}}]({{.QuestionUrl}}) \n 标签: 
{{.QuestionTags}}"
diff --git a/notification-wecom/info.yaml b/notification-wecom/info.yaml
new file mode 100644
index 0000000..bb07c5c
--- /dev/null
+++ b/notification-wecom/info.yaml
@@ -0,0 +1,5 @@
+slug_name: notification_wecom
+type: notification
+version: 0.0.1
+author: lihui
+link: 
https://github.com/lhui/incubator-answer-plugins/tree/dev/notification-wecom
diff --git a/notification-wecom/notification_wecom.go 
b/notification-wecom/notification_wecom.go
new file mode 100644
index 0000000..64fc0f5
--- /dev/null
+++ b/notification-wecom/notification_wecom.go
@@ -0,0 +1,149 @@
+package wecom
+
+import (
+  "embed"
+  "github.com/apache/incubator-answer/plugin"
+  "github.com/go-resty/resty/v2"
+  "strings"
+
+  "github.com/apache/incubator-answer-plugins/util"
+  wecomI18n 
"github.com/apache/incubator-answer-plugins/notification-wecom/i18n"
+  "github.com/segmentfault/pacman/i18n"
+  "github.com/segmentfault/pacman/log"
+)
+
+//go:embed  info.yaml
+var Info embed.FS
+
+type Notification struct {
+       Config          *NotificationConfig
+       UserConfigCache *UserConfigCache
+}
+
+func init() {
+       uc := &Notification{
+               Config:          &NotificationConfig{},
+               UserConfigCache: NewUserConfigCache(),
+       }
+       plugin.Register(uc)
+}
+func (*Notification) Info() plugin.Info {
+  info := &util.Info{}
+       info.GetInfo(Info)
+
+  return plugin.Info{
+    Name:        plugin.MakeTranslator(wecomI18n.InfoName),
+    SlugName:    info.SlugName,
+    Description: plugin.MakeTranslator(wecomI18n.InfoDescription),
+    Author:      info.Author,
+    Version:     info.Version,
+    Link:        info.Link,
+  }
+}
+
+
+
+// GetNewQuestionSubscribers returns the subscribers of the new question 
notification
+func (n *Notification) GetNewQuestionSubscribers() (userIDs []string) {
+       for userID, conf := range n.UserConfigCache.userConfigMapping {
+               if conf.AllNewQuestions {
+                       userIDs = append(userIDs, userID)
+               }
+       }
+       return userIDs
+}
+
+// Notify sends a notification to the user
+func (n *Notification) Notify(msg plugin.NotificationMessage) {
+       log.Debugf("try to send notification %+v", msg)
+
+       if !n.Config.Notification {
+               return
+       }
+
+       // get user config
+       userConfig, err := n.getUserConfig(msg.ReceiverUserID)
+       if err != nil {
+               log.Errorf("get user config failed: %v", err)
+               return
+       }
+       if userConfig == nil {
+               log.Debugf("user %s has no config", msg.ReceiverUserID)
+               return
+       }
+
+       // check if the notification is enabled
+       switch msg.Type {
+       case plugin.NotificationNewQuestion:
+               if !userConfig.AllNewQuestions {
+                       log.Debugf("user %s not config the new question", 
msg.ReceiverUserID)
+                       return
+               }
+       case plugin.NotificationNewQuestionFollowedTag:
+               if !userConfig.NewQuestionsForFollowingTags {
+                       log.Debugf("user %s not config the new question 
followed tag", msg.ReceiverUserID)
+                       return
+               }
+       default:
+               if !userConfig.InboxNotifications {
+                       log.Debugf("user %s not config the inbox notification", 
msg.ReceiverUserID)
+                       return
+               }
+       }
+
+       log.Debugf("user %s config the notification", msg.ReceiverUserID)
+
+       if len(userConfig.WebhookURL) == 0 {
+               log.Errorf("user %s has no webhook url", msg.ReceiverUserID)
+               return
+       }
+
+       notificationMsg:= renderNotification(msg)
+       // no need to send empty message
+       if len(notificationMsg) == 0 {
+               log.Debugf("this type of notification will be drop, the type is 
%s", msg.Type)
+               return
+       }
+
+       // Create a Resty Client
+       client := resty.New()
+       resp, err := client.R().
+               SetHeader("Content-Type", "application/json").
+               SetBody(NewWebhookReq(notificationMsg)).
+               Post(userConfig.WebhookURL)
+
+       if err != nil {
+               log.Errorf("send message failed: %v %v", err, resp)
+       } else {
+               log.Infof("send message to %s success, resp: %s", 
msg.ReceiverUserID, resp.String())
+       }
+}
+
+func renderNotification(msg plugin.NotificationMessage) (string) {
+       lang := i18n.Language(msg.ReceiverLang)
+       switch msg.Type {
+       case plugin.NotificationUpdateQuestion:
+               return plugin.TranslateWithData(lang, 
wecomI18n.TplUpdateQuestion, msg)
+       case plugin.NotificationAnswerTheQuestion:
+               return plugin.TranslateWithData(lang, 
wecomI18n.TplAnswerTheQuestion, msg)
+       case plugin.NotificationUpdateAnswer:
+               return plugin.TranslateWithData(lang, 
wecomI18n.TplUpdateAnswer, msg)
+       case plugin.NotificationAcceptAnswer:
+               return plugin.TranslateWithData(lang, 
wecomI18n.TplAcceptAnswer, msg)
+       case plugin.NotificationCommentQuestion:
+               return plugin.TranslateWithData(lang, 
wecomI18n.TplCommentQuestion, msg)
+       case plugin.NotificationCommentAnswer:
+               return plugin.TranslateWithData(lang, 
wecomI18n.TplCommentAnswer, msg)
+       case plugin.NotificationReplyToYou:
+               return plugin.TranslateWithData(lang, wecomI18n.TplReplyToYou, 
msg)
+       case plugin.NotificationMentionYou:
+               return plugin.TranslateWithData(lang, wecomI18n.TplMentionYou, 
msg)
+       case plugin.NotificationInvitedYouToAnswer:
+               return plugin.TranslateWithData(lang, 
wecomI18n.TplInvitedYouToAnswer, msg)
+       case plugin.NotificationNewQuestion, 
plugin.NotificationNewQuestionFollowedTag:
+               msg.QuestionTags = strings.Join(strings.Split(msg.QuestionTags, 
","), ", ")
+               return plugin.TranslateWithData(lang, wecomI18n.TplNewQuestion, 
msg)
+       }
+       return ""
+}
+
diff --git a/notification-wecom/schema.go b/notification-wecom/schema.go
new file mode 100644
index 0000000..45b0811
--- /dev/null
+++ b/notification-wecom/schema.go
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package wecom
+
+type WebhookReq struct {
+       MsgType  string `json:"msgtype"`
+       Markdown struct {
+               Text  string `json:"content"`
+       } `json:"markdown"`
+}
+
+func NewWebhookReq(content string) *WebhookReq {
+       return &WebhookReq{
+               MsgType: "markdown",
+               Markdown: struct {
+                       Text  string `json:"content"`
+               }{
+                       Text:  content,
+               },
+       }
+}
diff --git a/notification-wecom/user_config.go 
b/notification-wecom/user_config.go
new file mode 100644
index 0000000..bb294a8
--- /dev/null
+++ b/notification-wecom/user_config.go
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package wecom
+
+import (
+       "encoding/json"
+       "fmt"
+       "sync"
+
+       "github.com/apache/incubator-answer-plugins/notification-wecom/i18n"
+       "github.com/apache/incubator-answer/plugin"
+       "github.com/segmentfault/pacman/log"
+)
+
+type UserConfig struct {
+       WebhookURL                   string `json:"webhook_url"`
+       InboxNotifications           bool   `json:"inbox_notifications"`
+       AllNewQuestions              bool   `json:"all_new_questions"`
+       NewQuestionsForFollowingTags bool   
`json:"new_questions_for_following_tags"`
+}
+
+type UserConfigCache struct {
+       // key: userID value: user config
+       userConfigMapping map[string]*UserConfig
+       sync.Mutex
+}
+
+func NewUserConfigCache() *UserConfigCache {
+       ucc := &UserConfigCache{
+               userConfigMapping: make(map[string]*UserConfig),
+       }
+       return ucc
+}
+
+func (ucc *UserConfigCache) SetUserConfig(userID string, config *UserConfig) {
+       ucc.Lock()
+       defer ucc.Unlock()
+       ucc.userConfigMapping[userID] = config
+}
+
+func (n *Notification) UserConfigFields() []plugin.ConfigField {
+       fields := make([]plugin.ConfigField, 0)
+       // Show tip for user, if the notification service is disabled
+       if !n.Config.Notification {
+               fields = append(fields, plugin.ConfigField{
+                       Name:        "tip",
+                       Type:        plugin.ConfigTypeLegend,
+                       Title:       plugin.MakeTranslator(i18n.ConfigTipTitle),
+                       Description: plugin.Translator{},
+                       UIOptions: plugin.ConfigFieldUIOptions{
+                               ClassName:      "mb-3",
+                               FieldClassName: "mb-0 text-danger",
+                       },
+               })
+       }
+       fields = append(fields, plugin.ConfigField{
+               Name:     "webhook_url",
+               Type:     plugin.ConfigTypeInput,
+               Title:    plugin.MakeTranslator(i18n.UserConfigWebhookURLTitle),
+               Required: true,
+               UIOptions: plugin.ConfigFieldUIOptions{
+                       InputType: plugin.InputTypeText,
+               },
+       })
+       fields = append(fields, createSwitchConfig(
+               "inbox_notifications",
+               i18n.UserConfigInboxNotificationsTitle,
+               i18n.UserConfigInboxNotificationsLabel,
+               i18n.UserConfigInboxNotificationsDescription,
+       ))
+       fields = append(fields, createSwitchConfig(
+               "all_new_questions",
+               i18n.UserConfigAllNewQuestionsNotificationsTitle,
+               i18n.UserConfigAllNewQuestionsNotificationsLabel,
+               i18n.UserConfigAllNewQuestionsNotificationsDescription,
+       ))
+       fields = append(fields, createSwitchConfig(
+               "new_questions_for_following_tags",
+               i18n.UserConfigNewQuestionsForFollowingTagsTitle,
+               i18n.UserConfigNewQuestionsForFollowingTagsLabel,
+               i18n.UserConfigNewQuestionsForFollowingTagsDescription,
+       ))
+       return fields
+}
+
+func createSwitchConfig(name, title, label, desc string) plugin.ConfigField {
+       return plugin.ConfigField{
+               Name:        name,
+               Type:        plugin.ConfigTypeSwitch,
+               Title:       plugin.MakeTranslator(title),
+               Description: plugin.MakeTranslator(desc),
+               UIOptions: plugin.ConfigFieldUIOptions{
+                       Label: plugin.MakeTranslator(label),
+               },
+       }
+}
+
+func (n *Notification) UserConfigReceiver(userID string, config []byte) error {
+       log.Debugf("receive user config %s %s", userID, string(config))
+       var userConfig UserConfig
+       err := json.Unmarshal(config, &userConfig)
+       if err != nil {
+               return fmt.Errorf("unmarshal user config failed: %w", err)
+       }
+       n.UserConfigCache.SetUserConfig(userID, &userConfig)
+       return nil
+}
+
+func (n *Notification) getUserConfig(userID string) (config *UserConfig, err 
error) {
+       userConfig := plugin.GetPluginUserConfig(userID, n.Info().SlugName)
+       if len(userConfig) == 0 {
+               return nil, nil
+       }
+       config = &UserConfig{}
+       err = json.Unmarshal(userConfig, config)
+       if err != nil {
+               return nil, fmt.Errorf("unmarshal user config failed: %w", err)
+       }
+       return config, nil
+}


Reply via email to