package command import ( "fmt" "log/slog" "slices" "strconv" "strings" "sync" "git.maximotejeda.com/maximo/cedulados-bot/internal/application/auth" "git.maximotejeda.com/maximo/cedulados-bot/internal/ports" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) var CommandPool *sync.Pool type command struct { bot *tgbotapi.BotAPI update *tgbotapi.Update msg *tgbotapi.MessageConfig delMSG *tgbotapi.DeleteMessageConfig log *slog.Logger ce ports.CeduladosService uSVC ports.UserService } // NewMessage // Factory for query handler func Newcommand(bot *tgbotapi.BotAPI, update *tgbotapi.Update, cSVC ports.CeduladosService, user ports.UserService) *command { if CommandPool == nil { CommandPool = &sync.Pool{ New: func() any { return &command{} }, } for i := 0; i < 20; i++ { CommandPool.Put(CommandPool.New()) } } log := slog.Default() log = log.With("function", "command", "chat", update.Message.From.ID, "userid", update.Message.From.ID, "username", update.Message.From.UserName) query := CommandPool.Get().(*command) query.update = update query.bot = bot query.log = log query.ce = cSVC query.uSVC = user return query } // Empty // Returns pointer to pool func (c *command) Empty() { c.update = nil c.msg = nil c.log = nil c.ce = nil c.uSVC = nil CommandPool.Put(c) } // Send // Process message sending to bot func (c *command) Send() { defer c.Empty() c.bot.Send(c.msg) if c.delMSG != nil { c.bot.Send(c.delMSG) } } func (c *command) Handler(){ msg := tgbotapi.NewMessage(c.update.Message.From.ID, "") delMSG := tgbotapi.NewDeleteMessage(c.update.Message.From.ID, c.update.Message.MessageID) c.msg = &msg c.delMSG = &delMSG command := c.update.Message.Command() switch strings.ToLower(command) { case "baned": if !checkUserIsAdmin(c){ return } bannedUserCommand(c, msg) case "appeal": appealUserCommand(c, msg) case "pending": if !checkUserIsAdmin(c){ return } pendingUserCommand(c, msg) case "users": if !checkUserIsAdmin(c){ return } usersCommand(c, msg) case "user": if !checkUserIsAdmin(c){ return } userCommand(c) case "userid": if !checkUserIsAdmin(c){ return } userIDCommand(c) case "ban": if !checkUserIsAdmin(c){ return } banCommand(c, msg) } } func GenerateAppealRequestMessage(up *tgbotapi.User, adm int64, bn string) *tgbotapi.MessageConfig { txt := fmt.Sprintf(`User %s appeal ID: %d FirstName: %s LastName: %s ChatID: %d `, up.UserName, up.ID, up.FirstName, up.LastName, up.ID) msg := tgbotapi.NewMessage(adm, txt) keyboard := tgbotapi.InlineKeyboardMarkup{} row := tgbotapi.NewInlineKeyboardRow() row = append(row, tgbotapi.NewInlineKeyboardButtonData("Unban", fmt.Sprintf("operation=unban&userID=%d&bot=%s", up.ID, bn))) row = append(row, tgbotapi.NewInlineKeyboardButtonData("Delete", fmt.Sprintf("operation=delete&userID=%d&bot=%s", up.ID, bn))) row = append(row, tgbotapi.NewInlineKeyboardButtonData("Ignore", fmt.Sprintf("operation=ignore&userID=%d&bot=%s", up.ID, bn))) keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, row) msg.ReplyMarkup = keyboard return &msg } func GenerateUserMessage(up *tgbotapi.User, adm int64, bn string) *tgbotapi.MessageConfig { txt := fmt.Sprintf(`User %s ID: %d FirstName: %s LastName: %s ChatID: %d `, up.UserName, up.ID, up.FirstName, up.LastName, up.ID) msg := tgbotapi.NewMessage(adm, txt) keyboard := tgbotapi.InlineKeyboardMarkup{} row := tgbotapi.NewInlineKeyboardRow() row = append(row, tgbotapi.NewInlineKeyboardButtonData("ban", fmt.Sprintf("operation=ban&userID=%d&bot=%s", up.ID, bn))) row = append(row, tgbotapi.NewInlineKeyboardButtonData("Delete", fmt.Sprintf("operation=delete&userID=%d&bot=%s", up.ID, bn))) row = append(row, tgbotapi.NewInlineKeyboardButtonData("Ignore", fmt.Sprintf("operation=ignore&userID=%d&bot=%s", up.ID, bn))) keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, row) msg.ReplyMarkup = keyboard return &msg } // checkUserIsAdmin // if user is admin return true else false func checkUserIsAdmin(c *command)bool{ if !auth.IsUserAdmin(c.update.Message.From.ID){ text := "only admin can execute this command" c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return false } return true } // bannedUserCommand // check for user banned on the bot and send all for revision to admin requesting it func bannedUserCommand(c *command, msg tgbotapi.MessageConfig){ buser, err := c.uSVC.GetAllBannedUsers(c.bot.Self.UserName) if err != nil { c.log.Error("error querying banned user") } if len(buser.Bans) <=0{ msg.Text = "users baned not found" c.bot.Send(msg ) return } for _, u := range buser.GetBans(){ us, _ := c.uSVC.Get(u.TgbId) tgbUs := tgbotapi.User{ ID: us.TguID, FirstName: us.FirstName, LastName: us.LastName, UserName: us.Username, } // send message to issuer msg := GenerateAppealRequestMessage(&tgbUs, c.update.SentFrom().ID, c.bot.Self.UserName) c.bot.Send(msg ) } } // appealUserCommand // user banned has the oportunity to appeal a command if is unjustify its banning func appealUserCommand(c *command, msg tgbotapi.MessageConfig){ buser, err := c.uSVC.GetAllBannedUsers(c.bot.Self.UserName) if err != nil { c.log.Error("error querying banned user") } if len(buser.GetBans())<= 0{ msg.Text = "users baned not found" c.bot.Send(msg ) return } for _, u := range buser.GetBans(){ adms, _:= auth.GetAdminFromEnv() if u.TgbId == c.update.SentFrom().ID{ for _, adm := range adms{ // send message to all admins msg := GenerateAppealRequestMessage(c.update.SentFrom(), adm, c.bot.Self.UserName) c.bot.Send(msg ) } c.bot.Send(msg) return } } } // pendingUserCommand // render pending access request for bot func pendingUserCommand(c *command, msg tgbotapi.MessageConfig){ pac, _ := c.uSVC.GetAllAccessRequest(c.bot.Self.UserName) if len(pac.Access) <= 0 { msg.Text = "there are no users access request" c.bot.Send(msg ) return } for _, ac := range pac.Access { us, err := c.uSVC.Get(ac.TgbId) if err != nil{ c.log.Error("geting user", "tgbID", us.TguID) return } tgbUS := tgbotapi.User{ UserName: us.Username, FirstName: us.FirstName, LastName: us.LastName, ID: us.TguID, } msg := auth.GenerateAccessRequestMessage(&tgbUS, c.update.Message.From.ID, c.bot.Self.UserName) c.bot.Send(msg) } } // usersCommand // render all users on the bot to ban or delete func usersCommand(c *command, msg tgbotapi.MessageConfig){ usL , err :=c.uSVC.GetAllBotsUsers(c.bot.Self.UserName) if err != nil { c.log.Error("geting users from bot") return } if len(usL) <= 0 { msg.Text = "users not found registered" c.bot.Send(msg ) return } for _, us := range usL{ if auth.IsUserAdmin(us.TguID){ continue } tgbUS := tgbotapi.User{ UserName: us.Username, FirstName: us.FirstName, LastName: us.LastName, ID: us.TguID, } msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName) c.bot.Send(msg) } } // userCommand // render an user message with querys ban delete func userCommand(c *command){ msgTXT := strings.Split(c.update.Message.Text, " ") if len(msgTXT) <= 1{ text := "command require username argument" c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } userName := msgTXT[1] usL , err :=c.uSVC.GetAllBotsUsers(c.bot.Self.UserName) if err != nil { c.log.Error("geting users from bot") return } for _, us := range usL{ if us.Username == userName{ tgbUS := tgbotapi.User{ UserName: us.Username, FirstName: us.FirstName, LastName: us.LastName, ID: us.TguID, } msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName) c.bot.Send(msg) } } } // userIDCommand // Render a message with user info and querys ban delete func userIDCommand(c *command){ msgTXT := strings.Split(c.update.Message.Text, " ") if len(msgTXT) <= 1{ text := "command require username argument" c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } userIDSTR := msgTXT[1] userID, err := strconv.ParseInt(userIDSTR, 10, 64) if err != nil { c.log.Error("geting users from bot", "error", err) text := "wrong userID argument "+ err.Error() c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } us , err :=c.uSVC.Get(userID) if err != nil { c.log.Error("geting user from bot", "error", err) text := "ERROR: "+ err.Error() c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } bots , err := c.uSVC.GetBots(us.TguID) if err != nil { c.log.Error("geting bots for user ", "error", err) text := "ERROR: "+ err.Error() c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } if !slices.Contains(bots, c.bot.Self.UserName){ c.log.Error("user is not part of bot list") text := "user is not part of bot list" c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } tgbUS := tgbotapi.User{ UserName: us.Username, FirstName: us.FirstName, LastName: us.LastName, ID: us.TguID, } msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName) c.bot.Send(msg) } // banCommand // ban an user by its user name as param func banCommand(c *command, msg tgbotapi.MessageConfig){ msgTXT := strings.Split(c.update.Message.Text, " ") if len(msgTXT) <= 1{ text := "command require username argument" c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } userName := msgTXT[1] usL , err :=c.uSVC.GetAllBotsUsers(c.bot.Self.UserName) if err != nil { c.log.Error("geting users from bot") text := "ERROR: "+ err.Error() c.bot.Send(tgbotapi.NewMessage(c.update.FromChat().ID, text)) return } for _, us := range usL{ if us.Username == userName{ tgbUS := tgbotapi.User{ UserName: us.Username, FirstName: us.FirstName, LastName: us.LastName, ID: us.TguID, } msg := GenerateUserMessage(&tgbUS, c.update.SentFrom().ID, c.bot.Self.UserName) c.bot.Send(msg) return } } msg.Text = "user not foun on bot list" c.bot.Send(msg) }