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 +}