--- a/handlers/callbacks.go +++ b//handlers/callbacks.go @@ -6,7 +6,7 @@ "strings" "time" - utils "git.zio.sh/astra/telegram-join-approval-bot/pkg/utils" + utils "git.zio.sh/astra/telegram-join-approval-nuzzles/pkg/utils" api "github.com/OvyFlash/telegram-bot-api" ) @@ -39,7 +39,6 @@ bot.DeletePendingUser(args) case "decline": bot.handleDeclineRequest(query, user, userString, adminUserString) - bot.DeletePendingUser(args) case "ban": bot.showBanConfirmation(query, user) bot.API.Request(api.NewCallback(query.ID, "")) @@ -47,6 +46,19 @@ case "banc": bot.handleBanRequest(query, user, userString, adminUserString) bot.DeletePendingUser(args) + case "remind": + bot.sendReminder(query, user, userString, adminUserString) + bot.API.Request(api.NewCallback(query.ID, "Reminder sent!")) + case "cannedrespsel": + parts := strings.Split(query.Data, "_") + if len(parts) >= 3 { + var respIdx int + fmt.Sscanf(parts[2], "%d", &respIdx) + bot.sendCannedResponse(query, user, respIdx) + } + bot.DeletePendingUser(args) + bot.API.Request(api.NewCallback(query.ID, "")) + return } if bot.Config.DeleteRequestAfterDecision { @@ -54,6 +66,22 @@ } } +func (bot *Bot) sendReminder(query *api.CallbackQuery, user *ExtendedChatJoinRequest, userString, adminUserString string) { + utils.SendMessage(bot.API, user.From.ID, 0, bot.Config.ReminderMessage) + + // Edit admin message to show reminder was sent + messageText := fmt.Sprintf(AdminJoinRequestMsg, userString, user.From.ID, user.JoinReason) + messageText += fmt.Sprintf("\n\nReminder sent by: %s\nReminder sent at: %s", + adminUserString, time.Now().Format("2006-01-02 15:04:05")) + + keyboard := utils.NewApprovalKeyboard(user.From.ID) + + edit := api.NewEditMessageText(query.Message.Chat.ID, query.Message.MessageID, messageText) + edit.ParseMode = api.ModeHTML + edit.ReplyMarkup = &keyboard + bot.API.Send(edit) +} + // handleApproveRequest approves a join request and sends an approval callback. func (bot *Bot) handleApproveRequest(query *api.CallbackQuery, user *ExtendedChatJoinRequest, userString, adminUserString string) { r := api.ApproveChatJoinRequestConfig{ @@ -94,14 +122,39 @@ 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"), - defaultReason, - ), + messageText := fmt.Sprintf(AdminDeclinedMsg, + userString, user.From.ID, user.JoinReason, adminUserString, + time.Now().Format("2006-01-02 15:04:05"), + defaultReason, ) + edit := api.NewEditMessageText(query.Message.Chat.ID, query.Message.MessageID, messageText) + edit.ParseMode = api.ModeHTML + edit.Entities = query.Message.Entities + + if len(bot.Config.CannedDeclineResponses) > 0 { + var rows [][]api.InlineKeyboardButton + for i, response := range bot.Config.CannedDeclineResponses { + // Clean up the response text for button display + snippet := strings.TrimSpace(response) + snippet = strings.ReplaceAll(snippet, "\n", " ") + // Remove multiple consecutive spaces + for strings.Contains(snippet, " ") { + snippet = strings.ReplaceAll(snippet, " ", " ") + } + // Truncate to 30 chars for button text + if len(snippet) > 30 { + snippet = snippet[:30] + "..." + } + btn := api.NewInlineKeyboardButtonData(snippet, fmt.Sprintf("cannedrespsel_%d_%d", user.From.ID, i)) + rows = append(rows, []api.InlineKeyboardButton{btn}) + } + keyboard := api.NewInlineKeyboardMarkup(rows...) + edit.ReplyMarkup = &keyboard + } + + bot.API.Send(edit) + bot.API.Request(api.NewCallback(query.ID, "Join request declined.")) } @@ -176,12 +229,14 @@ userID, username, joinReason, declinedBy, declinedAt := utils.GetInfoFromMsg(repliedMsg.Text) reason := utils.EscapeHTML(update.Message.Text) - if strings.HasPrefix(update.Message.Text, "+") { - reason = utils.EscapeHTML(update.Message.Text[1:]) + if !strings.HasPrefix(update.Message.Text, "/") { + reason = utils.EscapeHTML(update.Message.Text) utils.SendMessage(bot.API, userID, 0, fmt.Sprintf("Your join request was declined for the following reason:\n\n%s", reason)) } + reason = strings.TrimPrefix(reason, "/") + utils.EditMessage(bot.API, update.Message.Chat.ID, repliedMsg.MessageID, fmt.Sprintf(AdminDeclinedMsg, username, userID, joinReason, declinedBy, declinedAt, reason)) @@ -189,6 +244,26 @@ bot.API.Send(api.NewDeleteMessage(update.Message.Chat.ID, update.Message.MessageID)) } +// sendCannedResponse sends a canned decline response to the declined user. +func (bot *Bot) sendCannedResponse(query *api.CallbackQuery, user *ExtendedChatJoinRequest, respIdx int) { + if respIdx < 0 || respIdx >= len(bot.Config.CannedDeclineResponses) { + return + } + + reason := utils.EscapeHTML(bot.Config.CannedDeclineResponses[respIdx]) + utils.SendMessage(bot.API, user.From.ID, 0, + fmt.Sprintf("Your join request was declined for the following reason:\n\n%s", reason)) + + // Extract user info from original message and reformat with the canned response + userID, username, joinReason, declinedBy, declinedAt := utils.GetInfoFromMsg(query.Message.Text) + + edit := api.NewEditMessageText(query.Message.Chat.ID, query.Message.MessageID, + fmt.Sprintf(AdminDeclinedMsg, username, userID, joinReason, declinedBy, declinedAt, reason)) + edit.ParseMode = api.ModeHTML + edit.Entities = query.Message.Entities + bot.API.Send(edit) +} + // parseCallbackData parses the action and user ID from a callback query's data string. func parseCallbackData(data string) (action string, userID int64, err error) { normalized := strings.Join(strings.Split(data, "_"), " ")