diff --git a/README.md b/README.md
index e128fe0d..0b8d4c41 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,8 @@ The Connector plugin helps us to implement third-party login functionality. For
The Storage plugin helps us to upload files to third-party storage. For example: Aliyun OSS or AWS S3.
-- [x] [Aliyun](https://github.com/apache/incubator-answer-plugins/tree/main/storage-aliyunoss)
+- [x] [Aliyun OSS](https://github.com/apache/incubator-answer-plugins/tree/main/storage-aliyunoss)
+- [x] [Tencentyun COS](https://github.com/apache/incubator-answer-plugins/tree/main/storage-tencentyuncos)
- [x] [S3](https://github.com/apache/incubator-answer-plugins/tree/main/storage-s3)
### Cache
@@ -53,6 +54,8 @@ Using the third-party user system to manage users. For example: WeCom
The Notification plugin helps us to send messages to third-party notification systems. For example: Slack.
- [x] [Slack](https://github.com/apache/incubator-answer-plugins/tree/main/notification-slack)
+- [x] [Lark](https://github.com/apache/incubator-answer-plugins/tree/main/notification-lark)
+- [x] [Ding talk](https://github.com/apache/incubator-answer-plugins/tree/main/notification-dingtalk)
### Route
diff --git a/captcha-google-v2/i18n/translation.go b/captcha-google-v2/i18n/translation.go
index 7a84a0c9..a76c6442 100644
--- a/captcha-google-v2/i18n/translation.go
+++ b/captcha-google-v2/i18n/translation.go
@@ -20,12 +20,12 @@
package i18n
const (
- InfoName = "plugin.google_v2_captcha.backend.info.name"
- InfoDescription = "plugin.google_v2_captcha.backend.info.description"
- ConfigSiteKeyTitle = "plugin.google_v2_captcha.backend.config.site_key.title"
- ConfigSiteKeyDescription = "plugin.google_v2_captcha.backend.config.site_key.description"
- ConfigSecretKeyTitle = "plugin.google_v2_captcha.backend.config.secret_key.title"
- ConfigSecretKeyDescription = "plugin.google_v2_captcha.backend.config.secret_key.description"
- ConfigSiteVerifyEndpointTitle = "plugin.google_v2_captcha.backend.config.site_verify_endpoint.title"
+ InfoName = "plugin.google_v2_captcha.backend.info.name"
+ InfoDescription = "plugin.google_v2_captcha.backend.info.description"
+ ConfigSiteKeyTitle = "plugin.google_v2_captcha.backend.config.site_key.title"
+ ConfigSiteKeyDescription = "plugin.google_v2_captcha.backend.config.site_key.description"
+ ConfigSecretKeyTitle = "plugin.google_v2_captcha.backend.config.secret_key.title"
+ ConfigSecretKeyDescription = "plugin.google_v2_captcha.backend.config.secret_key.description"
+ ConfigSiteVerifyEndpointTitle = "plugin.google_v2_captcha.backend.config.site_verify_endpoint.title"
ConfigSiteVerifyEndpointDescription = "plugin.google_v2_captcha.backend.config.site_verify_endpoint.description"
)
diff --git a/captcha-google-v2/recaptcha.go b/captcha-google-v2/recaptcha.go
index 7083bdc1..b610a819 100644
--- a/captcha-google-v2/recaptcha.go
+++ b/captcha-google-v2/recaptcha.go
@@ -42,8 +42,8 @@ type Captcha struct {
}
type CaptchaConfig struct {
- SiteKey string `json:"site_key"`
- SecretKey string `json:"secret_key"`
+ SiteKey string `json:"site_key"`
+ SecretKey string `json:"secret_key"`
SiteVerifyEndpoint string `json:"site_verify_endpoint"`
}
diff --git a/embed-basic/go.mod b/embed-basic/go.mod
index 6d0bf243..c20f43b1 100644
--- a/embed-basic/go.mod
+++ b/embed-basic/go.mod
@@ -3,8 +3,9 @@ module github.com/apache/incubator-answer-plugins/embed-basic
go 1.19
require (
- github.com/apache/incubator-answer v1.3.6
+ github.com/apache/incubator-answer v1.4.0
github.com/apache/incubator-answer-plugins/util v1.0.2
+ github.com/gin-gonic/gin v1.9.1
)
require (
@@ -14,7 +15,6 @@ require (
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
- github.com/gin-gonic/gin v1.9.1 // 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.14.0 // indirect
diff --git a/embed-basic/go.sum b/embed-basic/go.sum
index 02955dbb..309afe1a 100644
--- a/embed-basic/go.sum
+++ b/embed-basic/go.sum
@@ -2,8 +2,8 @@ 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.3.6 h1:OddJdWqDrgIKY2wnLOipT3mjNI9h7fLNc4eEyyUp+hs=
-github.com/apache/incubator-answer v1.3.6/go.mod h1:YKwpG0rwRC0kHcbILcIyIbPMwsWaZ8j5lHJ34DPIdMI=
+github.com/apache/incubator-answer v1.4.0 h1:W3y4TAQ4sdzgcqntGqNBPe0BdyeW7+l8FWYBDs9g8+Y=
+github.com/apache/incubator-answer v1.4.0/go.mod h1:Q4NkQmBd0sV7t3Cd8NBsWh9w8jFRo/2qjzOw9MlRNwk=
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=
diff --git a/embed-basic/info.yaml b/embed-basic/info.yaml
index 94d433e2..054f43ac 100644
--- a/embed-basic/info.yaml
+++ b/embed-basic/info.yaml
@@ -17,6 +17,6 @@
slug_name: basic_embed
type: editor
-version: 1.0.4
+version: 1.0.5
author: answerdev
link: https://github.com/apache/incubator-answer-plugins/tree/main/embed-basic
diff --git a/notification-dingtalk/README.md b/notification-dingtalk/README.md
new file mode 100644
index 00000000..3b1a7d8b
--- /dev/null
+++ b/notification-dingtalk/README.md
@@ -0,0 +1,20 @@
+# Ding talk Notification
+
+## Feature
+
+- Send message to Ding talk
+
+## Config
+
+> Config Webhook URL and open the notification
+
+- Webhook URL: such as `https://oapi.dingtalk.com/robot/send?access_token=xxxxxx`
+
+## Preview
+
+![Ding talk Config](./docs/dingtalk-config.png)
+
+## Document
+
+- https://open.dingtalk.com/document/robots/custom-robot-access
+- https://open.dingtalk.com/document/orgapp/custom-bot-send-message-type
\ No newline at end of file
diff --git a/notification-dingtalk/config.go b/notification-dingtalk/config.go
new file mode 100644
index 00000000..0bad4007
--- /dev/null
+++ b/notification-dingtalk/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 dingtalk
+
+import (
+ "encoding/json"
+
+ "github.com/apache/incubator-answer-plugins/notification-dingtalk/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-dingtalk/dingtalk_notification.go b/notification-dingtalk/dingtalk_notification.go
new file mode 100644
index 00000000..360a941d
--- /dev/null
+++ b/notification-dingtalk/dingtalk_notification.go
@@ -0,0 +1,166 @@
+/*
+ * 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 dingtalk
+
+import (
+ "embed"
+ "github.com/apache/incubator-answer-plugins/util"
+ "github.com/go-resty/resty/v2"
+ "strings"
+
+ dingtalkI18n "github.com/apache/incubator-answer-plugins/notification-dingtalk/i18n"
+ "github.com/apache/incubator-answer/plugin"
+ "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 (n *Notification) Info() plugin.Info {
+ info := &util.Info{}
+ info.GetInfo(Info)
+
+ return plugin.Info{
+ Name: plugin.MakeTranslator(dingtalkI18n.InfoName),
+ SlugName: info.SlugName,
+ Description: plugin.MakeTranslator(dingtalkI18n.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, notificationTitle := 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, notificationTitle)).
+ 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, string) {
+ lang := i18n.Language(msg.ReceiverLang)
+ switch msg.Type {
+ case plugin.NotificationUpdateQuestion:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplUpdateQuestion, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplUpdateQuestionTitle, nil)
+ case plugin.NotificationAnswerTheQuestion:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplAnswerTheQuestion, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplAnswerTheQuestionTitle, nil)
+ case plugin.NotificationUpdateAnswer:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplUpdateAnswer, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplUpdateAnswerTitle, nil)
+ case plugin.NotificationAcceptAnswer:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplAcceptAnswer, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplAcceptAnswerTitle, nil)
+ case plugin.NotificationCommentQuestion:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplCommentQuestion, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplCommentQuestionTitle, nil)
+ case plugin.NotificationCommentAnswer:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplCommentAnswer, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplCommentAnswerTitle, nil)
+ case plugin.NotificationReplyToYou:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplReplyToYou, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplReplyToYouTitle, nil)
+ case plugin.NotificationMentionYou:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplMentionYou, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplMentionYouTitle, nil)
+ case plugin.NotificationInvitedYouToAnswer:
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplInvitedYouToAnswer, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplInvitedYouToAnswerTitle, nil)
+ case plugin.NotificationNewQuestion, plugin.NotificationNewQuestionFollowedTag:
+ msg.QuestionTags = strings.Join(strings.Split(msg.QuestionTags, ","), ", ")
+ return plugin.TranslateWithData(lang, dingtalkI18n.TplNewQuestion, msg), plugin.TranslateWithData(lang, dingtalkI18n.TplNewQuestionTitle, nil)
+ }
+ return "", ""
+}
diff --git a/notification-dingtalk/docs/dingtalk-config.png b/notification-dingtalk/docs/dingtalk-config.png
new file mode 100644
index 00000000..22efeb98
Binary files /dev/null and b/notification-dingtalk/docs/dingtalk-config.png differ
diff --git a/notification-dingtalk/go.mod b/notification-dingtalk/go.mod
new file mode 100644
index 00000000..a0e03e07
--- /dev/null
+++ b/notification-dingtalk/go.mod
@@ -0,0 +1,47 @@
+module github.com/apache/incubator-answer-plugins/dingtalk
+
+go 1.21.3
+
+require (
+ github.com/apache/incubator-answer v1.4.0
+ github.com/apache/incubator-answer-plugins/util v1.0.2
+ github.com/go-resty/resty/v2 v2.15.3
+ github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f
+)
+
+require (
+ github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect
+ github.com/aymerick/douceur v0.2.0 // indirect
+ github.com/bytedance/sonic v1.9.1 // indirect
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/gin-gonic/gin v1.9.1 // 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.14.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/google/wire v0.5.0 // indirect
+ github.com/gorilla/css v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.4 // indirect
+ github.com/kr/text v0.2.0 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/microcosm-cc/bluemonday v1.0.21 // 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.0.8 // indirect
+ github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.11 // indirect
+ golang.org/x/arch v0.3.0 // indirect
+ golang.org/x/crypto v0.25.0 // indirect
+ golang.org/x/net v0.27.0 // indirect
+ golang.org/x/sys v0.22.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ google.golang.org/protobuf v1.30.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ sigs.k8s.io/yaml v1.3.0 // indirect
+)
diff --git a/notification-dingtalk/go.sum b/notification-dingtalk/go.sum
new file mode 100644
index 00000000..5299099b
--- /dev/null
+++ b/notification-dingtalk/go.sum
@@ -0,0 +1,152 @@
+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.0 h1:W3y4TAQ4sdzgcqntGqNBPe0BdyeW7+l8FWYBDs9g8+Y=
+github.com/apache/incubator-answer v1.4.0/go.mod h1:Q4NkQmBd0sV7t3Cd8NBsWh9w8jFRo/2qjzOw9MlRNwk=
+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.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
+github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+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.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
+github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
+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.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/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-20230516093754-b76aef1c1150 h1:OEuW1D7RGDE0CZDr0oGMw9Eiq7fAbD9C4WMrvSixamk=
+github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150/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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+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.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
+golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+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=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+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=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/notification-dingtalk/i18n/en_US.yaml b/notification-dingtalk/i18n/en_US.yaml
new file mode 100644
index 00000000..c4d14497
--- /dev/null
+++ b/notification-dingtalk/i18n/en_US.yaml
@@ -0,0 +1,112 @@
+# 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:
+ dingtalk_notification:
+ backend:
+ info:
+ name:
+ other: Ding talk Notification
+ description:
+ other: Send notifications to Ding Talk
+ 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 Ding Talk.
+ 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:
+ title:
+ other: Question Updated
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> updated question <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ answer_the_question:
+ title:
+ other: Answer Received
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> answered the question <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ update_answer:
+ title:
+ other: Answer Updated
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> updated answer <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ accept_answer:
+ title:
+ other: Answer Accepted
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> accepted answer <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ comment_question:
+ title:
+ other: Comment on Question
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> commented question <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ comment_answer:
+ title:
+ other: Comment on Answer
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> commented answer <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ reply_to_you:
+ title:
+ other: Reply to You
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> replied you <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ mention_you:
+ title:
+ other: Mentioned You
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> mentioned you <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ invited_you_to_answer:
+ title:
+ other: Invited to Answer
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> invited you to answer <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ new_question:
+ title:
+ other: New Question Posted
+ text:
+ other: "New question:\n<{{.QuestionUrl}}|{{.QuestionTitle}}>\n{{.QuestionTags}}"
diff --git a/notification-dingtalk/i18n/translation.go b/notification-dingtalk/i18n/translation.go
new file mode 100644
index 00000000..2d9c5a46
--- /dev/null
+++ b/notification-dingtalk/i18n/translation.go
@@ -0,0 +1,63 @@
+/*
+ * 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.dingtalk_notification.backend.info.name"
+ InfoDescription = "plugin.dingtalk_notification.backend.info.description"
+ ConfigTipTitle = "plugin.dingtalk_notification.backend.config.tip.title"
+ ConfigNotificationLabel = "plugin.dingtalk_notification.backend.config.notification.label"
+ ConfigNotificationTitle = "plugin.dingtalk_notification.backend.config.notification.title"
+ ConfigNotificationDescription = "plugin.dingtalk_notification.backend.config.notification.description"
+
+ UserConfigWebhookURLTitle = "plugin.dingtalk_notification.backend.user_config.webhook_url.title"
+ UserConfigInboxNotificationsTitle = "plugin.dingtalk_notification.backend.user_config.inbox_notifications.title"
+ UserConfigInboxNotificationsLabel = "plugin.dingtalk_notification.backend.user_config.inbox_notifications.label"
+ UserConfigInboxNotificationsDescription = "plugin.dingtalk_notification.backend.user_config.inbox_notifications.description"
+
+ UserConfigAllNewQuestionsNotificationsTitle = "plugin.dingtalk_notification.backend.user_config.all_new_questions.title"
+ UserConfigAllNewQuestionsNotificationsLabel = "plugin.dingtalk_notification.backend.user_config.all_new_questions.label"
+ UserConfigAllNewQuestionsNotificationsDescription = "plugin.dingtalk_notification.backend.user_config.all_new_questions.description"
+
+ UserConfigNewQuestionsForFollowingTagsTitle = "plugin.dingtalk_notification.backend.user_config.new_questions_for_following_tags.title"
+ UserConfigNewQuestionsForFollowingTagsLabel = "plugin.dingtalk_notification.backend.user_config.new_questions_for_following_tags.label"
+ UserConfigNewQuestionsForFollowingTagsDescription = "plugin.dingtalk_notification.backend.user_config.new_questions_for_following_tags.description"
+
+ TplUpdateQuestionTitle = "plugin.dingtalk_notification.backend.tpl.update_question.title"
+ TplUpdateQuestion = "plugin.dingtalk_notification.backend.tpl.update_question.text"
+ TplAnswerTheQuestionTitle = "plugin.dingtalk_notification.backend.tpl.answer_the_question.title"
+ TplAnswerTheQuestion = "plugin.dingtalk_notification.backend.tpl.answer_the_question.text"
+ TplUpdateAnswerTitle = "plugin.dingtalk_notification.backend.tpl.update_answer.title"
+ TplUpdateAnswer = "plugin.dingtalk_notification.backend.tpl.update_answer.text"
+ TplAcceptAnswerTitle = "plugin.dingtalk_notification.backend.tpl.accept_answer.title"
+ TplAcceptAnswer = "plugin.dingtalk_notification.backend.tpl.accept_answer.text"
+ TplCommentQuestionTitle = "plugin.dingtalk_notification.backend.tpl.comment_question.title"
+ TplCommentQuestion = "plugin.dingtalk_notification.backend.tpl.comment_question.text"
+ TplCommentAnswerTitle = "plugin.dingtalk_notification.backend.tpl.comment_answer.title"
+ TplCommentAnswer = "plugin.dingtalk_notification.backend.tpl.comment_answer.text"
+ TplReplyToYouTitle = "plugin.dingtalk_notification.backend.tpl.reply_to_you.title"
+ TplReplyToYou = "plugin.dingtalk_notification.backend.tpl.reply_to_you.text"
+ TplMentionYouTitle = "plugin.dingtalk_notification.backend.tpl.mention_you.title"
+ TplMentionYou = "plugin.dingtalk_notification.backend.tpl.mention_you.text"
+ TplInvitedYouToAnswerTitle = "plugin.dingtalk_notification.backend.tpl.invited_you_to_answer.title"
+ TplInvitedYouToAnswer = "plugin.dingtalk_notification.backend.tpl.invited_you_to_answer.text"
+ TplNewQuestionTitle = "plugin.dingtalk_notification.backend.tpl.new_question.title"
+ TplNewQuestion = "plugin.dingtalk_notification.backend.tpl.new_question.text"
+)
diff --git a/notification-dingtalk/i18n/zh_CN.yaml b/notification-dingtalk/i18n/zh_CN.yaml
new file mode 100644
index 00000000..80a170f4
--- /dev/null
+++ b/notification-dingtalk/i18n/zh_CN.yaml
@@ -0,0 +1,112 @@
+# 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:
+ dingtalk_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:
+ title:
+ other: 更新问题
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 更新问题 <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ answer_the_question:
+ title:
+ other: 回答了问题
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 回答了问题 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ update_answer:
+ title:
+ other: 更新答案
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 更新答案 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ accept_answer:
+ title:
+ other: 接受答案
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 接受答案 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ comment_question:
+ title:
+ other: 评论提问
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 评论提问 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ comment_answer:
+ title:
+ other: 评论回答
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 评论回答 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ reply_to_you:
+ title:
+ other: 回复了问题
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 回复了问题 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ mention_you:
+ title:
+ other: 提到了你
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 提到了你 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ invited_you_to_answer:
+ title:
+ other: 邀请你回答
+ text:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 邀请你回答 <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ new_question:
+ title:
+ other: 新问题
+ text:
+ other: "新问题:\n<{{.QuestionUrl}}|{{.QuestionTitle}}>\n{{.QuestionTags}}"
\ No newline at end of file
diff --git a/notification-dingtalk/info.yaml b/notification-dingtalk/info.yaml
new file mode 100644
index 00000000..c5481f20
--- /dev/null
+++ b/notification-dingtalk/info.yaml
@@ -0,0 +1,22 @@
+# 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.
+
+slug_name: dingtalk_notification
+type: notification
+version: 1.0.0
+author: Luffy
+link: https://github.com/apache/incubator-answer-plugins/tree/main/notification-dingtalk
diff --git a/notification-dingtalk/schema.go b/notification-dingtalk/schema.go
new file mode 100644
index 00000000..65d5d0e1
--- /dev/null
+++ b/notification-dingtalk/schema.go
@@ -0,0 +1,41 @@
+/*
+ * 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 dingtalk
+
+type WebhookReq struct {
+ MsgType string `json:"msgtype"`
+ Markdown struct {
+ Title string `json:"title"`
+ Text string `json:"text"`
+ } `json:"markdown"`
+}
+
+func NewWebhookReq(content string, title string) *WebhookReq {
+ return &WebhookReq{
+ MsgType: "markdown",
+ Markdown: struct {
+ Title string `json:"title"`
+ Text string `json:"text"`
+ }{
+ Title: title,
+ Text: content,
+ },
+ }
+}
diff --git a/notification-dingtalk/user_config.go b/notification-dingtalk/user_config.go
new file mode 100644
index 00000000..2dbdba6e
--- /dev/null
+++ b/notification-dingtalk/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 dingtalk
+
+import (
+ "encoding/json"
+ "fmt"
+ "sync"
+
+ "github.com/apache/incubator-answer-plugins/notification-dingtalk/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
+}
diff --git a/notification-slack/README.md b/notification-slack/README.md
index d52696f6..907e8936 100644
--- a/notification-slack/README.md
+++ b/notification-slack/README.md
@@ -1,14 +1,14 @@
-# Slack Notification
-## Feature
-- Send message to Slack
-
-## Config
-> Config Webhook URL and open the notification
-
-- Webhook URL: such as `https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX`
-
-## Preview
-![Slack Config](./docs/slack-config.png)
-
-## Document
+# Slack Notification
+## Feature
+- Send message to Slack
+
+## Config
+> Config Webhook URL and open the notification
+
+- Webhook URL: such as `https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX`
+
+## Preview
+![Slack Config](./docs/slack-config.png)
+
+## Document
- https://api.slack.com/messaging/webhooks
\ No newline at end of file
diff --git a/notification-slack/config.go b/notification-slack/config.go
index aafe828f..de3a9cd1 100644
--- a/notification-slack/config.go
+++ b/notification-slack/config.go
@@ -17,7 +17,7 @@
* under the License.
*/
-package slack
+package slack_notification
import (
"encoding/json"
diff --git a/notification-slack/go.mod b/notification-slack/go.mod
index 57943ed1..894b18b1 100644
--- a/notification-slack/go.mod
+++ b/notification-slack/go.mod
@@ -1,48 +1,48 @@
-module github.com/apache/incubator-answer-plugins/notification-slack
-
-go 1.19
-
-require (
- github.com/apache/incubator-answer v1.3.6
- github.com/apache/incubator-answer-plugins/util v1.0.2
- github.com/go-resty/resty/v2 v2.11.0
- github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f
-)
-
-require (
- github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect
- github.com/aymerick/douceur v0.2.0 // indirect
- github.com/bytedance/sonic v1.9.1 // indirect
- github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
- github.com/gabriel-vasile/mimetype v1.4.2 // indirect
- github.com/gin-contrib/sse v0.1.0 // indirect
- github.com/gin-gonic/gin v1.9.1 // 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.14.0 // indirect
- github.com/goccy/go-json v0.10.2 // indirect
- github.com/google/go-cmp v0.5.8 // indirect
- github.com/google/wire v0.5.0 // indirect
- github.com/gorilla/css v1.0.0 // indirect
- github.com/json-iterator/go v1.1.12 // indirect
- github.com/klauspost/cpuid/v2 v2.2.4 // indirect
- github.com/kr/text v0.2.0 // indirect
- github.com/leodido/go-urn v1.2.4 // indirect
- github.com/mattn/go-isatty v0.0.19 // indirect
- github.com/microcosm-cc/bluemonday v1.0.21 // 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.0.8 // indirect
- github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150 // indirect
- github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
- github.com/ugorji/go/codec v1.2.11 // indirect
- golang.org/x/arch v0.3.0 // indirect
- golang.org/x/crypto v0.21.0 // indirect
- golang.org/x/net v0.21.0 // indirect
- golang.org/x/sys v0.18.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- google.golang.org/protobuf v1.30.0 // indirect
- gopkg.in/yaml.v2 v2.4.0 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
- sigs.k8s.io/yaml v1.3.0 // indirect
-)
+module github.com/apache/incubator-answer-plugins/notification-slack
+
+go 1.19
+
+require (
+ github.com/apache/incubator-answer v1.3.6
+ github.com/apache/incubator-answer-plugins/util v1.0.2
+ github.com/go-resty/resty/v2 v2.15.0
+ github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f
+)
+
+require (
+ github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect
+ github.com/aymerick/douceur v0.2.0 // indirect
+ github.com/bytedance/sonic v1.9.1 // indirect
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/gin-gonic/gin v1.9.1 // 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.14.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/google/go-cmp v0.5.8 // indirect
+ github.com/google/wire v0.5.0 // indirect
+ github.com/gorilla/css v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.4 // indirect
+ github.com/kr/text v0.2.0 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/microcosm-cc/bluemonday v1.0.21 // 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.0.8 // indirect
+ github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.11 // indirect
+ golang.org/x/arch v0.3.0 // indirect
+ golang.org/x/crypto v0.25.0 // indirect
+ golang.org/x/net v0.27.0 // indirect
+ golang.org/x/sys v0.22.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ google.golang.org/protobuf v1.30.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ sigs.k8s.io/yaml v1.3.0 // indirect
+)
diff --git a/notification-slack/go.sum b/notification-slack/go.sum
index c1a852a8..03bbbd16 100644
--- a/notification-slack/go.sum
+++ b/notification-slack/go.sum
@@ -1,163 +1,146 @@
-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.3.6 h1:OddJdWqDrgIKY2wnLOipT3mjNI9h7fLNc4eEyyUp+hs=
-github.com/apache/incubator-answer v1.3.6/go.mod h1:YKwpG0rwRC0kHcbILcIyIbPMwsWaZ8j5lHJ34DPIdMI=
-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.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
-github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
-github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
-github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
-github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
-github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
-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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
-github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
-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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
-github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
-github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
-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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
-github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
-github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
-github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
-github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.8/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.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
-github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
-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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
-github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-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.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
-github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
-github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
-github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
-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.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
-github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
-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-20230516093754-b76aef1c1150 h1:OEuW1D7RGDE0CZDr0oGMw9Eiq7fAbD9C4WMrvSixamk=
-github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150/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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
-github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
-golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-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.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
-golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-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/sync v0.1.0/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-20220704084225-05e143d24a9e/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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
-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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
-golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-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/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-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/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=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
-sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
-sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
+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.3.6 h1:OddJdWqDrgIKY2wnLOipT3mjNI9h7fLNc4eEyyUp+hs=
+github.com/apache/incubator-answer v1.3.6/go.mod h1:YKwpG0rwRC0kHcbILcIyIbPMwsWaZ8j5lHJ34DPIdMI=
+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.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-resty/resty/v2 v2.15.0 h1:clPQLZ2x9h4yGY81IzpMPnty+xoGyFaDg0XMkCsHf90=
+github.com/go-resty/resty/v2 v2.15.0/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/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.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+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.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
+github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
+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.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+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-20230516093754-b76aef1c1150 h1:OEuW1D7RGDE0CZDr0oGMw9Eiq7fAbD9C4WMrvSixamk=
+github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150/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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+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.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
+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=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+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/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=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/notification-slack/i18n/en_US.yaml b/notification-slack/i18n/en_US.yaml
index 9067b38f..f3149992 100644
--- a/notification-slack/i18n/en_US.yaml
+++ b/notification-slack/i18n/en_US.yaml
@@ -1,82 +1,110 @@
-# 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:
- slack_notification:
- backend:
- info:
- name:
- other: Slack Notification
- description:
- other: Send notifications to Slack
- 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 Slack.
- 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:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> updated question <{{.QuestionUrl}}|{{.QuestionTitle}}>"
- answer_the_question:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> answered the question <{{.AnswerUrl}}|{{.QuestionTitle}}>"
- update_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> updated answer <{{.AnswerUrl}}|{{.QuestionTitle}}>"
- accept_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> accepted answer <{{.AnswerUrl}}|{{.QuestionTitle}}>"
- comment_question:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> commented question <{{.CommentUrl}}|{{.QuestionTitle}}>"
- comment_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> commented answer <{{.CommentUrl}}|{{.QuestionTitle}}>"
- reply_to_you:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> replied you <{{.CommentUrl}}|{{.QuestionTitle}}>"
- mention_you:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> mentioned you <{{.CommentUrl}}|{{.QuestionTitle}}>"
- invited_you_to_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> invited you to answer <{{.QuestionUrl}}|{{.QuestionTitle}}>"
- new_question:
- other: "New question:\n<{{.QuestionUrl}}|{{.QuestionTitle}}>\n{{.QuestionTags}}"
+# 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:
+ slack_notification:
+ backend:
+ info:
+ name:
+ other: Slack Notification
+ description:
+ other: Send notifications to Slack
+ 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 Slack.
+ 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.
+ upvoted_answers:
+ title:
+ other: UpVoted Answers
+ label:
+ other: Turn on notification for upvoted answers
+ description:
+ other: Get notified of upvoted answers
+ downvoted_answers:
+ title:
+ other: DownVoted Answers
+ label:
+ other: Turn on notification for downvoted answers
+ description:
+ other: Get notified of downvoted answers
+ updated_questions:
+ title:
+ other: Updated Questions
+ label:
+ other: Turn on notification for updated questions
+ description:
+ other: Get notified of updated questions
+ updated_answers:
+ title:
+ other: Updated Answers
+ label:
+ other: Turn on notification for updated answers
+ description:
+ other: Get notified of updated answers
+ tpl:
+ updated_questions:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> updated questions <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ answer_the_question:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> answered the question <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ updated_answers:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> updated answers <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ accept_answer:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> accepted answer <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ comment_question:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> commented question <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ comment_answer:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> commented answer <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ reply_to_you:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> replied you <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ mention_you:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> mentioned you <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ invited_you_to_answer:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> invited you to answer <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ new_question:
+ other: "New question:\n<{{.QuestionUrl}}|{{.QuestionTitle}}>\n{{.QuestionTags}}"
diff --git a/notification-slack/i18n/translation.go b/notification-slack/i18n/translation.go
index 16e8cab5..6c83c168 100644
--- a/notification-slack/i18n/translation.go
+++ b/notification-slack/i18n/translation.go
@@ -40,9 +40,25 @@ const (
UserConfigNewQuestionsForFollowingTagsLabel = "plugin.slack_notification.backend.user_config.new_questions_for_following_tags.label"
UserConfigNewQuestionsForFollowingTagsDescription = "plugin.slack_notification.backend.user_config.new_questions_for_following_tags.description"
- TplUpdateQuestion = "plugin.slack_notification.backend.tpl.update_question"
+ UserConfigUpvotedAnswersTitle = "plugin.slack_notification.backend.user_config.upvoted_answers.title"
+ UserConfigUpvotedAnswersLabel = "plugin.slack_notification.backend.user_config.upvoted_answers.label"
+ UserConfigUpvotedAnswersDescription = "plugin.slack_notification.backend.user_config.upvoted_answers.description"
+
+ UserConfigDownvotedAnswersTitle = "plugin.slack_notification.backend.user_config.downvoted_answers.title"
+ UserConfigDownvotedAnswersLabel = "plugin.slack_notification.backend.user_config.downvoted_answers.label"
+ UserConfigDownvotedAnswersDescription = "plugin.slack_notification.backend.user_config.downvoted_answers.description"
+
+ UserConfigUpdatedQuestionsTitle = "plugin.slack_notification.backend.user_config.updated_questions.title"
+ UserConfigUpdatedQuestionsLabel = "plugin.slack_notification.backend.user_config.updated_questions.label"
+ UserConfigUpdatedQuestionsDescription = "plugin.slack_notification.backend.user_config.updated_questions.description"
+
+ UserConfigUpdatedAnswersTitle = "plugin.slack_notification.backend.user_config.updated_answers.title"
+ UserConfigUpdatedAnswersLabel = "plugin.slack_notification.backend.user_config.updated_answers.label"
+ UserConfigUpdatedAnswersDescription = "plugin.slack_notification.backend.user_config.updated_answers.description"
+
+ TplUpdatedQuestions = "plugin.slack_notification.backend.tpl.updated_questions"
TplAnswerTheQuestion = "plugin.slack_notification.backend.tpl.answer_the_question"
- TplUpdateAnswer = "plugin.slack_notification.backend.tpl.update_answer"
+ TplUpdatedAnswers = "plugin.slack_notification.backend.tpl.updated_answers"
TplAcceptAnswer = "plugin.slack_notification.backend.tpl.accept_answer"
TplCommentQuestion = "plugin.slack_notification.backend.tpl.comment_question"
TplCommentAnswer = "plugin.slack_notification.backend.tpl.comment_answer"
@@ -50,4 +66,6 @@ const (
TplMentionYou = "plugin.slack_notification.backend.tpl.mention_you"
TplInvitedYouToAnswer = "plugin.slack_notification.backend.tpl.invited_you_to_answer"
TplNewQuestion = "plugin.slack_notification.backend.tpl.new_question"
+ TplUpvotedAnswer = "plugin.slack_notification.backend.tpl.upvoted_answer"
+ TplDownvotedAnswer = "plugin.slack_notification.backend.tpl.downvoted_answer"
)
diff --git a/notification-slack/i18n/zh_CN.yaml b/notification-slack/i18n/zh_CN.yaml
index 901e2c9b..23e61b9b 100644
--- a/notification-slack/i18n/zh_CN.yaml
+++ b/notification-slack/i18n/zh_CN.yaml
@@ -1,82 +1,110 @@
-# 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:
- slack_notification:
- backend:
- info:
- name:
- other: Slack 通知
- description:
- other: 发送通知到 Slack
- config:
- tip:
- title:
- other: 推送通知服务已关闭。
- notification:
- label:
- other: 打开通知
- title:
- other: 通知
- description:
- other: 用户将在 Slack 上收到通知。
- 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:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 更新问题 <{{.QuestionUrl}}|{{.QuestionTitle}}>"
- answer_the_question:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 回答了问题 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
- update_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 更新答案 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
- accept_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 接受答案 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
- comment_question:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 评论提问 <{{.CommentUrl}}|{{.QuestionTitle}}>"
- comment_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 评论回答 <{{.CommentUrl}}|{{.QuestionTitle}}>"
- reply_to_you:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 回复了问题 <{{.CommentUrl}}|{{.QuestionTitle}}>"
- mention_you:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 提到了你 <{{.CommentUrl}}|{{.QuestionTitle}}>"
- invited_you_to_answer:
- other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 邀请你回答 <{{.QuestionUrl}}|{{.QuestionTitle}}>"
- new_question:
+# 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:
+ slack_notification:
+ backend:
+ info:
+ name:
+ other: Slack 通知
+ description:
+ other: 发送通知到 Slack
+ config:
+ tip:
+ title:
+ other: 推送通知服务已关闭。
+ notification:
+ label:
+ other: 打开通知
+ title:
+ other: 通知
+ description:
+ other: 用户将在 Slack 上收到通知。
+ 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: 收到以下标签的新问题通知。
+ upvoted_answers:
+ title:
+ other: 收到一个点赞
+ label:
+ other: 打开点赞通知
+ description:
+ other: 收到点赞的通知
+ downvoted_answers:
+ title:
+ other: 收到一个反对
+ label:
+ other: 打开反对通知
+ description:
+ other: 收到反对的通知
+ updated_questions:
+ title:
+ other: 问题更新
+ label:
+ other: 打开问题更新通知
+ description:
+ other: 收到问题更新的通知
+ updated_answers:
+ title:
+ other: 回答更新
+ label:
+ other: 打开回答更新通知
+ description:
+ other: 收到回答更新的通知
+ tpl:
+ update_question:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 更新问题 <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ answer_the_question:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 回答了问题 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ update_answer:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 更新答案 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ accept_answer:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 接受答案 <{{.AnswerUrl}}|{{.QuestionTitle}}>"
+ comment_question:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 评论提问 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ comment_answer:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 评论回答 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ reply_to_you:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 回复了问题 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ mention_you:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 提到了你 <{{.CommentUrl}}|{{.QuestionTitle}}>"
+ invited_you_to_answer:
+ other: "<{{.TriggerUserUrl}}|{{.TriggerUserDisplayName}}> 邀请你回答 <{{.QuestionUrl}}|{{.QuestionTitle}}>"
+ new_question:
other: "新问题:\n<{{.QuestionUrl}}|{{.QuestionTitle}}>\n{{.QuestionTags}}"
\ No newline at end of file
diff --git a/notification-slack/info.yaml b/notification-slack/info.yaml
index 6b6e5fe0..7c034f04 100644
--- a/notification-slack/info.yaml
+++ b/notification-slack/info.yaml
@@ -1,22 +1,22 @@
-# 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.
-
-slug_name: slack_notification
-type: notification
-version: 1.0.2
-author: answerdev
-link: https://github.com/apache/incubator-answer-plugins/tree/main/notification-slack
+# 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.
+
+slug_name: slack_notification
+type: notification
+version: 1.0.2
+author: answerdev
+link: https://github.com/apache/incubator-answer-plugins/tree/main/notification-slack
diff --git a/notification-slack/schema.go b/notification-slack/schema.go
index d10581c1..d1c45788 100644
--- a/notification-slack/schema.go
+++ b/notification-slack/schema.go
@@ -17,7 +17,7 @@
* under the License.
*/
-package slack
+package slack_notification
type WebhookReq struct {
Blocks []struct {
diff --git a/notification-slack/slack_notification.go b/notification-slack/slack_notification.go
index c6b01587..0cd28e47 100644
--- a/notification-slack/slack_notification.go
+++ b/notification-slack/slack_notification.go
@@ -17,13 +17,14 @@
* under the License.
*/
-package slack
+package slack_notification
import (
"embed"
+ "strings"
+
"github.com/apache/incubator-answer-plugins/util"
"github.com/go-resty/resty/v2"
- "strings"
slackI18n "github.com/apache/incubator-answer-plugins/notification-slack/i18n"
"github.com/apache/incubator-answer/plugin"
@@ -102,6 +103,25 @@ func (n *Notification) Notify(msg plugin.NotificationMessage) {
log.Debugf("user %s not config the new question followed tag", msg.ReceiverUserID)
return
}
+ case plugin.NotificationUpVotedTheAnswer:
+ if !userConfig.UpvotedAnswers {
+ log.Debugf("user %s not config the new upvoted answers", msg.ReceiverUserID)
+ }
+ case plugin.NotificationDownVotedTheAnswer:
+ if !userConfig.DownvotedAnswers {
+ log.Debugf("user %s not config the new downvoted answers", msg.ReceiverUserID)
+ }
+
+ case plugin.NotificationUpdateQuestion:
+ if !userConfig.UpdatedQuestions {
+ log.Debugf("user %s not config the update question", msg.ReceiverUserID)
+ return
+ }
+ case plugin.NotificationUpdateAnswer:
+ if !userConfig.UpdatedAnswers {
+ log.Debugf("user %s not config the update answer", msg.ReceiverUserID)
+ return
+ }
default:
if !userConfig.InboxNotifications {
log.Debugf("user %s not config the inbox notification", msg.ReceiverUserID)
@@ -141,11 +161,11 @@ func renderNotification(msg plugin.NotificationMessage) string {
lang := i18n.Language(msg.ReceiverLang)
switch msg.Type {
case plugin.NotificationUpdateQuestion:
- return plugin.TranslateWithData(lang, slackI18n.TplUpdateQuestion, msg)
+ return plugin.TranslateWithData(lang, slackI18n.TplUpdatedQuestions, msg)
case plugin.NotificationAnswerTheQuestion:
return plugin.TranslateWithData(lang, slackI18n.TplAnswerTheQuestion, msg)
case plugin.NotificationUpdateAnswer:
- return plugin.TranslateWithData(lang, slackI18n.TplUpdateAnswer, msg)
+ return plugin.TranslateWithData(lang, slackI18n.TplUpdatedAnswers, msg)
case plugin.NotificationAcceptAnswer:
return plugin.TranslateWithData(lang, slackI18n.TplAcceptAnswer, msg)
case plugin.NotificationCommentQuestion:
@@ -161,6 +181,10 @@ func renderNotification(msg plugin.NotificationMessage) string {
case plugin.NotificationNewQuestion, plugin.NotificationNewQuestionFollowedTag:
msg.QuestionTags = strings.Join(strings.Split(msg.QuestionTags, ","), ", ")
return plugin.TranslateWithData(lang, slackI18n.TplNewQuestion, msg)
+ case plugin.NotificationUpVotedTheAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplUpvotedAnswer, msg)
+ case plugin.NotificationDownVotedTheAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplDownvotedAnswer, msg)
}
return ""
}
diff --git a/notification-slack/user_config.go b/notification-slack/user_config.go
index 270df6ee..f657b148 100644
--- a/notification-slack/user_config.go
+++ b/notification-slack/user_config.go
@@ -17,7 +17,7 @@
* under the License.
*/
-package slack
+package slack_notification
import (
"encoding/json"
@@ -34,6 +34,10 @@ type UserConfig struct {
InboxNotifications bool `json:"inbox_notifications"`
AllNewQuestions bool `json:"all_new_questions"`
NewQuestionsForFollowingTags bool `json:"new_questions_for_following_tags"`
+ UpvotedAnswers bool `json:"upvoted_answers"`
+ DownvotedAnswers bool `json:"downvoted_answers"`
+ UpdatedQuestions bool `json:"updated_questions"`
+ UpdatedAnswers bool `json:"updated_answers"`
}
type UserConfigCache struct {
@@ -97,6 +101,30 @@ func (n *Notification) UserConfigFields() []plugin.ConfigField {
i18n.UserConfigNewQuestionsForFollowingTagsLabel,
i18n.UserConfigNewQuestionsForFollowingTagsDescription,
))
+ fields = append(fields, createSwitchConfig(
+ "upvoted_answers",
+ i18n.UserConfigUpvotedAnswersTitle,
+ i18n.UserConfigUpvotedAnswersLabel,
+ i18n.UserConfigUpvotedAnswersDescription,
+ ))
+ fields = append(fields, createSwitchConfig(
+ "downvoted_answers",
+ i18n.UserConfigDownvotedAnswersTitle,
+ i18n.UserConfigDownvotedAnswersLabel,
+ i18n.UserConfigDownvotedAnswersDescription,
+ ))
+ fields = append(fields, createSwitchConfig(
+ "updated_questions",
+ i18n.UserConfigUpdatedQuestionsTitle,
+ i18n.UserConfigUpdatedQuestionsLabel,
+ i18n.UserConfigUpdatedQuestionsDescription,
+ ))
+ fields = append(fields, createSwitchConfig(
+ "updated_answers",
+ i18n.UserConfigUpdatedAnswersTitle,
+ i18n.UserConfigUpdatedAnswersLabel,
+ i18n.UserConfigUpdatedAnswersDescription,
+ ))
return fields
}
diff --git a/render-markdown-codehighlight/i18n/translation.go b/render-markdown-codehighlight/i18n/translation.go
index ed3059d5..d9a08b54 100644
--- a/render-markdown-codehighlight/i18n/translation.go
+++ b/render-markdown-codehighlight/i18n/translation.go
@@ -17,11 +17,10 @@
* under the License.
*/
- package i18n
+package i18n
- const (
- InfoName = "plugin.render_markdown_codehighlight.backend.info.name"
- InfoDescription = "plugin.render_markdown_codehighlight.backend.info.description"
- ConfigCssFilteringTitle = "plugin.render_markdown_codehighlight.backend.config.css_filtering.title"
- )
-
+const (
+ InfoName = "plugin.render_markdown_codehighlight.backend.info.name"
+ InfoDescription = "plugin.render_markdown_codehighlight.backend.info.description"
+ ConfigCssFilteringTitle = "plugin.render_markdown_codehighlight.backend.config.css_filtering.title"
+)
diff --git a/render-markdown-codehighlight/renderMarkdownCodehighlight.go b/render-markdown-codehighlight/renderMarkdownCodehighlight.go
index 380e0dd4..e97388bd 100644
--- a/render-markdown-codehighlight/renderMarkdownCodehighlight.go
+++ b/render-markdown-codehighlight/renderMarkdownCodehighlight.go
@@ -17,51 +17,51 @@
* under the License.
*/
- package render_markdown_codehighlight
+package render_markdown_codehighlight
- import (
- "embed"
- "encoding/json"
- "log"
- "github.com/gin-gonic/gin"
- "strings"
- "github.com/apache/incubator-answer-plugins/render-markdown-codehighlight/i18n"
- "github.com/apache/incubator-answer-plugins/util"
- "github.com/apache/incubator-answer/plugin"
- )
-
- //go:embed info.yaml
- var Info embed.FS
-
- type Render struct {
- Config *RenderConfig
- }
-
- type RenderConfig struct {
- SelectTheme string `json:"select_theme"`
- }
-
- func init() {
- plugin.Register(&Render{
- Config: &RenderConfig{},
- })
- }
-
- func (r *Render) Info() plugin.Info {
- info := &util.Info{}
- info.GetInfo(Info)
-
- return plugin.Info{
- Name: plugin.MakeTranslator(i18n.InfoName),
- SlugName: info.SlugName,
- Description: plugin.MakeTranslator(i18n.InfoDescription),
- Author: info.Author,
- Version: info.Version,
- Link: info.Link,
- }
- }
+import (
+ "embed"
+ "encoding/json"
+ "github.com/apache/incubator-answer-plugins/render-markdown-codehighlight/i18n"
+ "github.com/apache/incubator-answer-plugins/util"
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/gin-gonic/gin"
+ "log"
+ "strings"
+)
- func (r *Render) ConfigFields() []plugin.ConfigField {
+//go:embed info.yaml
+var Info embed.FS
+
+type Render struct {
+ Config *RenderConfig
+}
+
+type RenderConfig struct {
+ SelectTheme string `json:"select_theme"`
+}
+
+func init() {
+ plugin.Register(&Render{
+ Config: &RenderConfig{},
+ })
+}
+
+func (r *Render) Info() plugin.Info {
+ info := &util.Info{}
+ info.GetInfo(Info)
+
+ return plugin.Info{
+ Name: plugin.MakeTranslator(i18n.InfoName),
+ SlugName: info.SlugName,
+ Description: plugin.MakeTranslator(i18n.InfoDescription),
+ Author: info.Author,
+ Version: info.Version,
+ Link: info.Link,
+ }
+}
+
+func (r *Render) ConfigFields() []plugin.ConfigField {
themeOptions := make([]plugin.ConfigFieldOption, len(ThemeList))
for i, theme := range ThemeList {
@@ -87,20 +87,19 @@
},
}
}
-
- func (r *Render) ConfigReceiver(config []byte) error {
- c := &RenderConfig{}
- _ = json.Unmarshal(config, c)
- r.Config = c
- log.Println("Received theme:", r.Config.SelectTheme)
- return nil
- }
-
- func (r *Render) GetRenderConfig(ctx *gin.Context) (renderConfig *plugin.RenderConfig) {
- log.Println("Current theme:", r.Config.SelectTheme)
- renderConfig = &plugin.RenderConfig{
- SelectTheme: r.Config.SelectTheme,
- }
- return
- }
-
+
+func (r *Render) ConfigReceiver(config []byte) error {
+ c := &RenderConfig{}
+ _ = json.Unmarshal(config, c)
+ r.Config = c
+ log.Println("Received theme:", r.Config.SelectTheme)
+ return nil
+}
+
+func (r *Render) GetRenderConfig(ctx *gin.Context) (renderConfig *plugin.RenderConfig) {
+ log.Println("Current theme:", r.Config.SelectTheme)
+ renderConfig = &plugin.RenderConfig{
+ SelectTheme: r.Config.SelectTheme,
+ }
+ return
+}
diff --git a/render-markdown-codehighlight/theme_list.go b/render-markdown-codehighlight/theme_list.go
index 6051b673..dc16fa05 100644
--- a/render-markdown-codehighlight/theme_list.go
+++ b/render-markdown-codehighlight/theme_list.go
@@ -17,65 +17,64 @@
* under the License.
*/
-
package render_markdown_codehighlight
var ThemeList = []string{
-"1c-light",
- "a11y-all",
- "agate-dark",
- "an-dark",
- "androidstudio-dark",
- "arduino-light",
- "arta-dark",
- "ascetic-light",
- "atom-all",
- "brown-light",
- "codepen-dark",
- "color-light",
- "dark-dark",
- "default-light",
- "devibeans-dark",
- "docco-light",
- "far-dark",
- "felipec-dark",
- "foundation-light",
- "github-all",
- "gml-dark",
- "googlecode-light",
- "gradient-all",
- "grayscale-light",
- "hybrid-dark",
- "idea-light",
- "intellij-light",
- "ir-dark",
- "isbl-all",
- "kimbie-all",
- "lightfair-light",
- "lioshi-dark",
- "magula-light",
- "mono-light",
- "monokai-dark",
- "night-dark",
- "nnfx-all",
- "nord-dark",
- "obsidian-dark",
- "panda-all",
- "paraiso-all",
- "pojoaque-light",
- "purebasic-light",
- "qtcreator-all",
- "rainbow-dark",
- "routeros-light",
- "school-light",
- "shades-dark",
- "srcery-dark",
- "stackoverflow-all",
- "sunburst-dark",
- "tokyo-all",
- "tomorrow-dark",
- "vs-light",
- "vs2015-dark",
- "xcode-light",
- "xt256-dark",
+ "1c-light",
+ "a11y-all",
+ "agate-dark",
+ "an-dark",
+ "androidstudio-dark",
+ "arduino-light",
+ "arta-dark",
+ "ascetic-light",
+ "atom-all",
+ "brown-light",
+ "codepen-dark",
+ "color-light",
+ "dark-dark",
+ "default-light",
+ "devibeans-dark",
+ "docco-light",
+ "far-dark",
+ "felipec-dark",
+ "foundation-light",
+ "github-all",
+ "gml-dark",
+ "googlecode-light",
+ "gradient-all",
+ "grayscale-light",
+ "hybrid-dark",
+ "idea-light",
+ "intellij-light",
+ "ir-dark",
+ "isbl-all",
+ "kimbie-all",
+ "lightfair-light",
+ "lioshi-dark",
+ "magula-light",
+ "mono-light",
+ "monokai-dark",
+ "night-dark",
+ "nnfx-all",
+ "nord-dark",
+ "obsidian-dark",
+ "panda-all",
+ "paraiso-all",
+ "pojoaque-light",
+ "purebasic-light",
+ "qtcreator-all",
+ "rainbow-dark",
+ "routeros-light",
+ "school-light",
+ "shades-dark",
+ "srcery-dark",
+ "stackoverflow-all",
+ "sunburst-dark",
+ "tokyo-all",
+ "tomorrow-dark",
+ "vs-light",
+ "vs2015-dark",
+ "xcode-light",
+ "xt256-dark",
}
diff --git a/reviewer-baidu/README.md b/reviewer-baidu/README.md
new file mode 100644
index 00000000..401c62f7
--- /dev/null
+++ b/reviewer-baidu/README.md
@@ -0,0 +1,12 @@
+# Baidu Reviewer
+
+> Baidu Content Audit Platform is a service platform for intelligent auditing of multimedia content. It can be used to filter spam.
+
+## Config
+
+- `api_key`: Baidu App's API key
+- `secret_key`: Baidu App's Secret Key
+
+## Document
+
+- https://cloud.baidu.com/doc/ANTIPORN/s/Vk3h6xaga
diff --git a/reviewer-baidu/basic.go b/reviewer-baidu/basic.go
new file mode 100644
index 00000000..1baf3935
--- /dev/null
+++ b/reviewer-baidu/basic.go
@@ -0,0 +1,163 @@
+/*
+ * 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 basic
+
+import (
+ "embed"
+ "encoding/json"
+ "github.com/apache/incubator-answer-plugins/util"
+
+ "github.com/apache/incubator-answer-plugins/reviewer-baidu/i18n"
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/lufei/baidu-golang-sdk/aip/censor"
+ myI18n "github.com/segmentfault/pacman/i18n"
+ "github.com/segmentfault/pacman/log"
+)
+
+//go:embed info.yaml
+var Info embed.FS
+
+type Reviewer struct {
+ Config *ReviewerConfig
+}
+
+type ReviewerConfig struct {
+ APIKey string `json:"api_key"`
+ SecretKey string `json:"secret_key"`
+ SpamFiltering string `json:"span_filtering"`
+}
+
+func init() {
+ plugin.Register(&Reviewer{
+ Config: &ReviewerConfig{},
+ })
+}
+
+func (r *Reviewer) Info() plugin.Info {
+ info := &util.Info{}
+ info.GetInfo(Info)
+
+ return plugin.Info{
+ Name: plugin.MakeTranslator(i18n.InfoName),
+ SlugName: info.SlugName,
+ Description: plugin.MakeTranslator(i18n.InfoDescription),
+ Author: info.Author,
+ Version: info.Version,
+ Link: info.Link,
+ }
+}
+
+func (r *Reviewer) Review(content *plugin.ReviewContent) (result *plugin.ReviewResult) {
+ result = &plugin.ReviewResult{Approved: true}
+ if len(r.Config.APIKey) == 0 {
+ return result
+ }
+ // If the author is admin, no need to review
+ if content.Author.Role > 1 {
+ return result
+ }
+
+ client := censor.NewClient(r.Config.APIKey, r.Config.SecretKey)
+ textCensorResult, err := client.TextCensor(content.Title+"\n"+content.Content, content.IP)
+ if err != nil {
+ log.Errorf("Request baidu to check failed: %v", err)
+ return handleReviewError(content, plugin.ReviewStatusNeedReview)
+ }
+
+ var jsonMap map[string]interface{}
+ err = json.Unmarshal([]byte(textCensorResult), &jsonMap)
+ if err != nil {
+ return handleReviewError(content, plugin.ReviewStatusNeedReview)
+ }
+
+ if conclusionType, ok := jsonMap["conclusionType"].(float64); ok {
+ if conclusionType == 1.0 {
+ return result
+ }
+ }
+
+ if r.Config.SpamFiltering == "delete" {
+ return handleReviewError(content, plugin.ReviewStatusDeleteDirectly)
+ }
+
+ return handleReviewError(content, plugin.ReviewStatusNeedReview)
+}
+
+func (r *Reviewer) ConfigFields() []plugin.ConfigField {
+ return []plugin.ConfigField{
+ {
+ Name: "api_key",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigAPIKeyLabel),
+ Description: plugin.MakeTranslator(i18n.ConfigAPIKeyDescription),
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ Label: plugin.MakeTranslator(i18n.ConfigAPIKeyLabel),
+ },
+ Value: r.Config.APIKey,
+ },
+ {
+ Name: "secret_key",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigSecretKeyTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigSecretKeyDescription),
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ Label: plugin.MakeTranslator(i18n.ConfigSecretKeyLabel),
+ },
+ Value: r.Config.SecretKey,
+ },
+ {
+ Name: "span_filtering",
+ Type: plugin.ConfigTypeSelect,
+ Title: plugin.MakeTranslator(i18n.ConfigSpanFilteringTitle),
+ Required: false,
+ UIOptions: plugin.ConfigFieldUIOptions{},
+ Value: r.Config.SpamFiltering,
+ Options: []plugin.ConfigFieldOption{
+ {
+ Value: "review",
+ Label: plugin.MakeTranslator(i18n.ConfigSpanFilteringReview),
+ },
+ {
+ Value: "delete",
+ Label: plugin.MakeTranslator(i18n.ConfigSpanFilteringDelete),
+ },
+ },
+ },
+ }
+}
+
+func handleReviewError(content *plugin.ReviewContent, ReviewStatus plugin.ReviewStatus) *plugin.ReviewResult {
+ return &plugin.ReviewResult{
+ Approved: false,
+ ReviewStatus: ReviewStatus,
+ Reason: plugin.TranslateWithData(myI18n.Language(content.Language), i18n.CommentNeedReview, nil),
+ }
+}
+
+func (r *Reviewer) ConfigReceiver(config []byte) error {
+ c := &ReviewerConfig{}
+ _ = json.Unmarshal(config, c)
+ r.Config = c
+ return nil
+}
diff --git a/reviewer-baidu/go.mod b/reviewer-baidu/go.mod
new file mode 100644
index 00000000..d1fc5500
--- /dev/null
+++ b/reviewer-baidu/go.mod
@@ -0,0 +1,48 @@
+module github.com/apache/incubator-answer-plugins/reviewer-baidu
+
+go 1.21.3
+
+require (
+ github.com/apache/incubator-answer v1.4.0
+ github.com/apache/incubator-answer-plugins/util v1.0.2
+ github.com/lufei/baidu-golang-sdk v0.0.0-20241007032158-d85deddc0d61
+ github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f
+)
+
+require (
+ github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect
+ github.com/aymerick/douceur v0.2.0 // indirect
+ github.com/bytedance/sonic v1.9.1 // indirect
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/gin-gonic/gin v1.9.1 // 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.14.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/google/go-cmp v0.5.9 // indirect
+ github.com/google/wire v0.5.0 // indirect
+ github.com/gorilla/css v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.4 // indirect
+ github.com/kr/text v0.1.0 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/microcosm-cc/bluemonday v1.0.21 // 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.0.8 // indirect
+ github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.11 // indirect
+ golang.org/x/arch v0.3.0 // indirect
+ golang.org/x/crypto v0.21.0 // indirect
+ golang.org/x/net v0.21.0 // indirect
+ golang.org/x/sys v0.18.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ google.golang.org/protobuf v1.30.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ sigs.k8s.io/yaml v1.3.0 // indirect
+)
diff --git a/reviewer-baidu/go.sum b/reviewer-baidu/go.sum
new file mode 100644
index 00000000..a22f1ed4
--- /dev/null
+++ b/reviewer-baidu/go.sum
@@ -0,0 +1,150 @@
+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.0 h1:W3y4TAQ4sdzgcqntGqNBPe0BdyeW7+l8FWYBDs9g8+Y=
+github.com/apache/incubator-answer v1.4.0/go.mod h1:Q4NkQmBd0sV7t3Cd8NBsWh9w8jFRo/2qjzOw9MlRNwk=
+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.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/lufei/baidu-golang-sdk v0.0.0-20241007032158-d85deddc0d61 h1:kO3vkpA7fDnLsndVhYxZVcCIQRQqmxYuhINy4rtDGus=
+github.com/lufei/baidu-golang-sdk v0.0.0-20241007032158-d85deddc0d61/go.mod h1:UBdQbJ7zwTRkSaKOG8QDRrLjztTPGG+w8uIc8FH0J3I=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
+github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
+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.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/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-20230516093754-b76aef1c1150 h1:OEuW1D7RGDE0CZDr0oGMw9Eiq7fAbD9C4WMrvSixamk=
+github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150/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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+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.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+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.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+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=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+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=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/reviewer-baidu/i18n/en_US.yaml b/reviewer-baidu/i18n/en_US.yaml
new file mode 100644
index 00000000..aca0dbda
--- /dev/null
+++ b/reviewer-baidu/i18n/en_US.yaml
@@ -0,0 +1,49 @@
+# 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:
+ baidu_reviewer:
+ backend:
+ info:
+ name:
+ other: Baidu Anti-Spam
+ description:
+ other: Baidu Anti-Spam is used to check the content of posts and comments against the Baidu web service to see if they look like spam.
+ config:
+ api_key:
+ title:
+ other: API Key
+ label:
+ other: Enter your API key
+ description:
+ other: Your API key. Manage your API keys.
+ secret_key:
+ title:
+ other: Secret Key
+ label:
+ other: Enter your Secret key
+ description:
+ other: Your Secret key. Manage your API keys.
+ span_filtering:
+ title:
+ other: Span Filtering
+ review:
+ other: Put spam in the review queue
+ delete:
+ other: Silently delete the spam
+ comment:
+ need_review: Needs approval.
diff --git a/reviewer-baidu/i18n/translation.go b/reviewer-baidu/i18n/translation.go
new file mode 100644
index 00000000..b2cf1ce1
--- /dev/null
+++ b/reviewer-baidu/i18n/translation.go
@@ -0,0 +1,36 @@
+/*
+ * 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.baidu_reviewer.backend.info.name"
+ InfoDescription = "plugin.baidu_reviewer.backend.info.description"
+ ConfigAPIKeyLabel = "plugin.baidu_reviewer.backend.config.api_key.label"
+ ConfigAPIKeyTitle = "plugin.baidu_reviewer.backend.config.api_key.title"
+ ConfigAPIKeyDescription = "plugin.baidu_reviewer.backend.config.api_key.description"
+ ConfigSecretKeyLabel = "plugin.baidu_reviewer.backend.config.secret_key.label"
+ ConfigSecretKeyTitle = "plugin.baidu_reviewer.backend.config.secret_key.title"
+ ConfigSecretKeyDescription = "plugin.baidu_reviewer.backend.config.secret_key.description"
+ CommentNeedReview = "plugin.baidu_reviewer.backend.comment.need_review"
+ ConfigSpanFilteringTitle = "plugin.baidu_reviewer.backend.config.span_filtering.title"
+ ConfigSpanFilteringLabel = "plugin.baidu_reviewer.backend.config.span_filtering.label"
+ ConfigSpanFilteringReview = "plugin.baidu_reviewer.backend.config.span_filtering.review"
+ ConfigSpanFilteringDelete = "plugin.baidu_reviewer.backend.config.span_filtering.delete"
+)
diff --git a/reviewer-baidu/i18n/zh_CN.yaml b/reviewer-baidu/i18n/zh_CN.yaml
new file mode 100644
index 00000000..1d319845
--- /dev/null
+++ b/reviewer-baidu/i18n/zh_CN.yaml
@@ -0,0 +1,49 @@
+# 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:
+ baidu_reviewer:
+ backend:
+ info:
+ name:
+ other: 百度反垃圾
+ description:
+ other: 百度反垃圾用于检查帖子和评论内容,通过百度内容审核平台查看它们是否像垃圾内容。
+ config:
+ api_key:
+ title:
+ other: API Key
+ label:
+ other: 输入您的 API Key
+ description:
+ other: 您的 API Key。创建应用后查看您的 API 密钥信息。
+ secret_key:
+ title:
+ other: Secret Key
+ label:
+ other: 输入您的 Secret key
+ description:
+ other: 您的 Secret key。创建应用后查看您的 API 密钥信息。
+ span_filtering:
+ title:
+ other: 垃圾内容过滤
+ review:
+ other: 将垃圾内容放入审核队列
+ delete:
+ other: 静默删除垃圾内容
+ comment:
+ need_review: 需要批准。
diff --git a/reviewer-baidu/info.yaml b/reviewer-baidu/info.yaml
new file mode 100644
index 00000000..fd5cf7ad
--- /dev/null
+++ b/reviewer-baidu/info.yaml
@@ -0,0 +1,22 @@
+# 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.
+
+slug_name: baidu_reviewer
+type: reviewer
+version: 1.0.0
+author: Luffy
+link: https://github.com/apache/incubator-answer-plugins/tree/main/reviewer-baidu
diff --git a/storage-aliyunoss/README.md b/storage-aliyunoss/README.md
index f3cde2d1..e98a7d59 100644
--- a/storage-aliyunoss/README.md
+++ b/storage-aliyunoss/README.md
@@ -9,7 +9,7 @@
```
### Configuration
-- `Endpoint` - Endpoint of AliCloud OSS storage, such as oss-cn-hangzhou.aliyuncs.com
+- `Endpoint` - Endpoint of AliCloud OSS storage, such as oss-cn-hangzhou.aliyuncs.com
- `Bucket Name` - Your bucket name
- `Object Key Prefix` - Prefix of the object key like 'answer/data/' that ending with '/'
- `Access Key Id` - AccessKeyID of the AliCloud OSS storage
diff --git a/storage-aliyunoss/aliyunoss.go b/storage-aliyunoss/aliyunoss.go
index 66a065ef..af132ca3 100644
--- a/storage-aliyunoss/aliyunoss.go
+++ b/storage-aliyunoss/aliyunoss.go
@@ -89,14 +89,14 @@ func (s *Storage) UploadFile(ctx *plugin.GinContext, source plugin.UploadSource)
bucket, err := client.Bucket(s.Config.BucketName)
if err != nil {
- resp.OriginalError = fmt.Errorf("create oss client failed: %v", err)
+ resp.OriginalError = fmt.Errorf("get bucket failed: %v", err)
resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrMisStorageConfig)
return resp
}
file, err := ctx.FormFile("file")
if err != nil {
- resp.OriginalError = fmt.Errorf("get bucket failed: %v", err)
+ resp.OriginalError = fmt.Errorf("get upload file failed: %v", err)
resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrFileNotFound)
return resp
}
@@ -128,7 +128,7 @@ func (s *Storage) UploadFile(ctx *plugin.GinContext, source plugin.UploadSource)
}
respBody, err := bucket.DoPutObject(request, nil)
if err != nil {
- resp.OriginalError = fmt.Errorf("get file failed: %v", err)
+ resp.OriginalError = fmt.Errorf("upload file failed: %v", err)
resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrUploadFileFailed)
return resp
}
diff --git a/storage-s3/s3.go b/storage-s3/s3.go
index 83b55456..93b0e428 100644
--- a/storage-s3/s3.go
+++ b/storage-s3/s3.go
@@ -86,7 +86,7 @@ func (s *Storage) UploadFile(ctx *plugin.GinContext, source plugin.UploadSource)
file, err := ctx.FormFile("file")
if err != nil {
- resp.OriginalError = fmt.Errorf("get bucket failed: %v", err)
+ resp.OriginalError = fmt.Errorf("get upload file failed: %v", err)
resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrFileNotFound)
return resp
}
@@ -114,7 +114,7 @@ func (s *Storage) UploadFile(ctx *plugin.GinContext, source plugin.UploadSource)
objectKey := s.createObjectKey(file.Filename, source)
err = s.Client.PutObject(objectKey, strings.ToLower(filepath.Ext(file.Filename)), openFile)
if err != nil {
- resp.OriginalError = fmt.Errorf("get file failed: %v", err)
+ resp.OriginalError = fmt.Errorf("upload file failed: %v", err)
resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrUploadFileFailed)
return resp
}
diff --git a/storage-tencentyuncos/README.md b/storage-tencentyuncos/README.md
new file mode 100644
index 00000000..013fc10e
--- /dev/null
+++ b/storage-tencentyuncos/README.md
@@ -0,0 +1,21 @@
+# Tencent COS Storage (preview)
+
+> This plugin can be used to store attachments and avatars to Tencent COS.
+
+## How to use
+
+### Build
+
+```bash
+./answer build --with github.com/apache/incubator-answer-plugins/storage-tencentyuncos
+```
+
+### Configuration
+
+- `Region` - Region of Tencent COS storage, like 'ap-beijing'
+- `Bucket Name` - Your bucket name
+- `Object Key Prefix` - Prefix of the object key like 'answer/data/' that ending with '/'
+- `Secret ID` - Secret ID of the Tencent COS storage
+- `Secret Key` - Secret Key of the Tencent COS storage
+- `Visit Url Prefix` - Prefix of access address for the uploaded file, ending with '/' such as https://example.com/xxx/
+- `Max File Size` - Max file size in MB, default is 10MB
diff --git a/storage-tencentyuncos/go.mod b/storage-tencentyuncos/go.mod
new file mode 100644
index 00000000..55f64dcf
--- /dev/null
+++ b/storage-tencentyuncos/go.mod
@@ -0,0 +1,51 @@
+module github.com/apache/incubator-answer-plugins/tencentyuncos
+
+go 1.21.3
+
+require (
+ github.com/apache/incubator-answer v1.4.0
+ github.com/apache/incubator-answer-plugins/util v1.0.2
+ github.com/tencentyun/cos-go-sdk-v5 v0.7.55
+)
+
+require (
+ github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect
+ github.com/aymerick/douceur v0.2.0 // indirect
+ github.com/bytedance/sonic v1.9.1 // indirect
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+ github.com/clbanning/mxj v1.8.4 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/gin-gonic/gin v1.9.1 // 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.14.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/google/go-querystring v1.0.0 // indirect
+ github.com/google/wire v0.5.0 // indirect
+ github.com/gorilla/css v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.4 // indirect
+ github.com/kr/text v0.2.0 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/microcosm-cc/bluemonday v1.0.21 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mozillazg/go-httpheader v0.2.1 // indirect
+ github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+ github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f // indirect
+ github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.11 // indirect
+ golang.org/x/arch v0.3.0 // indirect
+ golang.org/x/crypto v0.21.0 // indirect
+ golang.org/x/net v0.21.0 // indirect
+ golang.org/x/sys v0.18.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ google.golang.org/protobuf v1.30.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ sigs.k8s.io/yaml v1.3.0 // indirect
+)
diff --git a/storage-tencentyuncos/go.sum b/storage-tencentyuncos/go.sum
new file mode 100644
index 00000000..2fa1db75
--- /dev/null
+++ b/storage-tencentyuncos/go.sum
@@ -0,0 +1,166 @@
+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/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
+github.com/apache/incubator-answer v1.4.0 h1:W3y4TAQ4sdzgcqntGqNBPe0BdyeW7+l8FWYBDs9g8+Y=
+github.com/apache/incubator-answer v1.4.0/go.mod h1:Q4NkQmBd0sV7t3Cd8NBsWh9w8jFRo/2qjzOw9MlRNwk=
+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.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
+github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
+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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+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/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+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.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+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.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+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.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
+github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
+github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+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/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
+github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
+github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/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-20230516093754-b76aef1c1150 h1:OEuW1D7RGDE0CZDr0oGMw9Eiq7fAbD9C4WMrvSixamk=
+github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150/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.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.55 h1:9DfH3umWUd0I2jdqcUxrU1kLfUPOydULNy4T9qN5PF8=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.55/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
+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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+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.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+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.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+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=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+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=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/storage-tencentyuncos/i18n/en_US.yaml b/storage-tencentyuncos/i18n/en_US.yaml
new file mode 100644
index 00000000..e3ae90dc
--- /dev/null
+++ b/storage-tencentyuncos/i18n/en_US.yaml
@@ -0,0 +1,72 @@
+# 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:
+ tencentyuncos_storage:
+ backend:
+ info:
+ name:
+ other: Tencent COS storage
+ description:
+ other: Upload files to Tencent COS storage
+ config:
+ region:
+ title:
+ other: Region
+ description:
+ other: Region of Tencent COS storage, like 'ap-beijing'
+ bucket_name:
+ title:
+ other: Bucket name
+ description:
+ other: Bucket name of Tencent COS storage
+ object_key_prefix:
+ title:
+ other: Object Key prefix
+ description:
+ other: prefix of the object key like 'answer/data/' that ending with '/'
+ secret_id:
+ title:
+ other: SecretID
+ description:
+ other: SecretID of the Tencent COS storage
+ secret_key:
+ title:
+ other: SecretKey
+ description:
+ other: SecretKey of the Tencent COS storage
+ visit_url_prefix:
+ title:
+ other: Access URL prefix
+ description:
+ other: prefix of the final access address of the uploaded file, ending with '/' https://example.com/xxx/
+ max_file_size:
+ title:
+ other: Maximum file size(MB)
+ description:
+ other: Limit the maximum size of uploaded files, in MB, default is 10MB
+ err:
+ mis_storage_config:
+ other: Wrong storage configuration causes upload failure.
+ file_not_found:
+ other: File not found.
+ unsupported_file_type:
+ other: Unsupported file type.
+ over_file_size_limit:
+ other: File size limit exceeded.
+ upload_file_failed:
+ other: Failed to upload a file.
diff --git a/storage-tencentyuncos/i18n/translation.go b/storage-tencentyuncos/i18n/translation.go
new file mode 100644
index 00000000..2664a8c4
--- /dev/null
+++ b/storage-tencentyuncos/i18n/translation.go
@@ -0,0 +1,46 @@
+/*
+ * 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.tencentyuncos_storage.backend.info.name"
+ InfoDescription = "plugin.tencentyuncos_storage.backend.info.description"
+
+ ConfigRegionTitle = "plugin.tencentyuncos_storage.backend.config.region.title"
+ ConfigRegionDescription = "plugin.tencentyuncos_storage.backend.config.region.description"
+ ConfigBucketNameTitle = "plugin.tencentyuncos_storage.backend.config.bucket_name.title"
+ ConfigBucketNameDescription = "plugin.tencentyuncos_storage.backend.config.bucket_name.description"
+ ConfigObjectKeyPrefixTitle = "plugin.tencentyuncos_storage.backend.config.object_key_prefix.title"
+ ConfigObjectKeyPrefixDescription = "plugin.tencentyuncos_storage.backend.config.object_key_prefix.description"
+ ConfigSecretIdTitle = "plugin.tencentyuncos_storage.backend.config.secret_id.title"
+ ConfigSecretIdDescription = "plugin.tencentyuncos_storage.backend.config.secret_id.description"
+ ConfigSecretKeyTitle = "plugin.tencentyuncos_storage.backend.config.secret_key.title"
+ ConfigSecretKeyDescription = "plugin.tencentyuncos_storage.backend.config.secret_key.description"
+ ConfigVisitUrlPrefixTitle = "plugin.tencentyuncos_storage.backend.config.visit_url_prefix.title"
+ ConfigVisitUrlPrefixDescription = "plugin.tencentyuncos_storage.backend.config.visit_url_prefix.description"
+ ConfigMaxFileSizeTitle = "plugin.tencentyuncos_storage.backend.config.max_file_size.title"
+ ConfigMaxFileSizeDescription = "plugin.tencentyuncos_storage.backend.config.max_file_size.description"
+
+ ErrMisStorageConfig = "plugin.tencentyuncos_storage.backend.err.mis_storage_config"
+ ErrFileNotFound = "plugin.tencentyuncos_storage.backend.err.file_not_found"
+ ErrUnsupportedFileType = "plugin.tencentyuncos_storage.backend.err.unsupported_file_type"
+ ErrOverFileSizeLimit = "plugin.tencentyuncos_storage.backend.err.over_file_size_limit"
+ ErrUploadFileFailed = "plugin.tencentyuncos_storage.backend.err.upload_file_failed"
+)
diff --git a/storage-tencentyuncos/i18n/zh_CN.yaml b/storage-tencentyuncos/i18n/zh_CN.yaml
new file mode 100644
index 00000000..280aa19d
--- /dev/null
+++ b/storage-tencentyuncos/i18n/zh_CN.yaml
@@ -0,0 +1,72 @@
+# 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:
+ tencentyuncos_storage:
+ backend:
+ info:
+ name:
+ other: 腾讯云COS对象存储
+ description:
+ other: 上传文件到腾讯云COS对象存储
+ config:
+ region:
+ title:
+ other: 所属地域
+ description:
+ other: 存储桶的所属地域,如'ap-beijing'
+ bucket_name:
+ title:
+ other: 存储桶名称
+ description:
+ other: 存储桶的名称,如'example-1234567890'
+ object_key_prefix:
+ title:
+ other: 对象Key前缀
+ description:
+ other: 对象键的前缀,如'answer/data/',以'/'结尾
+ secret_id:
+ title:
+ other: SecretID
+ description:
+ other: 腾讯云COS对象存储的SecretID,在 https://console.cloud.tencent.com/cam/capi 获取
+ secret_key:
+ title:
+ other: SecretKey
+ description:
+ other: 腾讯云COS对象存储的SecretKey
+ visit_url_prefix:
+ title:
+ other: 访问URL前缀
+ description:
+ other: 上传文件最终访问地址的前缀,以 '/' 结尾 https://example.com/xxx/
+ max_file_size:
+ title:
+ other: 最大文件大小(MB)
+ description:
+ other: 限制上传文件的最大大小,单位为MB,默认为 10MB
+ err:
+ mis_storage_config:
+ other: 错误的存储配置导致上传失败
+ file_not_found:
+ other: 文件未找到
+ unsupported_file_type:
+ other: 不支持的文件类型
+ over_file_size_limit:
+ other: 超过文件大小限制
+ upload_file_failed:
+ other: 上传文件失败
diff --git a/storage-tencentyuncos/info.yaml b/storage-tencentyuncos/info.yaml
new file mode 100644
index 00000000..5ee9905f
--- /dev/null
+++ b/storage-tencentyuncos/info.yaml
@@ -0,0 +1,22 @@
+# 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.
+
+slug_name: tencentyuncos_storage
+type: storage
+version: 1.0.0
+author: Luffy
+link: https://github.com/apache/incubator-answer-plugins/tree/main/storage-tencentyuncos
diff --git a/storage-tencentyuncos/tencentyuncos.go b/storage-tencentyuncos/tencentyuncos.go
new file mode 100644
index 00000000..f2273eef
--- /dev/null
+++ b/storage-tencentyuncos/tencentyuncos.go
@@ -0,0 +1,267 @@
+/*
+ * 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 tencentyuncos
+
+import (
+ "crypto/rand"
+ "embed"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-answer-plugins/util"
+ "net/http"
+ "net/url"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/apache/incubator-answer-plugins/storage-tencentyuncos/i18n"
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/tencentyun/cos-go-sdk-v5"
+)
+
+//go:embed info.yaml
+var Info embed.FS
+
+const (
+ // 10MB
+ defaultMaxFileSize int64 = 10 * 1024 * 1024
+)
+
+type Storage struct {
+ Config *StorageConfig
+}
+
+type StorageConfig struct {
+ Region string `json:"region"`
+ BucketName string `json:"bucket_name"`
+ ObjectKeyPrefix string `json:"object_key_prefix"`
+ SecretID string `json:"secret_id"`
+ SecretKey string `json:"secret_key"`
+ VisitUrlPrefix string `json:"visit_url_prefix"`
+ MaxFileSize string `json:"max_file_size"`
+}
+
+func init() {
+ plugin.Register(&Storage{
+ Config: &StorageConfig{},
+ })
+}
+
+func (s *Storage) Info() plugin.Info {
+ info := &util.Info{}
+ info.GetInfo(Info)
+
+ return plugin.Info{
+ Name: plugin.MakeTranslator(i18n.InfoName),
+ SlugName: info.SlugName,
+ Description: plugin.MakeTranslator(i18n.InfoDescription),
+ Author: info.Author,
+ Version: info.Version,
+ Link: info.Link,
+ }
+}
+
+func (s *Storage) UploadFile(ctx *plugin.GinContext, source plugin.UploadSource) (resp plugin.UploadFileResponse) {
+ resp = plugin.UploadFileResponse{}
+
+ BucketURL, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", s.Config.BucketName, s.Config.Region))
+ BaseURL := &cos.BaseURL{BucketURL: BucketURL}
+ client := cos.NewClient(BaseURL, &http.Client{
+ Transport: &cos.AuthorizationTransport{
+ SecretID: s.Config.SecretID,
+ SecretKey: s.Config.SecretKey,
+ },
+ })
+
+ _, err := client.Bucket.IsExist(ctx)
+ if err != nil {
+ resp.OriginalError = fmt.Errorf("head bucket failed: %v", err)
+ resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrMisStorageConfig)
+ return resp
+ }
+
+ file, err := ctx.FormFile("file")
+ if err != nil {
+ resp.OriginalError = fmt.Errorf("get upload file failed: %v", err)
+ resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrFileNotFound)
+ return resp
+ }
+
+ if !s.CheckFileType(file.Filename, source) {
+ resp.OriginalError = fmt.Errorf("file type not allowed")
+ resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrUnsupportedFileType)
+ return resp
+ }
+
+ if file.Size > s.maxFileSizeLimit() {
+ resp.OriginalError = fmt.Errorf("file size too large")
+ resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrOverFileSizeLimit)
+ return resp
+ }
+
+ openFile, err := file.Open()
+ if err != nil {
+ resp.OriginalError = fmt.Errorf("get file failed: %v", err)
+ resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrFileNotFound)
+ return resp
+ }
+ defer openFile.Close()
+
+ objectKey := s.createObjectKey(file.Filename, source)
+ _, err = client.Object.Put(ctx, objectKey, openFile, nil)
+ if err != nil {
+ resp.OriginalError = fmt.Errorf("upload file failed: %v", err)
+ resp.DisplayErrorMsg = plugin.MakeTranslator(i18n.ErrUploadFileFailed)
+ return resp
+ }
+ resp.FullURL = s.Config.VisitUrlPrefix + objectKey
+ return resp
+}
+
+func (s *Storage) createObjectKey(originalFilename string, source plugin.UploadSource) string {
+ ext := strings.ToLower(filepath.Ext(originalFilename))
+ randomString := s.randomObjectKey()
+ switch source {
+ case plugin.UserAvatar:
+ return s.Config.ObjectKeyPrefix + "avatar/" + randomString + ext
+ case plugin.UserPost:
+ return s.Config.ObjectKeyPrefix + "post/" + randomString + ext
+ case plugin.AdminBranding:
+ return s.Config.ObjectKeyPrefix + "branding/" + randomString + ext
+ default:
+ return s.Config.ObjectKeyPrefix + "other/" + randomString + ext
+ }
+}
+
+func (s *Storage) randomObjectKey() string {
+ bytes := make([]byte, 4)
+ _, _ = rand.Read(bytes)
+ return fmt.Sprintf("%d", time.Now().UnixNano()) + hex.EncodeToString(bytes)
+}
+
+func (s *Storage) CheckFileType(originalFilename string, source plugin.UploadSource) bool {
+ ext := strings.ToLower(filepath.Ext(originalFilename))
+ if _, ok := plugin.DefaultFileTypeCheckMapping[source][ext]; ok {
+ return true
+ }
+ return false
+}
+
+func (s *Storage) maxFileSizeLimit() int64 {
+ if len(s.Config.MaxFileSize) == 0 {
+ return defaultMaxFileSize
+ }
+ limit, _ := strconv.Atoi(s.Config.MaxFileSize)
+ if limit <= 0 {
+ return defaultMaxFileSize
+ }
+ return int64(limit) * 1024 * 1024
+}
+
+func (s *Storage) ConfigFields() []plugin.ConfigField {
+ return []plugin.ConfigField{
+ {
+ Name: "region",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigRegionTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigRegionDescription),
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: s.Config.Region,
+ },
+ {
+ Name: "bucket_name",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigBucketNameTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigBucketNameDescription),
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: s.Config.BucketName,
+ },
+ {
+ Name: "object_key_prefix",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigObjectKeyPrefixTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigObjectKeyPrefixDescription),
+ Required: false,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: s.Config.ObjectKeyPrefix,
+ },
+ {
+ Name: "secret_id",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigSecretIdTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigSecretIdDescription),
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: s.Config.SecretID,
+ },
+ {
+ Name: "secret_key",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigSecretKeyTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigSecretKeyDescription),
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: s.Config.SecretKey,
+ },
+ {
+ Name: "visit_url_prefix",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigVisitUrlPrefixTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigVisitUrlPrefixDescription),
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: s.Config.VisitUrlPrefix,
+ },
+ {
+ Name: "max_file_size",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigMaxFileSizeTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigMaxFileSizeDescription),
+ Required: false,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeNumber,
+ },
+ Value: s.Config.MaxFileSize,
+ },
+ }
+}
+
+func (s *Storage) ConfigReceiver(config []byte) error {
+ c := &StorageConfig{}
+ _ = json.Unmarshal(config, c)
+ s.Config = c
+ return nil
+}
diff --git a/user-center-slack/README.md b/user-center-slack/README.md
new file mode 100644
index 00000000..ec25cf9d
--- /dev/null
+++ b/user-center-slack/README.md
@@ -0,0 +1,38 @@
+# Slack User Center
+
+## Feature
+
+- User login via slack Account
+
+## Config
+
+To use this plugin, you need to create [a Slack App](https://api.slack.com/quickstart) first, set the Scope and Redirect URL correctly, and copy the `Client ID`, `Client Secrect`, `Signing Secret` and `Webhook URL`. To activate the Slash Command function, you also need to set the `slash command` in your app. Here are default settings you can try:
+
+> Scope: chat:write, commands, groups:write, im:write, incoming-webhook, mpim:write, users:read, users:read.email
+>
+> RedirectURL: https://Your_Site_URL/answer/api/v1/user-center/login/callback
+>
+> Slash command:
+>
+> * Command: /ask
+> * Requesti URL: https://Your_Site_URL/answer/api/v1/slack/slash
+> * Usage Hint: [Title][Content\][Tag1,Tag2...\]
+
+
+
+- `Client ID`: Slack App Client ID
+
+- `Client Secret`: Slack App Secret
+
+- `Signing Secret`: Slack App Signing Secret
+
+- `Webhook URL`: find in the `Incoming Webhooks` feature, such as `https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX`
+
+
+Note: A Redirect URL must also use HTTPS. You can configure a Redirect URL and scope in the **App Management** page under **OAuth & Permissions**.
+
+## Document
+- https://api.slack.com/quickstart
+- https://api.slack.com/authentication/oauth-v2
+- https://api.slack.com/messaging/webhooks
+- https://api.slack.com/interactivity/slash-commands
\ No newline at end of file
diff --git a/user-center-slack/client.go b/user-center-slack/client.go
new file mode 100644
index 00000000..4ff54c3d
--- /dev/null
+++ b/user-center-slack/client.go
@@ -0,0 +1,195 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/go-resty/resty/v2"
+ "github.com/segmentfault/pacman/log"
+)
+
+type SlackClient struct {
+ AccessToken string
+ ClientID string
+ ClientSecret string
+ RedirectURI string
+ AuthedUserID string
+
+ UserInfoMapping map[string]*UserInfo
+ ChannelMapping string
+}
+
+func NewSlackClient(clientID, clientSecret string) *SlackClient {
+ return &SlackClient{
+ ClientID: clientID,
+ ClientSecret: clientSecret,
+ }
+}
+
+// OAuthV2ResponseTeam
+type OAuthV2ResponseTeam struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+}
+
+// OAuthResponseIncomingWebhook
+type OAuthResponseIncomingWebhook struct {
+ URL string `json:"url"`
+ Channel string `json:"channel"`
+ ChannelID string `json:"channel_id,omitempty"`
+ ConfigurationURL string `json:"configuration_url"`
+}
+
+// OAuthV2ResponseEnterprise
+type OAuthV2ResponseEnterprise struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+}
+
+// OAuthV2ResponseAuthedUser
+type OAuthV2ResponseAuthedUser struct {
+ ID string `json:"id"`
+ Scope string `json:"scope"`
+ AccessToken string `json:"access_token"`
+ ExpiresIn int `json:"expires_in"`
+ RefreshToken string `json:"refresh_token"`
+ TokenType string `json:"token_type"`
+}
+
+type TokenResponse struct {
+ AccessToken string `json:"access_token"`
+ TokenType string `json:"token_type"`
+ Scope string `json:"scope"`
+ BotUserID string `json:"bot_user_id"`
+ AppID string `json:"app_id"`
+ Team OAuthV2ResponseTeam `json:"team"`
+ IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"`
+ Enterprise OAuthV2ResponseEnterprise `json:"enterprise"`
+ IsEnterpriseInstall bool `json:"is_enterprise_install"`
+ AuthedUser OAuthV2ResponseAuthedUser `json:"authed_user"`
+ RefreshToken string `json:"refresh_token"`
+ ExpiresIn int `json:"expires_in"`
+ Error string `json:"error,omitempty"`
+}
+
+type Member struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ TeamID string `json:"team_id"`
+}
+
+// ExchangeCodeForUser through OAuthToken
+func (sc *SlackClient) AuthUser(code string) (info *UserInfo, err error) {
+ clientID := sc.ClientID
+ clientSecret := sc.ClientSecret
+ redirectURI := fmt.Sprintf("%s/answer/api/v1/user-center/login/callback", plugin.SiteURL())
+
+ data := url.Values{}
+ data.Set("code", code)
+ data.Set("client_id", clientID)
+ data.Set("client_secret", clientSecret)
+ data.Set("redirect_uri", redirectURI)
+
+ resp, err := http.PostForm("https://slack.com/api/oauth.v2.access", data)
+ if err != nil {
+ log.Errorf("Failed to exchange code for token: %v", err)
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to read response body: %v", err)
+ }
+
+ var tokenResp TokenResponse
+ err = json.Unmarshal([]byte(body), &tokenResp)
+ if err != nil {
+ fmt.Println("Error parsing response:", err)
+ return nil, err
+ }
+
+ if tokenResp.Error != "" {
+ return nil, fmt.Errorf("Slack API error in AuthUser: %s", tokenResp.Error)
+ }
+
+ sc.AccessToken = tokenResp.AccessToken
+ sc.AuthedUserID = tokenResp.AuthedUser.ID
+
+ return sc.GetUserDetailInfo(sc.AuthedUserID)
+}
+
+func (sc *SlackClient) GetUserDetailInfo(userid string) (info *UserInfo, err error) {
+ getUserInfoResp, err := resty.New().R().
+ SetHeader("Authorization", fmt.Sprintf("Bearer %s", sc.AccessToken)).
+ SetHeader("Accept", "application/json").
+ Get("https://slack.com/api/users.info?user=" + userid)
+ if err != nil {
+ log.Errorf("Failed to get user info: %v", err)
+ return nil, err
+ }
+
+ var authUserResp *AuthUserResp
+ err = json.Unmarshal([]byte(getUserInfoResp.String()), &authUserResp)
+ if err != nil {
+ log.Errorf("Error unmarshaling user info: %v", err)
+ return nil, err
+ }
+ if !authUserResp.Ok {
+ log.Errorf("Failed to get valid user info, Slack API error: %s", authUserResp.Errmsg)
+ return nil, fmt.Errorf("Get user info failed: %s", authUserResp.Errmsg)
+ }
+ log.Debugf("Get user info for UserID: %s", userid)
+
+ if authUserResp.User == nil {
+ log.Errorf("No user data available in the response")
+ return nil, fmt.Errorf("No user data available in the response")
+ }
+
+ authUserResp.User.IsAvailable = true
+ authUserResp.User.Status = 1
+
+ // Directly returning the user data parsed from the response
+ return authUserResp.User, nil
+}
+
+func (sc *SlackClient) UpdateUserInfo() (err error) {
+ log.Debug("Try to update slack client")
+
+ userInfo, err := sc.GetUserDetailInfo(sc.AuthedUserID)
+ if err != nil {
+ log.Errorf("Failed to update user info: %v", err)
+ return err
+ }
+
+ if sc.UserInfoMapping == nil {
+ sc.UserInfoMapping = make(map[string]*UserInfo)
+ }
+ sc.UserInfoMapping[sc.AuthedUserID] = userInfo
+ log.Infof("Updated user info for UserID: %s", sc.AuthedUserID)
+
+ return nil
+}
diff --git a/user-center-slack/config.go b/user-center-slack/config.go
new file mode 100644
index 00000000..f1be5e67
--- /dev/null
+++ b/user-center-slack/config.go
@@ -0,0 +1,153 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "encoding/json"
+ "time"
+
+ "github.com/apache/incubator-answer-plugins/user-center-slack/i18n"
+ "github.com/apache/incubator-answer/plugin"
+)
+
+type UserCenterConfig struct {
+ ClientID string `json:"client_id"` // Slack Client ID
+ ClientSecret string `json:"client_secret"` // Slack Client Secret
+ RedirectURI string `json:"redirect_uri"` // OAuth Redirect URI
+ SigningSecret string `json:"signing_secret"` // Slack Signing Secret
+ AutoSync bool `json:"auto_sync"` // Auto sync
+ Notification bool `json:"notification"` // Notification
+}
+
+func NewSlackClientWithConfig(clientID, clientSecret, redirectURI string) *SlackClient {
+ return &SlackClient{
+ ClientID: clientID,
+ ClientSecret: clientSecret,
+ RedirectURI: redirectURI,
+ }
+}
+
+func (uc *UserCenter) ConfigFields() []plugin.ConfigField {
+ syncState := plugin.LoadingActionStateNone
+ lastSuccessfulSyncAt := "None"
+ if !uc.syncTime.IsZero() {
+ syncState = plugin.LoadingActionStateComplete
+ lastSuccessfulSyncAt = uc.syncTime.In(time.FixedZone("GMT", 8*3600)).Format("2006-01-02 15:04:05")
+ }
+ t := func(ctx *plugin.GinContext) string {
+ return plugin.Translate(ctx, i18n.ConfigSyncNowDescription) + ": " + lastSuccessfulSyncAt
+ }
+ syncNowDesc := plugin.Translator{Fn: t}
+
+ syncNowLabel := plugin.MakeTranslator(i18n.ConfigSyncNowLabel)
+
+ if uc.syncing {
+ syncNowLabel = plugin.MakeTranslator(i18n.ConfigSyncNowLabelForDoing)
+ syncState = plugin.LoadingActionStatePending
+ }
+
+ return []plugin.ConfigField{
+ {
+ Name: "auto_sync",
+ Type: plugin.ConfigTypeSwitch,
+ Title: plugin.MakeTranslator(i18n.ConfigAutoSyncTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigAutoSyncDescription),
+ Required: false,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ Label: plugin.MakeTranslator(i18n.ConfigAutoSyncLabel),
+ },
+ Value: uc.Config.AutoSync,
+ },
+ {
+ Name: "sync_now",
+ Type: plugin.ConfigTypeButton,
+ Title: plugin.MakeTranslator(i18n.ConfigSyncNowTitle),
+ Description: syncNowDesc,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ Text: syncNowLabel,
+ Action: &plugin.UIOptionAction{
+ Url: "/answer/admin/api/slack/sync",
+ Method: "get",
+ Loading: &plugin.LoadingAction{
+ Text: plugin.MakeTranslator(i18n.ConfigSyncNowLabelForDoing),
+ State: syncState,
+ },
+ OnComplete: &plugin.OnCompleteAction{
+ ToastReturnMessage: true,
+ RefreshFormConfig: true,
+ },
+ },
+ Variant: "outline-secondary",
+ },
+ },
+ {
+ Name: "client_id",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigClientIDTitle), // Slack Client ID
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: uc.Config.ClientID,
+ },
+ {
+ Name: "client_secret",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigClientSecretTitle), // Slack Client Secret
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypePassword,
+ },
+ Value: uc.Config.ClientSecret,
+ },
+ {
+ Name: "signing_secret",
+ Type: plugin.ConfigTypeInput,
+ Title: plugin.MakeTranslator(i18n.ConfigSigningSecretTitle), // Slack Redirect URI
+ Required: true,
+ UIOptions: plugin.ConfigFieldUIOptions{
+ InputType: plugin.InputTypeText,
+ },
+ Value: uc.Config.SigningSecret,
+ },
+ {
+ Name: "notification",
+ Type: plugin.ConfigTypeSwitch,
+ Title: plugin.MakeTranslator(i18n.ConfigNotificationTitle),
+ Description: plugin.MakeTranslator(i18n.ConfigNotificationDescription),
+ UIOptions: plugin.ConfigFieldUIOptions{
+ Label: plugin.MakeTranslator(i18n.ConfigNotificationLabel),
+ },
+ Value: uc.Config.Notification,
+ },
+ }
+}
+
+func (uc *UserCenter) ConfigReceiver(config []byte) error {
+ c := &UserCenterConfig{}
+ err := json.Unmarshal(config, c)
+ if err != nil {
+ return err
+ }
+ uc.Config = c
+
+ uc.SlackClient = NewSlackClient(c.ClientID, c.ClientSecret)
+ return nil
+}
diff --git a/user-center-slack/cron.go b/user-center-slack/cron.go
new file mode 100644
index 00000000..f1dd392e
--- /dev/null
+++ b/user-center-slack/cron.go
@@ -0,0 +1,41 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "time"
+
+ "github.com/segmentfault/pacman/log"
+)
+
+func (uc *UserCenter) CronSyncData() {
+ go func() {
+ ticker := time.NewTicker(time.Hour)
+ // defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ log.Infof("UserCenter is syncing Slack user data")
+ uc.syncSlackClient()
+ }
+ }
+ }()
+}
diff --git a/user-center-slack/go.mod b/user-center-slack/go.mod
new file mode 100644
index 00000000..50786fa4
--- /dev/null
+++ b/user-center-slack/go.mod
@@ -0,0 +1,53 @@
+module github.com/apache/incubator-answer-plugins/user-center-slack
+
+go 1.22.7
+
+toolchain go1.23.1
+
+require (
+ github.com/apache/incubator-answer v1.4.0
+ github.com/apache/incubator-answer-plugins/util v1.0.2
+ github.com/gin-gonic/gin v1.10.0
+ github.com/go-resty/resty/v2 v2.15.1
+ github.com/patrickmn/go-cache v2.1.0+incompatible
+ github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f
+)
+
+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/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/go-cmp v0.6.0 // indirect
+ github.com/google/wire v0.5.0 // indirect
+ github.com/gorilla/css v1.0.0 // 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.21 // 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/contrib/i18n v0.0.0-20230516093754-b76aef1c1150 // 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.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ sigs.k8s.io/yaml v1.3.0 // indirect
+)
diff --git a/user-center-slack/go.sum b/user-center-slack/go.sum
new file mode 100644
index 00000000..335f91e2
--- /dev/null
+++ b/user-center-slack/go.sum
@@ -0,0 +1,151 @@
+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.0 h1:W3y4TAQ4sdzgcqntGqNBPe0BdyeW7+l8FWYBDs9g8+Y=
+github.com/apache/incubator-answer v1.4.0/go.mod h1:Q4NkQmBd0sV7t3Cd8NBsWh9w8jFRo/2qjzOw9MlRNwk=
+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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/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/go-resty/resty/v2 v2.15.1 h1:vuna8FM2EaQ6IYbtjh+Gjh00uu7xEWuuGyTKeIaYkvE=
+github.com/go-resty/resty/v2 v2.15.1/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
+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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/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.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+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.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg=
+github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
+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/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/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-20230516093754-b76aef1c1150 h1:OEuW1D7RGDE0CZDr0oGMw9Eiq7fAbD9C4WMrvSixamk=
+github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150/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/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
+golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+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.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/user-center-slack/handler.go b/user-center-slack/handler.go
new file mode 100644
index 00000000..e4268471
--- /dev/null
+++ b/user-center-slack/handler.go
@@ -0,0 +1,149 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/gin-gonic/gin"
+ "github.com/segmentfault/pacman/log"
+)
+
+// RespBody response body.
+type RespBody struct {
+ // http code
+ Code int `json:"code"`
+ // reason key
+ Reason string `json:"reason"`
+ // response message
+ Message string `json:"msg"`
+ // response data
+ Data interface{} `json:"data"`
+}
+
+// NewRespBodyData new response body with data
+func NewRespBodyData(code int, reason string, data interface{}) *RespBody {
+ return &RespBody{
+ Code: code,
+ Reason: reason,
+ Data: data,
+ }
+}
+
+func (uc *UserCenter) BuildSlackBaseRedirectURL() string {
+ clientID := uc.Config.ClientID
+ log.Debug("Get client ID:", clientID)
+ scope := "chat:write,commands,groups:write,im:write,incoming-webhook,mpim:write,users:read,users:read.email"
+ response_type := "code"
+ redirect_uri := fmt.Sprintf("%s/answer/api/v1/user-center/login/callback", plugin.SiteURL())
+
+ base_redirectURL := fmt.Sprintf(
+ "https://slack.com/oauth/v2/authorize?client_id=%s&scope=%s&response_type=%s&redirect_uri=%s",
+ clientID, scope, response_type, redirect_uri,
+ )
+
+ state := genState()
+ nonce := genNonce()
+ uc.Cache.Set("oauth_state_"+state, state, time.Minute*5)
+
+ redirectURL := fmt.Sprintf("%s&state=%s&nonce=%s", base_redirectURL, state, nonce)
+ log.Debug("RedirectURL from BuildSlackBaseRedirectURL:", redirectURL)
+
+ return redirectURL
+}
+
+func (uc *UserCenter) GetSlackRedirectURL(ctx *gin.Context) {
+ redirectURL := uc.BuildSlackBaseRedirectURL()
+ log.Debug("Processing GetSlackRedirectURL")
+
+ ctx.Writer.Header().Set("Content-Type", "application/json")
+ encoder := json.NewEncoder(ctx.Writer)
+ encoder.SetEscapeHTML(false)
+
+ respData := NewRespBodyData(http.StatusOK, "success", map[string]string{
+ "redirect_url": redirectURL,
+ })
+ ctx.Writer.WriteHeader(http.StatusOK)
+ if err := encoder.Encode(respData); err != nil {
+ ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to encode response"})
+ return
+ }
+}
+
+func genNonce() string {
+ bytes := make([]byte, 10)
+ _, _ = rand.Read(bytes)
+ return hex.EncodeToString(bytes)
+}
+
+func genState() string {
+ bytes := make([]byte, 32)
+ _, _ = rand.Read(bytes)
+ return base64.URLEncoding.EncodeToString(bytes)
+}
+
+func (uc *UserCenter) Sync(ctx *gin.Context) {
+ uc.syncSlackClient()
+
+ if uc.syncSuccess {
+ ctx.JSON(http.StatusOK, NewRespBodyData(http.StatusOK, "success", map[string]any{
+ "message": "User data synced successfully",
+ }))
+ return
+ }
+
+ errRespBodyData := NewRespBodyData(http.StatusBadRequest, "error", map[string]any{
+ "err_type": "toast",
+ })
+ errRespBodyData.Message = "Failed to sync user data"
+ ctx.JSON(http.StatusBadRequest, errRespBodyData)
+}
+
+func (uc *UserCenter) syncSlackClient() {
+ if !uc.syncLock.TryLock() {
+ log.Infof("sync data is running")
+ return
+ }
+ defer func() {
+ uc.syncing = false
+ if uc.syncSuccess {
+ uc.syncTime = time.Now()
+ }
+ uc.syncLock.Unlock()
+ }()
+
+ log.Info("start sync slack data")
+ uc.syncing = true
+ uc.syncSuccess = true
+
+ if err := uc.SlackClient.UpdateUserInfo(); err != nil {
+ log.Errorf("list user error: %s", err)
+ uc.syncSuccess = false
+ return
+ }
+ log.Info("end sync slack data")
+}
diff --git a/user-center-slack/i18n/en_US.yaml b/user-center-slack/i18n/en_US.yaml
new file mode 100644
index 00000000..90d76451
--- /dev/null
+++ b/user-center-slack/i18n/en_US.yaml
@@ -0,0 +1,116 @@
+# 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:
+ slack_user_center:
+ backend:
+ response:
+ sync_now:
+ success:
+ other: Contacts sync successful.
+ failed:
+ other: Contacts sync failed.
+ info:
+ name:
+ other: Slack User Center
+ description:
+ other: A plugin for integrating Slack user management
+ config:
+ tip:
+ title:
+ other: Push notification service has been turned off.
+ auto_sync:
+ label:
+ other: Turn on auto sync
+ title:
+ other: Auto Sync Contacts
+ description:
+ other: Automatically synchronize every hour.
+ sync_now:
+ label:
+ other: Sync now
+ label_for_doing:
+ other: Syncing
+ title:
+ other: Manual Sync Contacts
+ description:
+ other: Last successful sync
+ client_id:
+ title:
+ other: Client ID
+ description:
+ other: Slack client ID
+ client_secret:
+ title:
+ other: Client Secret
+ description:
+ other: Slack client secret
+ notification:
+ label:
+ other: Turn on push notifications
+ title:
+ other: Notifications
+ description:
+ other: Users will receive notifications on Slack.
+ signing_secret:
+ title:
+ other: Signing Secret
+ description:
+ other: Slack signing secret
+ user_config:
+ 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:
+ other: "{{.TriggerUserDisplayName}} updated question {{.QuestionTitle}}"
+ answer_the_question:
+ other: "{{.TriggerUserDisplayName}} answered the question {{.QuestionTitle}}"
+ update_answer:
+ other: "{{.TriggerUserDisplayName}} updated answer {{.QuestionTitle}}"
+ accept_answer:
+ other: "{{.TriggerUserDisplayName}} accepted answer {{.QuestionTitle}}"
+ comment_question:
+ other: "{{.TriggerUserDisplayName}} commented question {{.QuestionTitle}}"
+ comment_answer:
+ other: "{{.TriggerUserDisplayName}} commented answer {{.QuestionTitle}}"
+ reply_to_you:
+ other: "{{.TriggerUserDisplayName}} replied you {{.QuestionTitle}}"
+ mention_you:
+ other: "{{.TriggerUserDisplayName}} mentioned you {{.QuestionTitle}}"
+ invited_you_to_answer:
+ other: "{{.TriggerUserDisplayName}} invited you to answer {{.QuestionTitle}}"
+ new_question:
+ other: "New question:\n{{.QuestionTitle}}\n{{.QuestionTags}}"
diff --git a/user-center-slack/i18n/translation.go b/user-center-slack/i18n/translation.go
new file mode 100644
index 00000000..ed7c5c97
--- /dev/null
+++ b/user-center-slack/i18n/translation.go
@@ -0,0 +1,87 @@
+/*
+ * 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.slack_user_center.backend.info.name"
+ InfoDescription = "plugin.slack_user_center.backend.info.description"
+ ConfigTipTitle = "plugin.slack_user_center.backend.config.tip.title"
+ ConfigAutoSyncLabel = "plugin.slack_user_center.backend.config.auto_sync.label"
+ ConfigAutoSyncTitle = "plugin.slack_user_center.backend.config.auto_sync.title"
+ ConfigAutoSyncDescription = "plugin.slack_user_center.backend.config.auto_sync.description"
+ ConfigSyncNowLabel = "plugin.slack_user_center.backend.config.sync_now.label"
+ ConfigSyncNowLabelForDoing = "plugin.slack_user_center.backend.config.sync_now.label_for_doing"
+ ConfigSyncNowTitle = "plugin.slack_user_center.backend.config.sync_now.title"
+ ConfigSyncNowDescription = "plugin.slack_user_center.backend.config.sync_now.description"
+ ConfigClientIDTitle = "plugin.slack_user_center.backend.config.client_id.title"
+ ConfigClientIDDescription = "plugin.slack_user_center.backend.config.client_id.description"
+ ConfigClientSecretTitle = "plugin.slack_user_center.backend.config.client_secret.title"
+ ConfigClientSecretDescription = "plugin.slack_user_center.backend.config.client_secret.description"
+ ConfigSigningSecretTitle = "plugin.slack_user_center.backend.config.signing_secret.title"
+ ConfigSigningSecretDescription = "plugin.slack_user_center.backend.config.signing_secret.description"
+ ConfigSyncNowSuccessResponse = "plugin.slack_user_center.backend.response.sync_now.success"
+ ConfigSyncNowFailedResponse = "plugin.slack_user_center.backend.response.sync_now.failed"
+ ConfigNotificationLabel = "plugin.slack_user_center.backend.config.notification.label"
+ ConfigNotificationTitle = "plugin.slack_user_center.backend.config.notification.title"
+ ConfigNotificationDescription = "plugin.slack_user_center.backend.config.notification.description"
+
+ UserConfigWebhookURLTitle = "plugin.slack_user_center.backend.user_config.webhook_url.title"
+
+ UserConfigInboxNotificationsTitle = "plugin.slack_user_center.backend.user_config.inbox_notifications.title"
+ UserConfigInboxNotificationsLabel = "plugin.slack_user_center.backend.user_config.inbox_notifications.label"
+ UserConfigInboxNotificationsDescription = "plugin.slack_user_center.backend.user_config.inbox_notifications.description"
+
+ UserConfigAllNewQuestionsNotificationsTitle = "plugin.slack_user_center.backend.user_config.all_new_questions.title"
+ UserConfigAllNewQuestionsNotificationsLabel = "plugin.slack_user_center.backend.user_config.all_new_questions.label"
+ UserConfigAllNewQuestionsNotificationsDescription = "plugin.slack_user_center.backend.user_config.all_new_questions.description"
+
+ UserConfigNewQuestionsForFollowingTagsTitle = "plugin.slack_user_center.backend.user_config.new_questions_for_following_tags.title"
+ UserConfigNewQuestionsForFollowingTagsLabel = "plugin.slack_user_center.backend.user_config.new_questions_for_following_tags.label"
+ UserConfigNewQuestionsForFollowingTagsDescription = "plugin.slack_user_center.backend.user_config.new_questions_for_following_tags.description"
+
+ UserConfigUpvotedAnswersTitle = "plugin.slack_user_center.backend.user_config.upvoted_answers.title"
+ UserConfigUpvotedAnswersLabel = "plugin.slack_user_center.backend.user_config.upvoted_answers.label"
+ UserConfigUpvotedAnswersDescription = "plugin.slack_user_center.backend.user_config.upvoted_answers.description"
+
+ UserConfigDownvotedAnswersTitle = "plugin.slack_user_center.backend.user_config.downvoted_answers.title"
+ UserConfigDownvotedAnswersLabel = "plugin.slack_user_center.backend.user_config.downvoted_answers.label"
+ UserConfigDownvotedAnswersDescription = "plugin.slack_user_center.backend.user_config.downvoted_answers.description"
+
+ UserConfigUpdatedQuestionsTitle = "plugin.slack_user_center.backend.user_config.updated_questions.title"
+ UserConfigUpdatedQuestionsLabel = "plugin.slack_user_center.backend.user_config.updated_questions.label"
+ UserConfigUpdatedQuestionsDescription = "plugin.slack_user_center.backend.user_config.updated_questions.description"
+
+ UserConfigUpdatedAnswersTitle = "plugin.slack_user_center.backend.user_config.updated_answers.title"
+ UserConfigUpdatedAnswersLabel = "plugin.slack_user_center.backend.user_config.updated_answers.label"
+ UserConfigUpdatedAnswersDescription = "plugin.slack_user_center.backend.user_config.updated_answers.description"
+
+ TplUpdatedQuestions = "plugin.slack_user_center.backend.tpl.update_question"
+ TplAnswerTheQuestion = "plugin.slack_user_center.backend.tpl.answer_the_question"
+ TplUpdatedAnswers = "plugin.slack_user_center.backend.tpl.update_answer"
+ TplAcceptAnswer = "plugin.slack_user_center.backend.tpl.accept_answer"
+ TplCommentQuestion = "plugin.slack_user_center.backend.tpl.comment_question"
+ TplCommentAnswer = "plugin.slack_user_center.backend.tpl.comment_answer"
+ TplReplyToYou = "plugin.slack_user_center.backend.tpl.reply_to_you"
+ TplMentionYou = "plugin.slack_user_center.backend.tpl.mention_you"
+ TplInvitedYouToAnswer = "plugin.slack_user_center.backend.tpl.invited_you_to_answer"
+ TplNewQuestion = "plugin.slack_user_center.backend.tpl.new_question"
+ TplUpvotedAnswer = "plugin.slack_user_center.backend.tpl.upvoted_answer"
+ TplDownvotedAnswer = "plugin.slack_user_center.backend.tpl.downvoted_answer"
+)
diff --git a/user-center-slack/i18n/zh_CN.yaml b/user-center-slack/i18n/zh_CN.yaml
new file mode 100644
index 00000000..f369ac8b
--- /dev/null
+++ b/user-center-slack/i18n/zh_CN.yaml
@@ -0,0 +1,116 @@
+# 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:
+ slack_user_center:
+ backend:
+ response:
+ sync_now:
+ success:
+ other: 同步成功。
+ failed:
+ other: 同步失败。
+ info:
+ name:
+ other: Slack
+ description:
+ other: 通过Slack进行登录
+ config:
+ tip:
+ title:
+ other: 推送通知服务已关闭。
+ auto_sync:
+ label:
+ other: 打开自动同步
+ title:
+ other: 自动同步
+ description:
+ other: 每小时自动同步。
+ sync_now:
+ label:
+ other: 立即同步
+ label_for_doing:
+ other: 同步中
+ title:
+ other: 手动同步
+ description:
+ other: 上次成功同步于
+ client_id:
+ title:
+ other: Client ID
+ description:
+ other: Slack client ID
+ client_secret:
+ title:
+ other: Client Secret
+ description:
+ other: Slack client secret
+ notification:
+ label:
+ other: 打开通知
+ title:
+ other: 通知
+ description:
+ other: 用户将在Slack上收到通知。
+ signing_secret:
+ title:
+ other: Signing Secret
+ description:
+ other: Slack signing secret
+ user_config:
+ 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:
+ other: "{{.TriggerUserDisplayName}} 更新问题 {{.QuestionTitle}}"
+ answer_the_question:
+ other: "{{.TriggerUserDisplayName}} 回答了问题 {{.QuestionTitle}}"
+ update_answer:
+ other: "{{.TriggerUserDisplayName}} 更新答案 {{.QuestionTitle}}"
+ accept_answer:
+ other: "{{.TriggerUserDisplayName}} 接受答案 {{.QuestionTitle}}"
+ comment_question:
+ other: "{{.TriggerUserDisplayName}} 评论提问 {{.QuestionTitle}}"
+ comment_answer:
+ other: "{{.TriggerUserDisplayName}} 评论回答 {{.QuestionTitle}}"
+ reply_to_you:
+ other: "{{.TriggerUserDisplayName}} 回复了问题 {{.QuestionTitle}}"
+ mention_you:
+ other: "{{.TriggerUserDisplayName}} 提到了你 {{.QuestionTitle}}"
+ invited_you_to_answer:
+ other: "{{.TriggerUserDisplayName}} 邀请你回答 {{.QuestionTitle}}"
+ new_question:
+ other: "新问题:\n{{.QuestionTitle}}\n{{.QuestionTags}}"
\ No newline at end of file
diff --git a/user-center-slack/importer.go b/user-center-slack/importer.go
new file mode 100644
index 00000000..8f2d19ca
--- /dev/null
+++ b/user-center-slack/importer.go
@@ -0,0 +1,197 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "bytes"
+ "context"
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/gin-gonic/gin"
+ "github.com/segmentfault/pacman/log"
+)
+
+func (uc *UserCenter) parseText(text string) (string, string, []string, error) {
+ re := regexp.MustCompile(`\[(.*?)\]`)
+ matches := re.FindAllStringSubmatch(text, -1)
+
+ if len(matches) != 3 {
+ return "", "", nil, fmt.Errorf("text field does not conform to the required format")
+ }
+
+ part1 := matches[0][1]
+ part2 := matches[1][1]
+ rawTags := strings.Split(matches[2][1], ",")
+
+ var tags []string
+ for _, tag := range rawTags {
+ if tag != "" {
+ tags = append(tags, tag)
+ }
+ }
+
+ // if part1 or part2 or tags in empty return error
+ if part1 == "" || part2 == "" || len(tags) == 0 {
+ return "", "", nil, fmt.Errorf("text field does not be empty")
+ }
+ return part1, part2, tags, nil
+}
+func getSlackUserEmail(userID, token string) (string, error) {
+ url := fmt.Sprintf("https://slack.com/api/users.info?user=%s", userID)
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return "", err
+ }
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+
+ var userResponse SlackUserResponse
+ if err := json.Unmarshal(body, &userResponse); err != nil {
+ return "", err
+ }
+ if !userResponse.Ok {
+ return "", fmt.Errorf("failed to get user info from Slack")
+ }
+
+ return userResponse.User.Profile.Email, nil
+}
+
+func (uc *UserCenter) verifySlackRequest(ctx *gin.Context) error {
+ body, err := io.ReadAll(ctx.Request.Body)
+ if err != nil {
+ return fmt.Errorf("could not read request body: %v", err)
+ }
+ timestamp := ctx.GetHeader("X-Slack-Request-Timestamp")
+ slackSignature := ctx.GetHeader("X-Slack-Signature")
+
+ // check the timestamp validity in 5 minutes
+ ts, err := strconv.ParseInt(timestamp, 10, 64)
+ if err != nil {
+ return fmt.Errorf("invalid timestamp: %v", err)
+ }
+ if time.Now().Unix()-ts > 60*5 {
+ return fmt.Errorf("timestamp is too old")
+ }
+ // Reset the request body for further processing
+ ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body))
+
+ sigBaseString := fmt.Sprintf("v0:%s:%s", timestamp, string(body))
+
+ h := hmac.New(sha256.New, []byte(uc.Config.SigningSecret))
+ h.Write([]byte(sigBaseString))
+ computedSignature := "v0=" + hex.EncodeToString(h.Sum(nil))
+
+ if !hmac.Equal([]byte(computedSignature), []byte(slackSignature)) {
+ return fmt.Errorf("invalid signature")
+ }
+
+ return nil
+}
+func (uc *UserCenter) GetQuestion(ctx *gin.Context) (questionInfo plugin.QuestionImporterInfo, err error) {
+ questionInfo = plugin.QuestionImporterInfo{}
+
+ err = uc.verifySlackRequest(ctx)
+ if err != nil {
+ return questionInfo, err
+ }
+
+ text := ctx.PostForm("text")
+ part1, part2, tags, err := uc.parseText(text)
+ if err != nil {
+ return questionInfo, err
+ }
+
+ questionInfo.Title = part1
+ questionInfo.Content = part2
+ questionInfo.Tags = tags
+ userID := ctx.PostForm("user_id")
+
+ token := uc.SlackClient.AccessToken
+ email, err := getSlackUserEmail(userID, token)
+ if err != nil {
+ return questionInfo, err
+ }
+
+ questionInfo.UserEmail = email
+ return questionInfo, nil
+}
+
+func (uc *UserCenter) SlashCommand(ctx *gin.Context) {
+ body, _ := io.ReadAll(ctx.Request.Body)
+ ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body))
+ cmd := ctx.PostForm("command")
+ if cmd != "/ask" {
+ log.Errorf("error: Invalid command")
+ ctx.JSON(http.StatusBadRequest, gin.H{"text": "Invalid command"})
+ return
+ }
+ ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body))
+ err := uc.verifySlackRequest(ctx)
+ if err != nil {
+ ctx.JSON(http.StatusBadRequest, gin.H{"text": "Slack request verification faild"})
+ log.Errorf("error: %v", err)
+ return
+ }
+ ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body))
+ questionInfo, err := uc.GetQuestion(ctx)
+ if err != nil {
+ log.Errorf("error: %v", err)
+ ctx.JSON(200, gin.H{"text": err.Error()})
+ return
+ }
+ if uc.importerFunc == nil {
+ log.Errorf("error: importerFunc is not initialized")
+ return
+ }
+ err = uc.importerFunc.AddQuestion(ctx, questionInfo)
+ if err != nil {
+ log.Errorf("error: %v", err)
+ ctx.JSON(http.StatusBadRequest, gin.H{"text": "Failed to add question"})
+ return
+ }
+ ctx.JSON(http.StatusOK, gin.H{"text": "Question has been added successfully"})
+}
+
+func (uc *UserCenter) RegisterImporterFunc(ctx context.Context, importerFunc plugin.ImporterFunc) {
+ uc.importerFunc = importerFunc
+}
diff --git a/user-center-slack/info.yaml b/user-center-slack/info.yaml
new file mode 100644
index 00000000..090e1454
--- /dev/null
+++ b/user-center-slack/info.yaml
@@ -0,0 +1,22 @@
+# 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.
+
+slug_name: slack_user_center
+type: user_center
+version: 1.0.1
+author: AnanChen
+link: https://github.com/apache/incubator-answer-plugins/tree/main/user-center-slack
diff --git a/user-center-slack/notification.go b/user-center-slack/notification.go
new file mode 100644
index 00000000..94fa8ac2
--- /dev/null
+++ b/user-center-slack/notification.go
@@ -0,0 +1,158 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "strings"
+
+ slackI18n "github.com/apache/incubator-answer-plugins/user-center-slack/i18n"
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/go-resty/resty/v2"
+ "github.com/segmentfault/pacman/i18n"
+ "github.com/segmentfault/pacman/log"
+)
+
+// GetNewQuestionSubscribers returns the subscribers of the new question notification
+func (uc *UserCenter) GetNewQuestionSubscribers() (userIDs []string) {
+ for userID, conf := range uc.UserConfigCache.userConfigMapping {
+ if conf.AllNewQuestions {
+ userIDs = append(userIDs, userID)
+ }
+ }
+ return userIDs
+}
+
+// Notify sends a notification to the user
+func (uc *UserCenter) Notify(msg plugin.NotificationMessage) {
+ log.Debugf("try to send notification %+v", msg)
+
+ if !uc.Config.Notification {
+ return
+ }
+
+ // get user config
+ userConfig, err := uc.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
+ }
+ case plugin.NotificationUpVotedTheAnswer:
+ if !userConfig.UpvotedAnswers {
+ log.Debugf("user %s not config the new upvoted answers", msg.ReceiverUserID)
+ }
+ case plugin.NotificationDownVotedTheAnswer:
+ if !userConfig.DownvotedAnswers {
+ log.Debugf("user %s not config the new downvoted answers", msg.ReceiverUserID)
+ }
+
+ case plugin.NotificationUpdateQuestion:
+ if !userConfig.UpdatedQuestions {
+ log.Debugf("user %s not config the update question", msg.ReceiverUserID)
+ return
+ }
+ case plugin.NotificationUpdateAnswer:
+ if !userConfig.UpdatedAnswers {
+ log.Debugf("user %s not config the update answer", 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())
+ }
+}
+
+// renderNotification generates the notification message based on type
+func renderNotification(msg plugin.NotificationMessage) string {
+ lang := i18n.Language(msg.ReceiverLang)
+ switch msg.Type {
+ case plugin.NotificationUpdateQuestion:
+ return plugin.TranslateWithData(lang, slackI18n.TplUpdatedQuestions, msg)
+ case plugin.NotificationAnswerTheQuestion:
+ return plugin.TranslateWithData(lang, slackI18n.TplAnswerTheQuestion, msg)
+ case plugin.NotificationUpdateAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplUpdatedAnswers, msg)
+ case plugin.NotificationAcceptAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplAcceptAnswer, msg)
+ case plugin.NotificationCommentQuestion:
+ return plugin.TranslateWithData(lang, slackI18n.TplCommentQuestion, msg)
+ case plugin.NotificationCommentAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplCommentAnswer, msg)
+ case plugin.NotificationReplyToYou:
+ return plugin.TranslateWithData(lang, slackI18n.TplReplyToYou, msg)
+ case plugin.NotificationMentionYou:
+ return plugin.TranslateWithData(lang, slackI18n.TplMentionYou, msg)
+ case plugin.NotificationInvitedYouToAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplInvitedYouToAnswer, msg)
+ case plugin.NotificationNewQuestion, plugin.NotificationNewQuestionFollowedTag:
+ msg.QuestionTags = strings.Join(strings.Split(msg.QuestionTags, ","), ", ")
+ return plugin.TranslateWithData(lang, slackI18n.TplNewQuestion, msg)
+ case plugin.NotificationUpVotedTheAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplUpvotedAnswer, msg)
+ case plugin.NotificationDownVotedTheAnswer:
+ return plugin.TranslateWithData(lang, slackI18n.TplDownvotedAnswer, msg)
+ }
+ return ""
+}
diff --git a/user-center-slack/schema.go b/user-center-slack/schema.go
new file mode 100644
index 00000000..743cabb7
--- /dev/null
+++ b/user-center-slack/schema.go
@@ -0,0 +1,110 @@
+/*
+ * 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 slack_user_center
+
+type AuthUserResp struct {
+ Ok bool `json:"ok"`
+ Errmsg string `json:"error"`
+ User *UserInfo `json:"user"`
+}
+
+type UserProfile struct {
+ AvatarHash string `json:"avatar_hash"`
+ StatusText string `json:"status_text"`
+ StatusEmoji string `json:"status_emoji"`
+ RealName string `json:"real_name"`
+ DisplayName string `json:"display_name"`
+ Email string `json:"email"`
+ ImageOriginal string `json:"image_original"`
+ Image24 string `json:"image_24"`
+ Image32 string `json:"image_32"`
+ Image48 string `json:"image_48"`
+ Image72 string `json:"image_72"`
+ Image192 string `json:"image_192"`
+ Image512 string `json:"image_512"`
+}
+
+type UserInfo struct {
+ ID string `json:"id"`
+ TeamID string `json:"team_id"`
+ Name string `json:"name"`
+ RealName string `json:"real_name"`
+ Deleted bool `json:"deleted"`
+ TimeZone string `json:"tz"`
+ TimeZoneLabel string `json:"tz_label"`
+ TimeZoneOffset int `json:"tz_offset"`
+ Profile UserProfile `json:"profile"`
+ IsAdmin bool `json:"is_admin"`
+ IsOwner bool `json:"is_owner"`
+ IsPrimaryOwner bool `json:"is_primary_owner"`
+ IsRestricted bool `json:"is_restricted"`
+ IsUltraRestricted bool `json:"is_ultra_restricted"`
+ IsBot bool `json:"is_bot"`
+ Updated int64 `json:"updated"`
+ IsAppUser bool `json:"is_app_user"`
+ Has2FA bool `json:"has_2fa"`
+
+ LastLogin int64 `json:"last_login,omitempty"`
+ IsAvailable bool `json:"is_available"`
+ Enable bool `json:"true"`
+ Status int `json:"status"`
+}
+
+type WebhookReq struct {
+ Blocks []struct {
+ Type string `json:"type"`
+ Text struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ } `json:"text"`
+ } `json:"blocks"`
+}
+
+func NewWebhookReq(content string) *WebhookReq {
+ return &WebhookReq{
+ Blocks: []struct {
+ Type string `json:"type"`
+ Text struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ } `json:"text"`
+ }{
+ {
+ Type: "section",
+ Text: struct {
+ Type string `json:"type"`
+ Text string `json:"text"`
+ }{
+ Type: "mrkdwn",
+ Text: content,
+ },
+ },
+ },
+ }
+}
+
+type SlackUserResponse struct {
+ Ok bool `json:"ok"`
+ User struct {
+ Profile struct {
+ Email string `json:"email"`
+ } `json:"profile"`
+ } `json:"user"`
+}
diff --git a/user-center-slack/slack_user_center.go b/user-center-slack/slack_user_center.go
new file mode 100644
index 00000000..63da5c44
--- /dev/null
+++ b/user-center-slack/slack_user_center.go
@@ -0,0 +1,251 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "embed"
+ "fmt"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/apache/incubator-answer-plugins/util"
+
+ "github.com/apache/incubator-answer-plugins/user-center-slack/i18n"
+ "github.com/apache/incubator-answer/plugin"
+ "github.com/gin-gonic/gin"
+ "github.com/patrickmn/go-cache"
+ "github.com/segmentfault/pacman/log"
+)
+
+//go:embed info.yaml
+var Info embed.FS
+
+type Importer struct{}
+
+type UserCenter struct {
+ Config *UserCenterConfig
+ SlackClient *SlackClient
+ UserConfigCache *UserConfigCache
+ Cache *cache.Cache
+ syncLock sync.Mutex
+ syncing bool
+ syncSuccess bool
+ syncTime time.Time
+ importerFunc plugin.ImporterFunc
+}
+
+func (uc *UserCenter) RegisterUnAuthRouter(r *gin.RouterGroup) {
+ r.GET("/slack/login/url", uc.GetSlackRedirectURL)
+ r.POST("/slack/slash", uc.SlashCommand)
+}
+
+func (uc *UserCenter) RegisterAuthUserRouter(r *gin.RouterGroup) {
+}
+
+func (uc *UserCenter) RegisterAuthAdminRouter(r *gin.RouterGroup) {
+ r.GET("/slack/sync", uc.Sync)
+}
+
+func (uc *UserCenter) AfterLogin(externalID, accessToken string) {
+ log.Debugf("user %s is login", externalID)
+ uc.Cache.Set(externalID, accessToken, time.Minute*5)
+}
+
+func (uc *UserCenter) UserStatus(externalID string) (userStatus plugin.UserStatus) {
+ if len(externalID) == 0 {
+ return plugin.UserStatusAvailable
+ }
+
+ var err error
+ userDetailInfo := uc.SlackClient.UserInfoMapping[externalID]
+ if userDetailInfo == nil {
+ userDetailInfo, err = uc.SlackClient.GetUserDetailInfo(externalID)
+ if err != nil {
+ log.Errorf("get user detail info failed: %v", err)
+ }
+ }
+ if userDetailInfo == nil {
+ return plugin.UserStatusDeleted
+ }
+ switch userDetailInfo.Status {
+ case 1:
+ return plugin.UserStatusAvailable
+ case 2:
+ return plugin.UserStatusSuspended
+ default:
+ return plugin.UserStatusDeleted
+ }
+}
+
+func init() {
+ uc := &UserCenter{
+ Config: &UserCenterConfig{},
+ UserConfigCache: NewUserConfigCache(),
+ SlackClient: NewSlackClient("", ""),
+ Cache: cache.New(5*time.Minute, 10*time.Minute),
+ syncLock: sync.Mutex{},
+ }
+
+ plugin.Register(uc)
+ uc.CronSyncData()
+}
+
+func (uc *UserCenter) Info() plugin.Info {
+ info := &util.Info{}
+ info.GetInfo(Info)
+
+ return plugin.Info{
+ Name: plugin.MakeTranslator(i18n.InfoName),
+ SlugName: info.SlugName,
+ Description: plugin.MakeTranslator(i18n.InfoDescription),
+ Author: info.Author,
+ Version: info.Version,
+ Link: info.Link,
+ }
+}
+
+func (uc *UserCenter) Description() plugin.UserCenterDesc {
+ redirectURL := uc.BuildSlackBaseRedirectURL()
+ desc := plugin.UserCenterDesc{
+ Name: "Slack",
+ DisplayName: plugin.MakeTranslator(i18n.InfoName),
+ Icon: "",
+ Url: "",
+ LoginRedirectURL: redirectURL,
+ SignUpRedirectURL: redirectURL,
+ RankAgentEnabled: false,
+ UserStatusAgentEnabled: false,
+ UserRoleAgentEnabled: false,
+ MustAuthEmailEnabled: true,
+ EnabledOriginalUserSystem: true,
+ }
+ return desc
+}
+
+func (uc *UserCenter) ControlCenterItems() []plugin.ControlCenter {
+ var controlCenterItems []plugin.ControlCenter
+ return controlCenterItems
+}
+
+func (uc *UserCenter) LoginCallback(ctx *plugin.GinContext) (userInfo *plugin.UserCenterBasicUserInfo, err error) {
+ log.Debugf("Processing LoginCallback")
+ CallbackURL := ctx.Request.URL.String()
+ log.Debugf("callbackURL in SlackLoginCallback:", CallbackURL)
+ code := ctx.Query("code")
+ if len(code) == 0 {
+ return nil, fmt.Errorf("code is empty")
+ }
+
+ state := ctx.Query("state")
+ if len(state) == 0 {
+ return nil, fmt.Errorf("state is empty")
+ }
+ log.Debugf("request code: %s, state: %s", code, state)
+
+ expectedState, exist := uc.Cache.Get("oauth_state_" + state)
+ if !exist {
+ fmt.Println("State not found in cache or expired")
+ ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or expired state"})
+ return
+ }
+ if state != expectedState {
+ fmt.Println("State mismatch")
+ ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid state"})
+ return
+ }
+ log.Debugf("State validated successfully")
+
+ info, err := uc.SlackClient.AuthUser(code)
+ if err != nil {
+ return nil, fmt.Errorf("auth user failed: %w", err)
+ }
+ if !info.IsAvailable {
+ return nil, fmt.Errorf("user is not available")
+ }
+ //Get Email
+ if len(info.Profile.Email) == 0 {
+ ctx.Redirect(http.StatusFound, "/user-center/auth-failed")
+ return nil, fmt.Errorf("user email is empty")
+ }
+
+ userInfo = &plugin.UserCenterBasicUserInfo{}
+ userInfo.ExternalID = info.ID
+ userInfo.Username = info.ID
+ userInfo.DisplayName = info.Name
+ userInfo.Email = info.Profile.Email
+ userInfo.Rank = 0
+ userInfo.Mobile = ""
+ userInfo.Avatar = info.Profile.Image192
+
+ uc.Cache.Set(state, userInfo.ExternalID, time.Minute*5)
+ return userInfo, nil
+}
+
+func (uc *UserCenter) SignUpCallback(ctx *plugin.GinContext) (userInfo *plugin.UserCenterBasicUserInfo, err error) {
+ return uc.LoginCallback(ctx)
+}
+
+func (uc *UserCenter) UserInfo(externalID string) (userInfo *plugin.UserCenterBasicUserInfo, err error) {
+ userDetailInfo := uc.SlackClient.UserInfoMapping[externalID]
+ if userDetailInfo == nil {
+ userDetailInfo, err = uc.SlackClient.GetUserDetailInfo(externalID)
+ if err != nil {
+ log.Errorf("get user detail info failed: %v", err)
+ userInfo = &plugin.UserCenterBasicUserInfo{
+ ExternalID: externalID,
+ Status: plugin.UserStatusDeleted,
+ }
+ return userInfo, nil
+ }
+ }
+
+ userInfo = &plugin.UserCenterBasicUserInfo{
+ ExternalID: externalID,
+ Username: userDetailInfo.ID,
+ DisplayName: userDetailInfo.Name,
+ Bio: "",
+ }
+ switch userDetailInfo.Status {
+ case 1:
+ userInfo.Status = plugin.UserStatusAvailable
+ case 2:
+ userInfo.Status = plugin.UserStatusSuspended
+ default:
+ userInfo.Status = plugin.UserStatusDeleted
+ }
+ return userInfo, nil
+}
+
+func (uc *UserCenter) UserList(externalIDs []string) (userList []*plugin.UserCenterBasicUserInfo, err error) {
+ userList = make([]*plugin.UserCenterBasicUserInfo, 0)
+ return userList, nil
+}
+
+func (uc *UserCenter) UserSettings(externalID string) (userSettings *plugin.SettingInfo, err error) {
+ return &plugin.SettingInfo{
+ ProfileSettingRedirectURL: "",
+ AccountSettingRedirectURL: "",
+ }, nil
+}
+
+func (uc *UserCenter) PersonalBranding(externalID string) (branding []*plugin.PersonalBranding) {
+ return branding
+}
diff --git a/user-center-slack/user_config.go b/user-center-slack/user_config.go
new file mode 100644
index 00000000..b39eebc4
--- /dev/null
+++ b/user-center-slack/user_config.go
@@ -0,0 +1,165 @@
+/*
+ * 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 slack_user_center
+
+import (
+ "encoding/json"
+ "fmt"
+ "sync"
+
+ "github.com/apache/incubator-answer-plugins/user-center-slack/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"`
+ UpvotedAnswers bool `json:"upvoted_answers"`
+ DownvotedAnswers bool `json:"downvoted_answers"`
+ UpdatedQuestions bool `json:"updated_questions"`
+ UpdatedAnswers bool `json:"updated_answers"`
+}
+
+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 (uc *UserCenter) UserConfigFields() []plugin.ConfigField {
+ fields := make([]plugin.ConfigField, 0)
+ // Show tip for user, if the notification service is disabled
+ if !uc.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,
+ ))
+ fields = append(fields, createSwitchConfig(
+ "upvoted_answers",
+ i18n.UserConfigUpvotedAnswersTitle,
+ i18n.UserConfigUpvotedAnswersLabel,
+ i18n.UserConfigUpvotedAnswersDescription,
+ ))
+ fields = append(fields, createSwitchConfig(
+ "downvoted_answers",
+ i18n.UserConfigDownvotedAnswersTitle,
+ i18n.UserConfigDownvotedAnswersLabel,
+ i18n.UserConfigDownvotedAnswersDescription,
+ ))
+ fields = append(fields, createSwitchConfig(
+ "updated_questions",
+ i18n.UserConfigUpdatedQuestionsTitle,
+ i18n.UserConfigUpdatedQuestionsLabel,
+ i18n.UserConfigUpdatedQuestionsDescription,
+ ))
+ fields = append(fields, createSwitchConfig(
+ "updated_answers",
+ i18n.UserConfigUpdatedAnswersTitle,
+ i18n.UserConfigUpdatedAnswersLabel,
+ i18n.UserConfigUpdatedAnswersDescription,
+ ))
+ 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 (uc *UserCenter) 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)
+ }
+ uc.UserConfigCache.SetUserConfig(userID, &userConfig)
+ return nil
+}
+
+func (uc *UserCenter) getUserConfig(userID string) (config *UserConfig, err error) {
+ userConfig := plugin.GetPluginUserConfig(userID, uc.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
+}