2024-12-18 11:08:21 -04:00

383 lines
10 KiB
Go

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)
}