diff --git a/internal/repo/plugin_config/plugin_user_config_repo.go b/internal/repo/plugin_config/plugin_user_config_repo.go index 1f233783..5b3b54de 100644 --- a/internal/repo/plugin_config/plugin_user_config_repo.go +++ b/internal/repo/plugin_config/plugin_user_config_repo.go @@ -21,6 +21,7 @@ package plugin_config import ( "context" + "github.com/apache/incubator-answer/internal/base/pager" "xorm.io/xorm" "github.com/apache/incubator-answer/internal/base/data" @@ -86,3 +87,13 @@ func (ur *pluginUserConfigRepo) GetPluginUserConfig(ctx context.Context, userID, } return pluginUserConfig, exist, err } + +func (ur *pluginUserConfigRepo) GetPluginUserConfigPage(ctx context.Context, page, pageSize int) ( + pluginUserConfigs []*entity.PluginUserConfig, total int64, err error) { + pluginUserConfigs = make([]*entity.PluginUserConfig, 0) + total, err = pager.Help(page, pageSize, &pluginUserConfigs, &entity.PluginUserConfig{}, ur.data.DB.Context(ctx)) + if err != nil { + err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } + return +} diff --git a/internal/service/notification/external_notification.go b/internal/service/notification/external_notification.go index a745d6f7..58228884 100644 --- a/internal/service/notification/external_notification.go +++ b/internal/service/notification/external_notification.go @@ -21,7 +21,6 @@ package notification import ( "context" - "github.com/apache/incubator-answer/internal/base/constant" "github.com/apache/incubator-answer/internal/base/data" "github.com/apache/incubator-answer/internal/schema" "github.com/apache/incubator-answer/internal/service/activity_common" @@ -31,10 +30,7 @@ import ( usercommon "github.com/apache/incubator-answer/internal/service/user_common" "github.com/apache/incubator-answer/internal/service/user_external_login" "github.com/apache/incubator-answer/internal/service/user_notification_config" - "github.com/apache/incubator-answer/pkg/display" - "github.com/apache/incubator-answer/plugin" "github.com/segmentfault/pacman/log" - "strings" ) type ExternalNotificationService struct { @@ -90,64 +86,3 @@ func (ns *ExternalNotificationService) Handler(ctx context.Context, msg *schema. log.Errorf("unknown notification message: %+v", msg) return nil } - -func (ns *ExternalNotificationService) syncNotificationToPlugin(ctx context.Context, - source constant.NotificationSource, msg *schema.ExternalNotificationMsg) { - pluginNotificationMsg := &plugin.NotificationMessage{ - ReceiverUserID: msg.ReceiverUserID, - ReceiverLang: msg.ReceiverLang, - } - - switch source { - case constant.InboxSource: - return - case constant.AllNewQuestionSource: - pluginNotificationMsg.Type = plugin.NotificationNewQuestion - pluginNotificationMsg = ns.newPluginQuestionNotification(ctx, msg) - case constant.AllNewQuestionForFollowingTagsSource: - pluginNotificationMsg.Type = plugin.NotificationNewQuestionFollowedTag - pluginNotificationMsg = ns.newPluginQuestionNotification(ctx, msg) - } - - if len(msg.ReceiverLang) == 0 && len(msg.ReceiverUserID) > 0 { - userInfo, _, _ := ns.userRepo.GetByUserID(ctx, msg.ReceiverUserID) - if userInfo != nil { - pluginNotificationMsg.ReceiverLang = userInfo.Language - } - } - - _ = plugin.CallNotification(func(fn plugin.Notification) error { - userInfo, exist, err := ns.userExternalLoginRepo.GetByUserID(ctx, fn.Info().SlugName, msg.ReceiverUserID) - if err != nil { - log.Errorf("get user external login info failed: %v", err) - return nil - } - if exist { - pluginNotificationMsg.ReceiverExternalID = userInfo.ExternalID - } - fn.Notify(pluginNotificationMsg) - return nil - }) -} - -func (ns *ExternalNotificationService) newPluginQuestionNotification( - ctx context.Context, msg *schema.ExternalNotificationMsg) (raw *plugin.NotificationMessage) { - raw = &plugin.NotificationMessage{ - ReceiverUserID: msg.ReceiverUserID, - ReceiverLang: msg.ReceiverLang, - QuestionTitle: msg.NewQuestionTemplateRawData.QuestionTitle, - QuestionTags: strings.Join(msg.NewQuestionTemplateRawData.Tags, ","), - } - siteInfo, err := ns.siteInfoService.GetSiteGeneral(ctx) - if err != nil { - return raw - } - seoInfo, err := ns.siteInfoService.GetSiteSeo(ctx) - if err != nil { - return raw - } - raw.QuestionUrl = display.QuestionURL( - seoInfo.Permalink, siteInfo.SiteUrl, - msg.NewQuestionTemplateRawData.QuestionID, msg.NewQuestionTemplateRawData.QuestionTitle) - return raw -} diff --git a/internal/service/notification/invite_answer_notification.go b/internal/service/notification/invite_answer_notification.go index 539e89c7..1122d052 100644 --- a/internal/service/notification/invite_answer_notification.go +++ b/internal/service/notification/invite_answer_notification.go @@ -49,7 +49,6 @@ func (ns *ExternalNotificationService) handleInviteAnswerNotification(ctx contex ns.sendInviteAnswerNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewInviteAnswerTemplateRawData) } } - ns.syncNotificationToPlugin(ctx, constant.InboxSource, msg) return nil } diff --git a/internal/service/notification/new_answer_notification.go b/internal/service/notification/new_answer_notification.go index fbddde68..4a43192f 100644 --- a/internal/service/notification/new_answer_notification.go +++ b/internal/service/notification/new_answer_notification.go @@ -49,7 +49,6 @@ func (ns *ExternalNotificationService) handleNewAnswerNotification(ctx context.C ns.sendNewAnswerNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewAnswerTemplateRawData) } } - ns.syncNotificationToPlugin(ctx, constant.InboxSource, msg) return nil } diff --git a/internal/service/notification/new_comment_notification.go b/internal/service/notification/new_comment_notification.go index 623040e2..048c66f1 100644 --- a/internal/service/notification/new_comment_notification.go +++ b/internal/service/notification/new_comment_notification.go @@ -49,7 +49,6 @@ func (ns *ExternalNotificationService) handleNewCommentNotification(ctx context. ns.sendNewCommentNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewCommentTemplateRawData) } } - ns.syncNotificationToPlugin(ctx, constant.InboxSource, msg) return nil } diff --git a/internal/service/notification/new_question_notification.go b/internal/service/notification/new_question_notification.go index 2b850487..f97c93b9 100644 --- a/internal/service/notification/new_question_notification.go +++ b/internal/service/notification/new_question_notification.go @@ -23,10 +23,12 @@ import ( "context" "github.com/apache/incubator-answer/internal/base/constant" "github.com/apache/incubator-answer/internal/schema" + "github.com/apache/incubator-answer/pkg/display" "github.com/apache/incubator-answer/pkg/token" - "github.com/jinzhu/copier" + "github.com/apache/incubator-answer/plugin" "github.com/segmentfault/pacman/i18n" "github.com/segmentfault/pacman/log" + "strings" "time" ) @@ -61,11 +63,9 @@ func (ns *ExternalNotificationService) handleNewQuestionNotification(ctx context }) } } - pluginMsg := &schema.ExternalNotificationMsg{} - _ = copier.Copy(pluginMsg, msg) - pluginMsg.ReceiverUserID = subscriber.UserID - ns.syncNotificationToPlugin(ctx, subscriber.NotificationSource, pluginMsg) } + + ns.syncNewQuestionNotificationToPlugin(ctx, msg) return nil } @@ -190,3 +190,78 @@ func (ns *ExternalNotificationService) sendNewQuestionNotificationEmail(ctx cont ns.emailService.SendAndSaveCodeWithTime( ctx, userInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 1*24*time.Hour) } + +func (ns *ExternalNotificationService) syncNewQuestionNotificationToPlugin(ctx context.Context, + msg *schema.ExternalNotificationMsg) { + _ = plugin.CallNotification(func(fn plugin.Notification) error { + // 1. get all this new question's tags followers + subscribersMapping := make(map[string]plugin.NotificationType) + for _, tagID := range msg.NewQuestionTemplateRawData.TagIDs { + userIDs, err := ns.followRepo.GetFollowUserIDs(ctx, tagID) + if err != nil { + log.Error(err) + continue + } + for _, userID := range userIDs { + subscribersMapping[userID] = plugin.NotificationNewQuestion + } + } + + // 2. get all new question's followers + questionSubscribers := fn.GetNewQuestionSubscribers() + for _, subscriber := range questionSubscribers { + subscribersMapping[subscriber] = plugin.NotificationNewQuestionFollowedTag + } + + // 3. remove question owner + delete(subscribersMapping, msg.NewQuestionTemplateRawData.QuestionAuthorUserID) + + pluginNotificationMsg := ns.newPluginQuestionNotification(ctx, msg) + + // 4. send notification + for subscriber, notificationType := range subscribersMapping { + pluginNotificationMsg.ReceiverUserID = subscriber + pluginNotificationMsg.Type = notificationType + + if len(msg.ReceiverLang) == 0 && len(msg.ReceiverUserID) > 0 { + userInfo, _, _ := ns.userRepo.GetByUserID(ctx, msg.ReceiverUserID) + if userInfo != nil { + pluginNotificationMsg.ReceiverLang = userInfo.Language + } + } + + userInfo, exist, err := ns.userExternalLoginRepo.GetByUserID(ctx, fn.Info().SlugName, msg.ReceiverUserID) + if err != nil { + log.Errorf("get user external login info failed: %v", err) + return nil + } + if exist { + pluginNotificationMsg.ReceiverExternalID = userInfo.ExternalID + } + fn.Notify(pluginNotificationMsg) + } + return nil + }) +} + +func (ns *ExternalNotificationService) newPluginQuestionNotification( + ctx context.Context, msg *schema.ExternalNotificationMsg) (raw *plugin.NotificationMessage) { + raw = &plugin.NotificationMessage{ + ReceiverUserID: msg.ReceiverUserID, + ReceiverLang: msg.ReceiverLang, + QuestionTitle: msg.NewQuestionTemplateRawData.QuestionTitle, + QuestionTags: strings.Join(msg.NewQuestionTemplateRawData.Tags, ","), + } + siteInfo, err := ns.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + return raw + } + seoInfo, err := ns.siteInfoService.GetSiteSeo(ctx) + if err != nil { + return raw + } + raw.QuestionUrl = display.QuestionURL( + seoInfo.Permalink, siteInfo.SiteUrl, + msg.NewQuestionTemplateRawData.QuestionID, msg.NewQuestionTemplateRawData.QuestionTitle) + return raw +} diff --git a/internal/service/plugin_common/plugin_common_service.go b/internal/service/plugin_common/plugin_common_service.go index 099051cf..35327126 100644 --- a/internal/service/plugin_common/plugin_common_service.go +++ b/internal/service/plugin_common/plugin_common_service.go @@ -43,6 +43,8 @@ type PluginUserConfigRepo interface { SaveUserPluginConfig(ctx context.Context, userID string, pluginSlugName, configValue string) (err error) GetPluginUserConfig(ctx context.Context, userID, pluginSlugName string) ( pluginUserConfig *entity.PluginUserConfig, exist bool, err error) + GetPluginUserConfigPage(ctx context.Context, page, pageSize int) ( + pluginUserConfigs []*entity.PluginUserConfig, total int64, err error) } // PluginCommonService user service @@ -61,53 +63,14 @@ func NewPluginCommonService( data *data.Data, ) *PluginCommonService { - // init plugin status - pluginStatus, err := configService.GetStringValue(context.TODO(), constant.PluginStatus) - if err != nil { - log.Error(err) - } else { - if err := plugin.StatusManager.UnmarshalJSON([]byte(pluginStatus)); err != nil { - log.Error(err) - } - } - - // init plugin config - pluginConfigs, err := pluginConfigRepo.GetPluginConfigAll(context.Background()) - if err != nil { - log.Error(err) - } else { - for _, pluginConfig := range pluginConfigs { - err := plugin.CallConfig(func(fn plugin.Config) error { - if fn.Info().SlugName == pluginConfig.PluginSlugName { - return fn.ConfigReceiver([]byte(pluginConfig.Value)) - } - return nil - }) - if err != nil { - log.Errorf("parse plugin config failed: %s %v", pluginConfig.PluginSlugName, err) - } - } - } - - // init plugin user config - plugin.RegisterGetPluginUserConfigFunc(func(userID, pluginSlugName string) []byte { - pluginUserConfig, exist, err := pluginUserConfigRepo.GetPluginUserConfig(context.Background(), userID, pluginSlugName) - if err != nil { - log.Error(err) - return nil - } - if !exist { - return nil - } - return []byte(pluginUserConfig.Value) - }) - - return &PluginCommonService{ + p := &PluginCommonService{ configService: configService, pluginConfigRepo: pluginConfigRepo, pluginUserConfigRepo: pluginUserConfigRepo, data: data, } + p.initPluginData() + return p } // UpdatePluginStatus update plugin status @@ -158,3 +121,73 @@ func (ps *PluginCommonService) GetUserPluginConfig(ctx context.Context, req *sch } return pluginUserConfig.Value, nil } + +func (ps *PluginCommonService) initPluginData() { + // init plugin status + pluginStatus, err := ps.configService.GetStringValue(context.TODO(), constant.PluginStatus) + if err != nil { + log.Error(err) + } else { + if err := plugin.StatusManager.UnmarshalJSON([]byte(pluginStatus)); err != nil { + log.Error(err) + } + } + + // init plugin config + pluginConfigs, err := ps.pluginConfigRepo.GetPluginConfigAll(context.Background()) + if err != nil { + log.Error(err) + } else { + for _, pluginConfig := range pluginConfigs { + err := plugin.CallConfig(func(fn plugin.Config) error { + if fn.Info().SlugName == pluginConfig.PluginSlugName { + return fn.ConfigReceiver([]byte(pluginConfig.Value)) + } + return nil + }) + if err != nil { + log.Errorf("parse plugin config failed: %s %v", pluginConfig.PluginSlugName, err) + } + } + } + + // init plugin user config + plugin.RegisterGetPluginUserConfigFunc(func(userID, pluginSlugName string) []byte { + pluginUserConfig, exist, err := ps.pluginUserConfigRepo.GetPluginUserConfig(context.Background(), userID, pluginSlugName) + if err != nil { + log.Error(err) + return nil + } + if !exist { + return nil + } + return []byte(pluginUserConfig.Value) + }) + + // init plugin user config data + go func() { + page, pageSize := 1, 1000 + for { + userConfigs, _, err := ps.pluginUserConfigRepo.GetPluginUserConfigPage(context.Background(), page, pageSize) + if err != nil { + log.Error(err) + return + } + if len(userConfigs) == 0 { + return + } + for _, userConfig := range userConfigs { + err := plugin.CallUserConfig(func(fn plugin.UserConfig) error { + if fn.Info().SlugName == userConfig.PluginSlugName { + return fn.UserConfigReceiver(userConfig.UserID, []byte(userConfig.Value)) + } + return nil + }) + if err != nil { + log.Errorf("parse plugin user config failed: %s %v", userConfig.PluginSlugName, err) + } + } + page++ + } + }() +} diff --git a/plugin/notification.go b/plugin/notification.go index 0591774f..d3dfd989 100644 --- a/plugin/notification.go +++ b/plugin/notification.go @@ -48,6 +48,9 @@ const ( type Notification interface { Base + // GetNewQuestionSubscribers returns the subscribers of the new question notification + GetNewQuestionSubscribers() (userIDs []string) + // Notify sends a notification to the user Notify(msg *NotificationMessage) }