diff --git a/README.md b/README.md index e128fe0db..0b8d4c412 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/notification-dingtalk/README.md b/notification-dingtalk/README.md new file mode 100644 index 000000000..3b1a7d8bf --- /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 000000000..0bad4007c --- /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 000000000..360a941db --- /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 000000000..22efeb98e 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 000000000..a0e03e07d --- /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 000000000..5299099b1 --- /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 000000000..c4d144973 --- /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 000000000..2d9c5a465 --- /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 000000000..80a170f45 --- /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 000000000..c5481f20a --- /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 000000000..65d5d0e11 --- /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 000000000..2dbdba6ea --- /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/storage-tencentyuncos/go.mod b/storage-tencentyuncos/go.mod index bebd74e10..55f64dcfb 100644 --- a/storage-tencentyuncos/go.mod +++ b/storage-tencentyuncos/go.mod @@ -1,4 +1,4 @@ -module tencentyuncos +module github.com/apache/incubator-answer-plugins/tencentyuncos go 1.21.3