Skip to content

Commit

Permalink
[i18n] slack support (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
LemonNekoGH committed Mar 18, 2024
1 parent e9d317f commit ad792e2
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 32 deletions.
2 changes: 1 addition & 1 deletion internal/bots/discord/listeners/listeners.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (b *Listeners) smrCmd(event *events.ApplicationCommandInteractionCreate, da
Platform: bot.FromPlatformDiscord,
URL: urlString,
ChannelID: event.Channel().ID().String(),
// TODO: support i18n for discord and slack
// TODO: support i18n for discord
Language: "zh-CN",
})
if err != nil {
Expand Down
47 changes: 37 additions & 10 deletions internal/bots/slack/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import (
"net/http"
"strings"

"github.com/gin-gonic/gin"
"github.com/nekomeowww/insights-bot/internal/services/smr"
"github.com/nekomeowww/insights-bot/internal/services/smr/smrqueue"

"github.com/gin-gonic/gin"
"github.com/nekomeowww/insights-bot/ent"
"github.com/nekomeowww/insights-bot/ent/slackoauthcredentials"
"github.com/nekomeowww/insights-bot/internal/configs"
"github.com/nekomeowww/insights-bot/internal/datastore"
"github.com/nekomeowww/insights-bot/pkg/bots/slackbot"
"github.com/nekomeowww/insights-bot/pkg/bots/slackbot/services"
"github.com/nekomeowww/insights-bot/pkg/i18n"
"github.com/nekomeowww/insights-bot/pkg/logger"
"github.com/nekomeowww/insights-bot/pkg/types/bot"
types "github.com/nekomeowww/insights-bot/pkg/types/smr"
Expand All @@ -38,6 +39,7 @@ type NewHandlersParam struct {
Ent *datastore.Ent
SmrQueue *smrqueue.Queue
Services *services.Services
I18n *i18n.I18n
}

type Handlers struct {
Expand All @@ -46,6 +48,7 @@ type Handlers struct {
ent *datastore.Ent
smrQueue *smrqueue.Queue
services *services.Services
i18n *i18n.I18n
}

func NewHandlers() func(param NewHandlersParam) *Handlers {
Expand All @@ -56,6 +59,7 @@ func NewHandlers() func(param NewHandlersParam) *Handlers {
logger: param.Logger,
smrQueue: param.SmrQueue,
services: param.Services,
i18n: param.I18n,
}
}
}
Expand Down Expand Up @@ -83,6 +87,30 @@ func (h *Handlers) PostCommandInfo(ctx *gin.Context) {
zap.String("channel_id", body.ChannelID),
)

// get user locale, navie code, maybe need to refactor
token, err := h.ent.SlackOAuthCredentials.Query().
Where(slackoauthcredentials.TeamID(body.TeamID)).
First(context.Background())

if err != nil {
h.logger.Warn("smr service: failed to get team's access token when get user locale",
zap.Error(err),
)

return
}

slackCli := slackbot.NewSlackCli(nil, h.config.Slack.ClientID, h.config.Slack.ClientSecret, token.RefreshToken, token.AccessToken)
user, err := slackCli.GetUserInfoWithTokenExpirationCheck(body.UserID, h.services.NewStoreFuncForRefresh(body.TeamID))

if err != nil {
h.logger.Warn("smr service: failed to user locale",
zap.Error(err),
)

return
}

urlString := body.Text

urlString = strings.TrimSpace(urlString)
Expand All @@ -93,13 +121,12 @@ func (h *Handlers) PostCommandInfo(ctx *gin.Context) {
err, originErr := smr.CheckUrl(urlString)
if err != nil {
if smr.IsUrlCheckError(err) {
// TODO: i18n support for slack
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage(smr.FormatUrlCheckError(err, bot.FromPlatformSlack, "", nil)))
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage(smr.FormatUrlCheckError(err, bot.FromPlatformSlack, user.Locale, nil)))
return
}

ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage("出现了一些问题,可以再试试?"))
h.logger.Warn("discord: failed to send error message", zap.Error(err), zap.NamedError("original_error", originErr))
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage(h.i18n.TWithLanguage(user.Locale, "commands.groups.summarization.commands.smr.failedToRead")))
h.logger.Warn("slack: failed to send error message", zap.Error(err), zap.NamedError("original_error", originErr))

return
}
Expand All @@ -111,11 +138,11 @@ func (h *Handlers) PostCommandInfo(ctx *gin.Context) {
if err != nil {
h.logger.Warn("slack: failed to get team's access token", zap.Error(err))
if ent.IsNotFound(err) {
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage("本应用没有权限向这个频道发送消息,尝试重新安装一下?"))
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage(h.i18n.TWithLanguage(user.Locale, "commands.groups.summarization.commands.smr.permissionDenied")))
return
}

ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage("出现了一些问题,可以再试试?"))
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage(h.i18n.TWithLanguage(user.Locale, "commands.groups.summarization.commands.smr.failedToRead")))

return
}
Expand All @@ -126,18 +153,18 @@ func (h *Handlers) PostCommandInfo(ctx *gin.Context) {
URL: urlString,
ChannelID: body.ChannelID,
TeamID: body.TeamID,
// TODO: support i18n for discord and slack
// TODO: support i18n for discord
Language: "zh-CN",
})
if err != nil {
h.logger.Warn("slack: failed to add task", zap.Error(err))
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage("量子速读请求发送失败了,可以再试试?"))
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage(h.i18n.TWithLanguage(user.Locale, "commands.groups.summarization.commands.smr.failedToRead")))

return
}

// response
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage("请稍等,量子速读中..."))
ctx.JSON(http.StatusOK, slackbot.NewSlackWebhookMessage(h.i18n.TWithLanguage(user.Locale, "commands.groups.summarization.commands.smr.reading")))
}

// GetInstallAuth Receive auth code and request for access token.
Expand Down
3 changes: 2 additions & 1 deletion internal/bots/slack/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package slack

import (
"context"
"net/http"

"github.com/nekomeowww/insights-bot/internal/bots/slack/handlers"
"github.com/nekomeowww/insights-bot/internal/configs"
"github.com/nekomeowww/insights-bot/pkg/bots/slackbot"
"github.com/nekomeowww/insights-bot/pkg/bots/slackbot/services"
"github.com/nekomeowww/insights-bot/pkg/logger"
"go.uber.org/fx"
"net/http"
)

func NewModules() fx.Option {
Expand Down
2 changes: 1 addition & 1 deletion internal/bots/telegram/handlers/summarize/smr_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (h *Handlers) Handle(c *tgbot.Context) (tgbot.Response, error) {
}
if urlString == "" {
return nil, tgbot.
NewMessageError(c.T("commands.groups.summarization.commands.smr.noLinksFound")).
NewMessageError(c.T("commands.groups.summarization.commands.smr.noLinksFound.telegram")).
WithReply(c.Update.Message)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/services/smr/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (s *Service) processor(info types.TaskInfo) {
smrResult, err := s.model.SummarizeInputURL(ctx, info.URL, info.Platform)
if err != nil {
s.logger.Warn("smr service: summarization failed", zap.Error(err))
// TODO: support i18n for discord and slack
// TODO: support i18n for discord
errStr := s.processError(err, lo.Ternary(info.Language == "", "en", info.Language))
s.sendResult(nil, info, errStr)

Expand Down
10 changes: 4 additions & 6 deletions internal/services/smr/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,18 @@ func FormatUrlCheckError(err error, platform bot.FromPlatform, language string,
case errors.Is(err, ErrNoLink):
switch platform {
case bot.FromPlatformTelegram:
return i18n.TWithLanguage(language, "commands.groups.summarization.commands.smr.noLinksFound")
// TODO: support i18n for discord and slack
return i18n.TWithLanguage(language, "commands.groups.summarization.commands.smr.noLinksFound.telegram")
case bot.FromPlatformDiscord, bot.FromPlatformSlack:
return "没有找到链接,可以发送一个有效的链接吗?用法:`/smr <链接>`"
return i18n.TWithLanguage(language, "commands.groups.summarization.commands.smr.noLinksFound.slackOrDiscord")
default:
return err.Error()
}
case errors.Is(err, ErrParse), errors.Is(err, ErrScheme):
switch platform {
case bot.FromPlatformTelegram:
return i18n.TWithLanguage(language, "commands.groups.summarization.commands.smr.invalidLink")
return i18n.TWithLanguage(language, "commands.groups.summarization.commands.smr.invalidLink.telegram")
case bot.FromPlatformDiscord, bot.FromPlatformSlack:
// TODO: support i18n for discord and slack
return "你发来的链接无法被理解,可以重新发一个试试。用法:`/smr <链接>`"
return i18n.TWithLanguage(language, "commands.groups.summarization.commands.smr.invalidLink.slackOrDiscord")
default:
return err.Error()
}
Expand Down
9 changes: 7 additions & 2 deletions locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,18 @@ commands:
commands:
smr:
help: 量子速读网页文章(也支持在频道中使用) 用法:/smr <code>&lt;链接&gt;</code>
noLinksFound: 没有找到链接,可以发送一个有效的链接吗?用法:<code>/smr &lt;链接&gt;</code>
invalidLink: 你发来的链接无法被理解,可以重新发一个试试。用法:<code>/smr &lt;链接&gt;</code>
noLinksFound:
telegram: 没有找到链接,可以发送一个有效的链接吗?用法:<code>/smr &lt;链接&gt;</code>
slackOrDiscord: 你发来的链接无法被理解,可以重新发一个试试。用法:`/smr <链接>`
invalidLink:
telegram: 你发来的链接无法被理解,可以重新发一个试试。用法:<code>/smr &lt;链接&gt;</code>
slackOrDiscord: 你发来的链接无法被理解,可以重新发一个试试。用法:`/smr <链接>`
reading: 请稍等,量子速读中...
rateLimitExceeded: 很抱歉,您的操作触发了我们的限制机制,为了保证系统的可用性,本命令每最多 {{ .Seconds }} 秒使用一次,请您耐心等待 {{ .SecondsToBeWaited }} 秒后再试,感谢您的理解和支持。
failedToRead: 量子速读失败了,可以再试试?
failedToReadDueToFailedToFetch: 量子速读的链接读取失败了哦。可以再试试?
contentNotSupported: 暂时不支持量子速读这样的内容呢,可以换个别的链接试试。
permissionDenied: No permission to send message, please try to reinstall this APP

prompts:
smr:
Expand Down
9 changes: 7 additions & 2 deletions locales/zh-CN.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ commands:
commands:
smr:
help: 量子速读网页文章(也支持在频道中使用) 用法:/smr <code>&lt;链接&gt;</code>
noLinksFound: 没有找到链接,可以发送一个有效的链接吗?用法:<code>/smr &lt;链接&gt;</code>
invalidLink: 你发来的链接无法被理解,可以重新发一个试试。用法:<code>/smr &lt;链接&gt;</code>
noLinksFound:
telegram: 没有找到链接,可以发送一个有效的链接吗?用法:<code>/smr &lt;链接&gt;</code>
slackOrDiscord: 没有找到链接,可以发送一个有效的链接吗?用法:`/smr <链接>`
invalidLink:
telegram: 你发来的链接无法被理解,可以重新发一个试试。用法:<code>/smr &lt;链接&gt;</code>
slackOrDiscord: 你发来的链接无法被理解,可以重新发一个试试。用法:`/smr <链接>`
reading: 请稍等,量子速读中...
rateLimitExceeded: 很抱歉,您的操作触发了我们的限制机制,为了保证系统的可用性,本命令每最多 {{ .Seconds }} 秒使用一次,请您耐心等待 {{ .SecondsToBeWaited }} 秒后再试,感谢您的理解和支持。
failedToRead: 量子速读失败了,可以再试试?
failedToReadDueToFailedToFetch: 量子速读的链接读取失败了哦。可以再试试?
contentNotSupported: 暂时不支持量子速读这样的内容呢,可以换个别的链接试试。
permissionDenied: 本应用没有权限向这个频道发送消息,尝试重新安装一下?

modules:
telegram:
Expand Down
12 changes: 8 additions & 4 deletions pkg/bots/slackbot/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package services
import (
"context"

"github.com/nekomeowww/insights-bot/internal/configs"
"github.com/nekomeowww/insights-bot/internal/datastore"
"github.com/nekomeowww/insights-bot/pkg/logger"
"go.uber.org/fx"
Expand All @@ -16,18 +17,21 @@ type NewServicesParam struct {

Logger *logger.Logger
Ent *datastore.Ent
Config *configs.Config
}

type Services struct {
logger *logger.Logger
ent *datastore.Ent
Ent *datastore.Ent
Config *configs.Config
}

func NewServices() func(param NewServicesParam) *Services {
return func(param NewServicesParam) *Services {
return &Services{
logger: param.Logger,
ent: param.Ent,
Ent: param.Ent,
Config: param.Config,
}
}
}
Expand All @@ -39,7 +43,7 @@ func (b *Services) NewStoreFuncForRefresh(teamID string) func(accessToken, refre
}

func (b *Services) CreateOrUpdateSlackCredential(teamID, accessToken, refreshToken string) error {
affectRows, err := b.ent.SlackOAuthCredentials.Update().
affectRows, err := b.Ent.SlackOAuthCredentials.Update().
Where(slackoauthcredentials.TeamID(teamID)).
SetAccessToken(accessToken).
SetRefreshToken(refreshToken).
Expand All @@ -51,7 +55,7 @@ func (b *Services) CreateOrUpdateSlackCredential(teamID, accessToken, refreshTok

if affectRows == 0 {
// create
err = b.ent.SlackOAuthCredentials.Create().
err = b.Ent.SlackOAuthCredentials.Create().
SetTeamID(teamID).
SetAccessToken(accessToken).
SetRefreshToken(refreshToken).
Expand Down
8 changes: 4 additions & 4 deletions pkg/bots/slackbot/services/services_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ func newTestServices() *Services {
}

return &Services{
ent: ent,
Ent: ent,
logger: logger,
}
}

func cleanSlackCredential(s *Services, r *require.Assertions) {
_, err := s.ent.SlackOAuthCredentials.Delete().Exec(context.Background())
_, err := s.Ent.SlackOAuthCredentials.Delete().Exec(context.Background())
r.Empty(err)
}

Expand All @@ -58,7 +58,7 @@ func TestSlackBot_createNewSlackCredential(t *testing.T) {
r.Empty(s.CreateOrUpdateSlackCredential(expectTeamID, expectAccessToken, expectRefreshToken))

// query
cre, err := s.ent.SlackOAuthCredentials.Query().First(context.Background())
cre, err := s.Ent.SlackOAuthCredentials.Query().First(context.Background())
r.Empty(err)
a.Equal(expectTeamID, cre.TeamID)
a.Equal(expectAccessToken, cre.AccessToken)
Expand All @@ -79,7 +79,7 @@ func TestSlackBot_createNewSlackCredential(t *testing.T) {
r.Empty(s.CreateOrUpdateSlackCredential(expectTeamID, expectAccessToken, expectRefreshToken))

// query
cre, err := s.ent.SlackOAuthCredentials.Query().First(context.Background())
cre, err := s.Ent.SlackOAuthCredentials.Query().First(context.Background())
r.Empty(err)
a.Equal(expectTeamID, cre.TeamID)
a.Equal(expectAccessToken, cre.AccessToken)
Expand Down
23 changes: 23 additions & 0 deletions pkg/bots/slackbot/slackbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,29 @@ func (cli *Client) SendMessageWithTokenExpirationCheck(channel string, storeFn S
return cli.SendMessageWithTokenExpirationCheck(channel, storeFn, options...)
}

// GetUserInfoWithTokenExpirationCheck will checks if the error is "token_expired" error,
// if so, will get new token and try again.
func (cli *Client) GetUserInfoWithTokenExpirationCheck(channel string, storeFn StoreNewTokenFunc, options ...slack.MsgOption) (slackUser *slack.User, err error) {
slackUser, err = cli.GetUserInfo(channel)
if err == nil || err.Error() != "token_expired" {
return
}

resp, err := slack.RefreshOAuthV2Token(cli.httpClient, cli.clientID, cli.clientSecret, cli.refreshToken)
if err != nil {
return
}

err = storeFn(resp.AccessToken, resp.RefreshToken)
if err != nil {
return
}
// create new slack client
cli.Client = newOriginSlackCli(cli.httpClient, resp.AccessToken)

return cli.GetUserInfoWithTokenExpirationCheck(channel, storeFn, options...)
}

var _ healthchecker.HealthChecker = (*BotService)(nil)

type BotService struct {
Expand Down

0 comments on commit ad792e2

Please sign in to comment.