Update telegram-join-approval-bot submodule and apply patches

This commit is contained in:
Astra 2026-04-02 14:28:20 +01:00
parent 334fe2bf8f
commit c848f78895
7 changed files with 582 additions and 3 deletions

180
patches/0002-nuzzles.patch Normal file
View file

@ -0,0 +1,180 @@
--- a/config/config.go
+++ b/config/config.go
@@ -8,14 +8,15 @@
)
type Config struct {
- BotToken *string `yaml:"bot_token"`
- AdminChatId *int64 `yaml:"admin_chat_id"`
- AdminChatTopicId *int `yaml:"admin_chat_topic_id"`
- TargetChatId *int64 `yaml:"target_chat_id"`
- EntryMessage string `yaml:"entry_message"`
- ApprovalMessage string `yaml:"approval_message"`
- SendApprovalMessage bool `yaml:"send_approval_message"`
- DeleteRequestAfterDecision bool `yaml:"delete_request_after_decision"`
+ BotToken *string `yaml:"bot_token"`
+ AdminChatId *int64 `yaml:"admin_chat_id"`
+ AdminChatTopicId *int `yaml:"admin_chat_topic_id"`
+ TargetChatId *int64 `yaml:"target_chat_id"`
+ EntryMessage string `yaml:"entry_message"`
+ ApprovalMessage string `yaml:"approval_message"`
+ SendApprovalMessage bool `yaml:"send_approval_message"`
+ DeleteRequestAfterDecision bool `yaml:"delete_request_after_decision"`
+ CannedDeclineResponses []string `yaml:"canned_decline_responses"`
}
func (c *Config) LoadConfig() error {
@@ -47,6 +48,7 @@
ApprovalMessage: "",
SendApprovalMessage: false,
DeleteRequestAfterDecision: false,
+ CannedDeclineResponses: []string{},
}
encoder := yaml.NewEncoder(f)
--- a/handlers/callbacks.go
+++ b/handlers/callbacks.go
@@ -36,6 +36,7 @@
switch action {
case "approve":
bot.handleApproveRequest(query, user, userString, adminUserString)
+ bot.DeletePendingUser(args)
case "decline":
bot.handleDeclineRequest(query, user, userString, adminUserString)
case "ban":
@@ -44,10 +45,19 @@
return
case "banc":
bot.handleBanRequest(query, user, userString, adminUserString)
+ bot.DeletePendingUser(args)
+ 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
}
- bot.DeletePendingUser(args)
-
if bot.Config.DeleteRequestAfterDecision {
go bot.scheduleMessageDeletion(query.Message.Chat.ID, query.Message.MessageID, 10*time.Second)
}
@@ -93,14 +103,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."))
}
@@ -175,12 +210,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))
@@ -188,6 +225,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, "_"), " ")
--- a/handlers/handlers.go 2026-03-10
+++ b/handlers/handlers.go 2026-03-11
@@ -13,7 +13,7 @@
AdminDeclinedMsg = "❌ Join #request declined for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Declined by</b>: %s\n<b>Declined at</b>: %s\n<b>Declined reason</b>: %s"
AdminBannedMsg = "🚫 Join #request banned for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Banned by</b>: %s\n<b>Banned at</b>: %s\n<b>Banned until</b>: %s"
AdminFailedMsg = "⚠️ Join #request failed for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Failure reason</b>: %s"
- defaultReason = "(no reason provided, reply to this to set one, prepend with + to also send to user)"
+ defaultReason = "(no reason provided, reply to this message to send one to the user, prepend with / to just set it)"
)
// Types shared by handler files
diff -ruN '--exclude=.git' '--exclude=.gitmodules' '--exclude=telegram-join-approval-bot' telegram-join-approval-bot/pkg/utils/utils.go telegram-join-approval-nuzzles/pkg/utils/utils.go
--- a/pkg/utils/utils.go 2026-03-10
+++ b/pkg/utils/utils.go 2026-03-11
@@ -38,7 +38,7 @@
func NewApprovalKeyboard(userID int64) api.InlineKeyboardMarkup {
approveBtn := api.NewInlineKeyboardButtonData("Approve", fmt.Sprintf("approve_%d", userID))
declineBtn := api.NewInlineKeyboardButtonData("Decline", fmt.Sprintf("decline_%d", userID))
- banBtn := api.NewInlineKeyboardButtonData("Ban", fmt.Sprintf("ban_%d", userID))
+ banBtn := api.NewInlineKeyboardButtonData("Ban (24h)", fmt.Sprintf("ban_%d", userID))
return api.NewInlineKeyboardMarkup(
[]api.InlineKeyboardButton{approveBtn, declineBtn},
[]api.InlineKeyboardButton{banBtn},

246
patches/0003-nuzzles.patch Normal file
View file

@ -0,0 +1,246 @@
--- a/config/config.go
+++ b/config/config.go
@@ -8,14 +8,16 @@
)
type Config struct {
- BotToken *string `yaml:"bot_token"`
- AdminChatId *int64 `yaml:"admin_chat_id"`
- AdminChatTopicId *int `yaml:"admin_chat_topic_id"`
- TargetChatId *int64 `yaml:"target_chat_id"`
- EntryMessage string `yaml:"entry_message"`
- ApprovalMessage string `yaml:"approval_message"`
- SendApprovalMessage bool `yaml:"send_approval_message"`
- DeleteRequestAfterDecision bool `yaml:"delete_request_after_decision"`
+ BotToken *string `yaml:"bot_token"`
+ AdminChatId *int64 `yaml:"admin_chat_id"`
+ AdminChatTopicId *int `yaml:"admin_chat_topic_id"`
+ TargetChatId *int64 `yaml:"target_chat_id"`
+ EntryMessage string `yaml:"entry_message"`
+ ApprovalMessage string `yaml:"approval_message"`
+ ReminderMessage string `yaml:"reminder_message"`
+ SendApprovalMessage bool `yaml:"send_approval_message"`
+ DeleteRequestAfterDecision bool `yaml:"delete_request_after_decision"`
+ CannedDeclineResponses []string `yaml:"canned_decline_responses"`
}
func (c *Config) LoadConfig() error {
@@ -43,10 +45,12 @@
AdminChatId: Int64Ptr(0),
AdminChatTopicId: IntPtr(0),
TargetChatId: Int64Ptr(0),
- EntryMessage: "You have requested to join the group, please write a brief message explaining why you want to join.",
+ EntryMessage: "You have requested to join the group. Please write a brief message explaining why you want to join.",
+ ReminderMessage: "Don't forget to give a one-message response to your join request.",
ApprovalMessage: "",
SendApprovalMessage: false,
DeleteRequestAfterDecision: false,
+ CannedDeclineResponses: []string{},
}
encoder := yaml.NewEncoder(f)
--- a/handlers/callbacks.go
+++ b//handlers/callbacks.go
@@ -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\n<b>Reminder sent by</b>: %s\n<b>Reminder sent at</b>: %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, "_"), " ")
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -13,7 +13,7 @@
AdminDeclinedMsg = "❌ Join #request declined for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Declined by</b>: %s\n<b>Declined at</b>: %s\n<b>Declined reason</b>: %s"
AdminBannedMsg = "🚫 Join #request banned for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Banned by</b>: %s\n<b>Banned at</b>: %s\n<b>Banned until</b>: %s"
AdminFailedMsg = "⚠️ Join #request failed for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Failure reason</b>: %s"
- defaultReason = "(no reason provided, reply to this to set one, prepend with + to also send to user)"
+ defaultReason = "(no reason provided, reply to this message to send one to the user, prepend with / to just set it)"
)
// Types shared by handler files
--- a/handlers/join.go
+++ b/handlers/join.go
@@ -19,6 +19,10 @@
userString := utils.BuildUserString(&user.From)
keyboard := utils.NewApprovalKeyboard(user.From.ID)
+ if bot.Config.ReminderMessage != "" {
+ newButton := api.NewInlineKeyboardButtonData("Send Reminder", fmt.Sprintf("remind_%d", user.From.ID))
+ keyboard.InlineKeyboard[1] = append([]api.InlineKeyboardButton{newButton}, keyboard.InlineKeyboard[1]...)
+ }
utils.EditMessageWithKeyboard(bot.API, *bot.Config.AdminChatId, user.JoinRequestMessageID,
fmt.Sprintf(AdminJoinRequestMsg, userString, user.From.ID, user.JoinReason), &keyboard)
@@ -27,12 +31,24 @@
// HandleJoinRequest initiates the join approval flow by sending the entry message and admin notification.
func (bot *Bot) HandleJoinRequest(request *api.ChatJoinRequest) {
+ // Check if user already has a pending request
+ if existingUser := bot.GetPendingUser(request.From.ID); existingUser != nil {
+ utils.SendMessage(bot.API, request.From.ID, 0,
+ "You have already requested to join. Please send a single message as your join reason.")
+ return
+ }
+
utils.SendMessage(bot.API, request.From.ID, 0, bot.Config.EntryMessage)
userString := utils.BuildUserString(&request.From)
+ keyboard := utils.NewApprovalKeyboard(request.From.ID)
+ if bot.Config.ReminderMessage != "" {
+ newButton := api.NewInlineKeyboardButtonData("Send Reminder", fmt.Sprintf("remind_%d", request.From.ID))
+ keyboard.InlineKeyboard[1] = append([]api.InlineKeyboardButton{newButton}, keyboard.InlineKeyboard[1]...)
+ }
m := api.NewMessage(*bot.Config.AdminChatId,
fmt.Sprintf(AdminJoinRequestMsg, userString, request.From.ID, "(awaiting user response)"))
- m.ReplyMarkup = utils.NewApprovalKeyboard(request.From.ID)
+ m.ReplyMarkup = keyboard
m.ParseMode = api.ModeHTML
m.LinkPreviewOptions = api.LinkPreviewOptions{IsDisabled: true}
if topic := *bot.Config.AdminChatTopicId; topic != 0 {
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -38,7 +38,7 @@
func NewApprovalKeyboard(userID int64) api.InlineKeyboardMarkup {
approveBtn := api.NewInlineKeyboardButtonData("Approve", fmt.Sprintf("approve_%d", userID))
declineBtn := api.NewInlineKeyboardButtonData("Decline", fmt.Sprintf("decline_%d", userID))
- banBtn := api.NewInlineKeyboardButtonData("Ban (24h)", fmt.Sprintf("ban_%d", userID))
+ banBtn := api.NewInlineKeyboardButtonData("⚠️ Ban (24h)aaa", fmt.Sprintf("ban_%d", userID))
return api.NewInlineKeyboardMarkup(
[]api.InlineKeyboardButton{approveBtn, declineBtn},
[]api.InlineKeyboardButton{banBtn},

View file

@ -0,0 +1,152 @@
--- 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\n<b>Reminder sent by</b>: %s\n<b>Reminder sent at</b>: %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, "_"), " ")