Code refactor
This commit is contained in:
parent
5e37545782
commit
d2f7297d92
7 changed files with 534 additions and 330 deletions
159
handlers/callbacks.go
Normal file
159
handlers/callbacks.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
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 <i>%d</i>", 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.ApprovalMessage != "" {
|
||||
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("<i>%s</i>", 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))
|
||||
}
|
||||
60
handlers/handlers.go
Normal file
60
handlers/handlers.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
config "git.zio.sh/astra/telegram-approval-join/config"
|
||||
api "github.com/OvyFlash/telegram-bot-api"
|
||||
)
|
||||
|
||||
const (
|
||||
AdminJoinRequestMsg = "New join #request from %s [<code>%d</code>]\n\n<b>Join reason</b>: %s"
|
||||
AdminApprovedMsg = "✅ Join #request approved for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Approved by</b>: %s\n<b>Approved at</b>: %s"
|
||||
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"
|
||||
AdminFailedMsg = "⚠️ Join #request failed for %s [<code>%d</code>]\n\n<b>Join reason</b>: %s\n<b>Failure reason</b>: %s"
|
||||
BotApprovalEnabled = "Join approval bot enabled, to get started, add the bot to the target group and make it an admin with invite permissions, then use <code>/targetchat <chat_id></code> to add the group to the config.\n\nTo set the entry message that users will receive when they request to join, use <code>/setentrymessage <entry_message></code> in the admin chat. You can use HTML formatting in the entry message."
|
||||
BotAddedToGroup = "Hello! I help out with join approvals.\n\nTo get started, make sure this bot is added to the admin group where it will send join approvals to be accepted or declined, then type /request when you have done that."
|
||||
BotRequestMsg = "You have requested this chat to be the admin chat, please wait for it to be activated."
|
||||
)
|
||||
|
||||
// Types shared by handler files
|
||||
type ExtendedChatJoinRequest struct {
|
||||
*api.ChatJoinRequest
|
||||
JoinReason string
|
||||
JoinRequestMessageID int
|
||||
}
|
||||
|
||||
type Bot struct {
|
||||
API *api.BotAPI
|
||||
Config config.Config
|
||||
mu sync.RWMutex
|
||||
WaitingForApproval map[int64]*ExtendedChatJoinRequest
|
||||
}
|
||||
|
||||
// GetPendingUser retrieves a pending user request (read-safe).
|
||||
func (bot *Bot) GetPendingUser(userID int64) *ExtendedChatJoinRequest {
|
||||
bot.mu.RLock()
|
||||
defer bot.mu.RUnlock()
|
||||
user := bot.WaitingForApproval[userID]
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
// SetPendingUser stores a pending user request (write-safe).
|
||||
func (bot *Bot) SetPendingUser(userID int64, user *ExtendedChatJoinRequest) {
|
||||
bot.mu.Lock()
|
||||
defer bot.mu.Unlock()
|
||||
if _, ok := bot.WaitingForApproval[userID]; !ok {
|
||||
bot.WaitingForApproval = make(map[int64]*ExtendedChatJoinRequest)
|
||||
}
|
||||
bot.WaitingForApproval[userID] = user
|
||||
}
|
||||
|
||||
// DeletePendingUser removes a pending user request (write-safe).
|
||||
func (bot *Bot) DeletePendingUser(userID int64) {
|
||||
bot.mu.Lock()
|
||||
defer bot.mu.Unlock()
|
||||
delete(bot.WaitingForApproval, userID)
|
||||
}
|
||||
72
handlers/join.go
Normal file
72
handlers/join.go
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
utils "git.zio.sh/astra/telegram-approval-join/pkg/utils"
|
||||
api "github.com/OvyFlash/telegram-bot-api"
|
||||
)
|
||||
|
||||
// HandleJoinRequestResponse records the user's join reason and notifies admins.
|
||||
func (bot *Bot) HandleJoinRequestResponse(user *ExtendedChatJoinRequest, update *api.Message) {
|
||||
if user.JoinReason == "" {
|
||||
user.JoinReason = utils.EscapeHTML(update.Text)
|
||||
userString := utils.BuildUserString(&user.From)
|
||||
|
||||
keyboard := utils.NewApprovalKeyboard(user.From.ID)
|
||||
utils.EditMessageWithKeyboard(bot.API, *bot.Config.AdminChatId, user.JoinRequestMessageID,
|
||||
fmt.Sprintf(AdminJoinRequestMsg,
|
||||
userString, user.From.ID, user.JoinReason), &keyboard)
|
||||
|
||||
utils.SendMessage(bot.API, update.From.ID, 0, "Thank you! Your request has been sent to the admins for review.")
|
||||
} else {
|
||||
utils.SendMessage(bot.API, update.From.ID, 0, "Your request is already pending approval.")
|
||||
}
|
||||
}
|
||||
|
||||
// HandleJoinRequest initiates join approval flow by sending entry message and admin notification.
|
||||
func (bot *Bot) HandleJoinRequest(request *api.ChatJoinRequest) {
|
||||
// if chat is not in config, ignore
|
||||
if *bot.Config.TargetChatId != request.Chat.ID {
|
||||
m := api.NewMessage(*bot.Config.AdminChatId,
|
||||
fmt.Sprintf("Received join request for chat %s (<code>%d</code>), but it's not in config, ignoring",
|
||||
request.Chat.Title, request.Chat.ID))
|
||||
leaveBtn := api.NewInlineKeyboardButtonData("Leave Chat", fmt.Sprintf("leave_%d", request.Chat.ID))
|
||||
m.ReplyMarkup = api.NewInlineKeyboardMarkup([]api.InlineKeyboardButton{leaveBtn})
|
||||
m.ParseMode = api.ModeHTML
|
||||
bot.API.Send(m)
|
||||
return
|
||||
}
|
||||
|
||||
utils.SendMessage(bot.API, request.From.ID, 0, bot.Config.EntryMessage)
|
||||
userString := utils.BuildUserString(&request.From)
|
||||
|
||||
m := api.NewMessage(*bot.Config.AdminChatId,
|
||||
fmt.Sprintf(AdminJoinRequestMsg, userString, request.From.ID, "(awaiting user response)"))
|
||||
m.ReplyMarkup = utils.NewApprovalKeyboard(request.From.ID)
|
||||
m.ParseMode = api.ModeHTML
|
||||
if topic := *bot.Config.AdminChatTopicId; topic != 0 {
|
||||
m.MessageThreadID = topic
|
||||
}
|
||||
r, err := bot.API.Send(m)
|
||||
if err != nil {
|
||||
log.Printf("Failed to send join request to admin chat: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
bot.SetPendingUser(request.From.ID, &ExtendedChatJoinRequest{
|
||||
ChatJoinRequest: request,
|
||||
JoinReason: "",
|
||||
JoinRequestMessageID: r.MessageID,
|
||||
})
|
||||
}
|
||||
|
||||
// SendFailureMessage edits the admin request message to show a failure status.
|
||||
func (bot *Bot) SendFailureMessage(user *ExtendedChatJoinRequest, query *api.CallbackQuery, userString string) {
|
||||
utils.EditMessage(bot.API, *bot.Config.AdminChatId, user.JoinRequestMessageID,
|
||||
fmt.Sprintf(AdminFailedMsg, userString, user.From.ID, utils.EscapeHTML(user.JoinReason), "User not found in requests."))
|
||||
|
||||
callback := api.NewCallback(query.ID, "Join request failed.")
|
||||
bot.API.Request(callback)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue