224 lines
7.6 KiB
Go
224 lines
7.6 KiB
Go
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/ban/leave).
|
|
func (bot *Bot) HandleCallbackQuery(query *api.CallbackQuery) {
|
|
action, args, err := parseCallbackData(query.Data)
|
|
if err != nil {
|
|
log.Printf("Failed to parse callback data: %v", err)
|
|
return
|
|
}
|
|
|
|
if action == "leave" {
|
|
bot.handleLeaveAction(query, args)
|
|
return
|
|
}
|
|
|
|
user := bot.GetPendingUser(args)
|
|
if user == nil {
|
|
log.Printf("No pending request for user ID %d", args)
|
|
bot.handleMissingUser(query)
|
|
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)
|
|
case "ban":
|
|
bot.showBanConfirmation(query, user)
|
|
bot.API.Request(api.NewCallback(query.ID, ""))
|
|
return
|
|
case "banc":
|
|
bot.handleBanRequest(query, user, userString, adminUserString)
|
|
}
|
|
|
|
bot.DeletePendingUser(args)
|
|
|
|
if bot.Config.DeleteRequestAfterDecision {
|
|
go bot.scheduleMessageDeletion(query.Message.Chat.ID, query.Message.MessageID, 10*time.Second)
|
|
}
|
|
}
|
|
|
|
// 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{
|
|
ChatConfig: api.ChatConfig{ChatID: user.ChatJoinRequest.Chat.ID},
|
|
UserID: user.ChatJoinRequest.From.ID,
|
|
}
|
|
|
|
if _, err := bot.API.Request(r); err != nil {
|
|
log.Println(err)
|
|
bot.restoreMessage(query)
|
|
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)
|
|
}
|
|
|
|
bot.API.Request(api.NewCallback(query.ID, "Join request approved."))
|
|
}
|
|
|
|
// handleDeclineRequest declines a join request and sends a 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,
|
|
}
|
|
|
|
if _, err := bot.API.Request(r); err != nil {
|
|
log.Println(err)
|
|
bot.restoreMessage(query)
|
|
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,
|
|
),
|
|
)
|
|
|
|
bot.API.Request(api.NewCallback(query.ID, "Join request declined."))
|
|
}
|
|
|
|
// handleBanRequest bans a user from the chat for 24 hours.
|
|
func (bot *Bot) handleBanRequest(query *api.CallbackQuery, user *ExtendedChatJoinRequest, userString, adminUserString string) {
|
|
r := api.BanChatMemberConfig{
|
|
ChatMemberConfig: api.ChatMemberConfig{
|
|
ChatConfig: api.ChatConfig{ChatID: user.ChatJoinRequest.Chat.ID},
|
|
UserID: user.ChatJoinRequest.From.ID,
|
|
},
|
|
UntilDate: time.Now().Add(24 * time.Hour).Unix(),
|
|
}
|
|
|
|
if _, err := bot.API.Request(r); err != nil {
|
|
log.Println(err)
|
|
bot.restoreMessage(query)
|
|
return
|
|
}
|
|
|
|
bannedUntil := time.Now().Add(24 * time.Hour).Format("2006-01-02 15:04:05")
|
|
utils.EditMessage(bot.API, query.Message.Chat.ID, query.Message.MessageID,
|
|
fmt.Sprintf(AdminBannedMsg,
|
|
userString, user.From.ID, user.JoinReason, adminUserString,
|
|
time.Now().Format("2006-01-02 15:04:05"), bannedUntil,
|
|
),
|
|
)
|
|
|
|
bot.API.Request(api.NewCallback(query.ID, "User banned."))
|
|
}
|
|
|
|
// showBanConfirmation displays a confirmation prompt for the ban action.
|
|
func (bot *Bot) showBanConfirmation(query *api.CallbackQuery, user *ExtendedChatJoinRequest) {
|
|
approveBtn := api.NewInlineKeyboardButtonData("Approve", fmt.Sprintf("approve_%d", user.From.ID))
|
|
declineBtn := api.NewInlineKeyboardButtonData("Decline", fmt.Sprintf("decline_%d", user.From.ID))
|
|
confirmBtn := api.NewInlineKeyboardButtonData("Ban (confirm)", fmt.Sprintf("banc_%d", user.From.ID))
|
|
keyboard := api.NewInlineKeyboardMarkup(
|
|
[]api.InlineKeyboardButton{approveBtn, declineBtn},
|
|
[]api.InlineKeyboardButton{confirmBtn},
|
|
)
|
|
|
|
edit := api.NewEditMessageText(query.Message.Chat.ID, query.Message.MessageID, query.Message.Text)
|
|
edit.ReplyMarkup = &keyboard
|
|
edit.ParseMode = api.ModeHTML
|
|
edit.Entities = query.Message.Entities
|
|
bot.API.Send(edit)
|
|
}
|
|
|
|
// 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, defaultReason) {
|
|
return
|
|
}
|
|
|
|
lines := strings.Split(repliedMsg.Text, "\n")
|
|
userString := utils.BuildUserString(update.Message.From)
|
|
if strings.TrimPrefix(lines[3], "Declined by: ") != userString {
|
|
return
|
|
}
|
|
|
|
userID, username, joinReason, declinedBy, declinedAt := utils.GetInfoFromMsg(repliedMsg.Text)
|
|
if entities, _ := utils.FilterEntitiesByTypeWithContext(repliedMsg, "italic"); len(entities) >= 1 {
|
|
username = fmt.Sprintf("<i>%s</i>", entities[0].Text)
|
|
}
|
|
|
|
reason := utils.EscapeHTML(update.Message.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))
|
|
}
|
|
|
|
// 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, "_"), " ")
|
|
_, err = fmt.Sscanf(normalized, "%s %d", &action, &userID)
|
|
return
|
|
}
|
|
|
|
// handleLeaveAction handles the "leave" callback by leaving the specified chat.
|
|
func (bot *Bot) handleLeaveAction(query *api.CallbackQuery, chatID int64) {
|
|
utils.LeaveChatRequest(bot.API, []int64{chatID})
|
|
utils.EditMessage(bot.API, query.Message.Chat.ID, query.Message.MessageID,
|
|
fmt.Sprintf("We have left chat <i>%d</i>", chatID))
|
|
bot.API.Request(api.NewCallback(query.ID, ""))
|
|
}
|
|
|
|
// handleMissingUser notifies the admin that the user's pending request could not be found.
|
|
func (bot *Bot) handleMissingUser(query *api.CallbackQuery) {
|
|
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)
|
|
|
|
bot.API.Request(api.NewCallback(query.ID, ""))
|
|
}
|
|
|
|
// restoreMessage restores a message to its original state after a failed API request.
|
|
func (bot *Bot) restoreMessage(query *api.CallbackQuery) {
|
|
edit := api.NewEditMessageText(query.Message.Chat.ID, query.Message.MessageID, query.Message.Text)
|
|
edit.Entities = query.Message.Entities
|
|
bot.API.Send(edit)
|
|
}
|
|
|
|
// scheduleMessageDeletion deletes a message after a given delay.
|
|
func (bot *Bot) scheduleMessageDeletion(chatID int64, messageID int, delay time.Duration) {
|
|
timer := time.NewTimer(delay)
|
|
defer timer.Stop()
|
|
<-timer.C
|
|
bot.API.Send(api.NewDeleteMessage(chatID, messageID))
|
|
}
|