package handlers import ( "fmt" "log" "strings" "time" utils "git.zio.sh/astra/telegram-approval-join/pkg/utils" api "github.com/OvyFlash/telegram-bot-api" ) // HandleCallbackQuery processes inline button callbacks (approve/decline/leave). func (bot *Bot) HandleCallbackQuery(query *api.CallbackQuery) { data := strings.Join(strings.Split(query.Data, "_"), " ") var action string var args int64 _, err := fmt.Sscanf(data, "%s %d", &action, &args) if err != nil { log.Printf("Failed to parse callback data: %v", err) return } if action == "leave" { utils.LeaveChatRequest(bot.API, []int64{args}) utils.EditMessage(bot.API, query.Message.Chat.ID, query.Message.MessageID, fmt.Sprintf("We have left chat %d", args)) callback := api.NewCallback(query.ID, "") bot.API.Request(callback) return } user := bot.GetPendingUser(args) if user == nil { log.Printf("No pending request for user ID %d", args) msg := api.NewMessage(query.Message.Chat.ID, "Unable to find user, bot may have restarted") msg.ReplyParameters = api.ReplyParameters{MessageID: query.Message.MessageID, ChatID: query.Message.Chat.ID} r, _ := bot.API.Send(msg) edit := api.NewEditMessageText(query.Message.Chat.ID, r.ReplyToMessage.MessageID, r.ReplyToMessage.Text) edit.Entities = r.ReplyToMessage.Entities bot.API.Send(edit) callback := api.NewCallback(query.ID, "") bot.API.Request(callback) return } userString := utils.BuildUserString(&user.From) adminUserString := utils.BuildUserString(query.From) switch action { case "approve": bot.handleApproveRequest(query, user, userString, adminUserString) case "decline": bot.handleDeclineRequest(query, user, userString, adminUserString) } if bot.Config.DeleteRequestAfterDecision { deleteTimer := time.NewTimer(10 * time.Second) go func() { defer deleteTimer.Stop() <-deleteTimer.C del := api.NewDeleteMessage(query.Message.Chat.ID, query.Message.MessageID) bot.API.Send(del) }() } bot.DeletePendingUser(args) } // handleApproveRequest approves a join request and sends approval callback. func (bot *Bot) handleApproveRequest(query *api.CallbackQuery, user *ExtendedChatJoinRequest, userString, adminUserString string) { r := api.ApproveChatJoinRequestConfig{ ChatConfig: api.ChatConfig{ ChatID: user.ChatJoinRequest.Chat.ID, }, UserID: user.ChatJoinRequest.From.ID, } _, e := bot.API.Request(r) if e != nil { log.Println(e.Error()) edit := api.NewEditMessageText(query.Message.Chat.ID, query.Message.MessageID, query.Message.Text) edit.Entities = query.Message.Entities bot.API.Send(edit) return } utils.EditMessage(bot.API, query.Message.Chat.ID, query.Message.MessageID, fmt.Sprintf(AdminApprovedMsg, userString, user.From.ID, user.JoinReason, adminUserString, time.Now().Format("2006-01-02 15:04:05"))) if bot.Config.SendApprovalMessage { utils.SendMessage(bot.API, user.From.ID, 0, bot.Config.ApprovalMessage) } callback := api.NewCallback(query.ID, "Join request approved.") bot.API.Request(callback) } // handleDeclineRequest declines a join request and sends decline callback. func (bot *Bot) handleDeclineRequest(query *api.CallbackQuery, user *ExtendedChatJoinRequest, userString, adminUserString string) { r := api.DeclineChatJoinRequest{ ChatConfig: api.ChatConfig{ ChatID: user.ChatJoinRequest.Chat.ID, }, UserID: user.ChatJoinRequest.From.ID, } _, e := bot.API.Request(r) if e != nil { log.Println(e.Error()) edit := api.NewEditMessageText(query.Message.Chat.ID, query.Message.MessageID, query.Message.Text) edit.Entities = query.Message.Entities bot.API.Send(edit) return } utils.EditMessage(bot.API, query.Message.Chat.ID, query.Message.MessageID, fmt.Sprintf(AdminDeclinedMsg, userString, user.From.ID, user.JoinReason, adminUserString, time.Now().Format("2006-01-02 15:04:05"), "(no reason provided, reply to this to set one, prepend with + to also send to user)"), ) callback := api.NewCallback(query.ID, "Join request declined.") bot.API.Request(callback) } // HandleDeclineReason allows admins to provide a decline reason by replying to decline messages. func (bot *Bot) HandleDeclineReason(update *api.Update) { repliedMsg := update.Message.ReplyToMessage if !strings.Contains(repliedMsg.Text, "(no reason provided, reply to this to set one, prepend with + to also send to user)") { return } lines := strings.Split(repliedMsg.Text, "\n") userString := utils.BuildUserString(update.Message.From) if strings.TrimPrefix(lines[3], "Declined by: ") != userString { return } reason := utils.EscapeHTML(update.Message.Text) userID, username, joinReason, declinedBy, declinedAt := utils.GetInfoFromMsg(repliedMsg.Text) entities, _ := utils.FilterEntitiesByTypeWithContext(repliedMsg, "italic") if len(entities) >= 1 { username = fmt.Sprintf("%s", entities[0].Text) } if strings.HasPrefix(update.Message.Text, "+") { reason = utils.EscapeHTML(update.Message.Text[1:]) utils.SendMessage(bot.API, userID, 0, fmt.Sprintf("Your join request was declined for the following reason:\n\n%s", reason)) } utils.EditMessage(bot.API, update.Message.Chat.ID, repliedMsg.MessageID, fmt.Sprintf(AdminDeclinedMsg, username, userID, joinReason, declinedBy, declinedAt, reason)) }