diff --git a/internal/handler/wizard/add_chat_member_state.go b/internal/handler/wizard/add_chat_member_state.go index c255961..7110291 100644 --- a/internal/handler/wizard/add_chat_member_state.go +++ b/internal/handler/wizard/add_chat_member_state.go @@ -1,10 +1,17 @@ package wizard import ( - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/db/model" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/util" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" + "github.com/mymmrac/telego" + tu "github.com/mymmrac/telego/telegoutil" + "golang.org/x/text/language" ) +const MaxChatMembers = 32 + const AddChatMemberStateID fsm.StateID = "add_chat_member" type AddChatMemberState struct { @@ -23,7 +30,127 @@ func (s *AddChatMemberState) Enter(pl *Wizard, mc fsm.MachineControls[*Wizard]) } func (s *AddChatMemberState) Handle(pl *Wizard, mc fsm.MachineControls[*Wizard]) { - // todo: copy implementation from func (h *ChatMemberUpdatedHandler) handleAddToChat(tgChat telego.Chat) error + next := WaitingForMemberWebhookStateID + defer func() { + _ = mc.Move(next, pl) + }() + cr := s.App.DB().ForChat() + tgChat := pl.Data.MyChatMember.Chat + chat, err := cr.ByTelegramID(tgChat.ID) + if err != nil { + s.LogError(util.SendInternalError(s.App.TG(), tgChat.ID, nil)) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + + user, err := s.getRegisteredAdmin(tgChat.ID) + if err != nil { + s.LogError(util.SendInternalError(s.App.TG(), tgChat.ID, nil)) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + if user == nil || user.ID == 0 { + _, err = s.App.TG().SendMessage(&telego.SendMessageParams{ + ChatID: tu.ID(tgChat.ID), + Text: s.Localizer(language.English.String()). + Template("you_should_register_first", map[string]interface{}{"Name": s.App.TGProfile().Username}), + ParseMode: telego.ModeMarkdown, + }) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + + loc := s.Localizer(user.Language) + totalMembers, err := s.App.TG().GetChatMemberCount(&telego.GetChatMemberCountParams{ + ChatID: tu.ID(tgChat.ID), + }) + if err != nil { + s.LogError(util.SendInternalError(s.App.TG(), tgChat.ID, nil)) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + if *totalMembers > MaxChatMembers { + _, err = s.App.TG().SendMessage(&telego.SendMessageParams{ + ChatID: tu.ID(tgChat.ID), + Text: loc.Template("too_many_members_in_the_group", map[string]interface{}{"Limit": MaxChatMembers}), + ParseMode: telego.ModeMarkdown, + }) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + + if chat == nil || chat.ID == 0 { + chat = &model.Chat{ + TelegramID: tgChat.ID, + UserID: user.ID, + } + err := cr.Save(chat) + if err != nil { + _ = util.SendInternalError(s.App.TG(), tgChat.ID, loc) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + } else { + chat.UserID = user.ID + err := s.App.DB().ForIntegration().DeleteForChat(chat.ID) + if err != nil { + _ = util.SendInternalError(s.App.TG(), tgChat.ID, loc) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + err = cr.Save(chat) + if err != nil { + _ = util.SendInternalError(s.App.TG(), tgChat.ID, loc) + s.leaveChat(tgChat.ID) + s.LogError(err) + return + } + } + + _, err = s.App.TG().SendMessage(&telego.SendMessageParams{ + ChatID: tu.ID(user.ChatID), + Text: loc.Template("bot_was_added", map[string]interface{}{"Name": tgChat.Title}), + ParseMode: telego.ModeMarkdown, + }) + s.LogError(err) + pl.User = user + pl.TGChat = tgChat + pl.Loc = loc + next = KeyboardChooserStateID +} + +func (s *AddChatMemberState) getRegisteredAdmin(chatID int64) (*model.User, error) { + admins, err := s.App.TG().GetChatAdministrators(&telego.GetChatAdministratorsParams{ + ChatID: tu.ID(chatID), + }) + if err != nil { + return nil, err + } + adminIDs := make([]int64, len(admins)) + for i, admin := range admins { + adminIDs[i] = admin.MemberUser().ID + } + dbAdmins, err := s.App.DB().ForUser().ByTelegramIDs(adminIDs) + if err != nil { + return nil, err + } + if len(dbAdmins) > 0 { + return &dbAdmins[0], nil + } + return nil, nil +} + +func (s *AddChatMemberState) leaveChat(chatID int64) { + _ = s.App.TG().LeaveChat(&telego.LeaveChatParams{ + ChatID: tu.ID(chatID), + }) } func (s *AddChatMemberState) ID() fsm.StateID { diff --git a/internal/handler/wizard/base.go b/internal/handler/wizard/base.go index d52f469..2002d6a 100644 --- a/internal/handler/wizard/base.go +++ b/internal/handler/wizard/base.go @@ -1,16 +1,21 @@ package wizard import ( - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/db/model" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/locale" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" "github.com/mymmrac/telego" + "golang.org/x/text/language" "strings" ) type Wizard struct { UserID int64 ChatID int64 + TGChat telego.Chat + User *model.User + Loc locale.Localizer Data telego.Update } @@ -23,7 +28,14 @@ func newBase(app iface.App) State { return State{App: app} } -func (s State) Localizer(lang string) locale.Localizer { +func (s State) Localizer(langCode ...string) locale.Localizer { + lang := language.English.String() + if len(langCode) > 0 { + lang = langCode[0] + } + if s.Payload.Loc != nil && (len(langCode) == 0 || s.Payload.Loc.Tag().String() == lang) { + return s.Payload.Loc + } lang = strings.ToLower(lang) if len(lang) > 2 { lang = lang[:2] diff --git a/internal/handler/wizard/error_state.go b/internal/handler/wizard/error_state.go index 6ecf782..f69e529 100644 --- a/internal/handler/wizard/error_state.go +++ b/internal/handler/wizard/error_state.go @@ -1,7 +1,7 @@ package wizard import ( - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" "go.uber.org/zap" ) diff --git a/internal/handler/wizard/help_state.go b/internal/handler/wizard/help_state.go index 45ac4a8..bf2770f 100644 --- a/internal/handler/wizard/help_state.go +++ b/internal/handler/wizard/help_state.go @@ -1,8 +1,8 @@ package wizard import ( - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" "github.com/mymmrac/telego" tu "github.com/mymmrac/telego/telegoutil" ) diff --git a/internal/handler/wizard/init.go b/internal/handler/wizard/init.go index 64601fc..1dc1a35 100644 --- a/internal/handler/wizard/init.go +++ b/internal/handler/wizard/init.go @@ -1,8 +1,8 @@ package wizard import ( - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" "github.com/maypok86/otter" "time" ) @@ -44,6 +44,7 @@ func PopulateStates(app iface.App) { NewRegisterState(app), NewWaitingForMemberWebhookState(app), NewAddChatMemberState(app), + NewKeyboardChooserState(app), NewRemoveChatMemberState(app), NewHelpState(app), } diff --git a/internal/handler/wizard/keyboard_chooser_state.go b/internal/handler/wizard/keyboard_chooser_state.go new file mode 100644 index 0000000..e20500d --- /dev/null +++ b/internal/handler/wizard/keyboard_chooser_state.go @@ -0,0 +1,56 @@ +package wizard + +import ( + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/db/model" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/util" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" + "github.com/mymmrac/telego" + tu "github.com/mymmrac/telego/telegoutil" +) + +const KeyboardChooserStateID fsm.StateID = "keyboard_chooser" + +type KeyboardChooserState struct { + State +} + +func NewKeyboardChooserState(app iface.App) fsm.IState[Wizard] { + return &KeyboardChooserState{newBase(app)} +} + +func (s *KeyboardChooserState) Enter(pl *Wizard, _ fsm.MachineControls[*Wizard]) error { + _, err := s.App.TG().SendMessage(&telego.SendMessageParams{ + ChatID: tu.ID(pl.User.ChatID), + Text: s.Localizer().Template("choose_keyboard", map[string]interface{}{"Name": pl.TGChat.Title}), + ParseMode: telego.ModeMarkdown, + ReplyMarkup: &telego.InlineKeyboardMarkup{ + InlineKeyboard: [][]telego.InlineKeyboardButton{ + { + { + Text: s.Localizer().Message("standard_vote_keyboard"), + CallbackData: util.NewKeyboardChooserPayload( + pl.User.TelegramID, pl.TGChat.ID, uint8(model.StandardKeyboard)).String(), + }, + }, + { + { + Text: s.Localizer().Message("sp_vote_keyboard"), + CallbackData: util.NewKeyboardChooserPayload( + pl.User.TelegramID, pl.TGChat.ID, uint8(model.StoryPointsKeyboard)).String(), + }, + }, + }, + }, + }) + s.LogError(err) + return err +} + +func (s *KeyboardChooserState) Handle(pl *Wizard, _ fsm.MachineControls[*Wizard]) { + // todo: implement this using func (h *CallbackQueryHandler) handleChooseKeyboard(pl util.Payload, msgID int, user *model.User) error +} + +func (s *KeyboardChooserState) ID() fsm.StateID { + return KeyboardChooserStateID +} diff --git a/internal/handler/wizard/register_state.go b/internal/handler/wizard/register_state.go index adc7dca..6270e15 100644 --- a/internal/handler/wizard/register_state.go +++ b/internal/handler/wizard/register_state.go @@ -2,8 +2,8 @@ package wizard import ( "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/db/model" - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" "github.com/mymmrac/telego" tu "github.com/mymmrac/telego/telegoutil" ) @@ -39,6 +39,7 @@ func (s *RegisterState) Handle(pl *Wizard, mc fsm.MachineControls[*Wizard]) { if shouldUpdate { _ = userRepo.Save(user) } + pl.User = user _ = mc.Move(WaitingForMemberWebhookStateID, pl) return } @@ -57,6 +58,7 @@ func (s *RegisterState) Handle(pl *Wizard, mc fsm.MachineControls[*Wizard]) { }) s.LogError(err) } + pl.User = user _ = mc.Move(WaitingForMemberWebhookStateID, pl) } diff --git a/internal/handler/wizard/remove_chat_member_state.go b/internal/handler/wizard/remove_chat_member_state.go index 1a1387b..fab7cc7 100644 --- a/internal/handler/wizard/remove_chat_member_state.go +++ b/internal/handler/wizard/remove_chat_member_state.go @@ -1,8 +1,8 @@ package wizard import ( - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" ) const RemoveChatMemberStateID fsm.StateID = "remove_chat_member" diff --git a/internal/handler/wizard/waiting_for_member_webhook_state.go b/internal/handler/wizard/waiting_for_member_webhook_state.go index 4b348e3..982aa3e 100644 --- a/internal/handler/wizard/waiting_for_member_webhook_state.go +++ b/internal/handler/wizard/waiting_for_member_webhook_state.go @@ -1,8 +1,8 @@ package wizard import ( - "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/fsm" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" + "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" "github.com/mymmrac/telego" tu "github.com/mymmrac/telego/telegoutil" ) diff --git a/internal/locale/load.go b/internal/locale/load.go index fb08868..e8fe0a1 100644 --- a/internal/locale/load.go +++ b/internal/locale/load.go @@ -32,7 +32,7 @@ func init() { } for _, tag := range tags { - localizers[tag.String()] = &localizer{loc: i18n.NewLocalizer(bundle, tag.String())} + localizers[tag.String()] = &localizer{loc: i18n.NewLocalizer(bundle, tag.String()), tag: tag} } } diff --git a/internal/locale/localizer.go b/internal/locale/localizer.go index c6853ca..ebe3ace 100644 --- a/internal/locale/localizer.go +++ b/internal/locale/localizer.go @@ -1,14 +1,19 @@ package locale -import "github.com/nicksnyder/go-i18n/v2/i18n" +import ( + "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" +) type Localizer interface { Message(string) string Template(string, interface{}) string + Tag() language.Tag } type localizer struct { loc *i18n.Localizer + tag language.Tag } func (l *localizer) Message(str string) string { @@ -21,3 +26,7 @@ func (l *localizer) Template(str string, tpl interface{}) string { TemplateData: tpl, }) } + +func (l *localizer) Tag() language.Tag { + return l.tag +} diff --git a/internal/fsm/machine.go b/pkg/fsm/machine.go similarity index 100% rename from internal/fsm/machine.go rename to pkg/fsm/machine.go diff --git a/internal/fsm/state.go b/pkg/fsm/state.go similarity index 100% rename from internal/fsm/state.go rename to pkg/fsm/state.go