Code refactor
This commit is contained in:
parent
5e37545782
commit
d2f7297d92
7 changed files with 534 additions and 330 deletions
293
main.go
293
main.go
|
|
@ -1,49 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.zio.sh/astra/telegram-approval-join/config"
|
||||
"git.zio.sh/astra/telegram-approval-join/handlers"
|
||||
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"
|
||||
)
|
||||
|
||||
type ExtendedChatJoinRequest struct {
|
||||
*api.ChatJoinRequest
|
||||
JoinReason string
|
||||
JoinRequestMessageID int
|
||||
}
|
||||
|
||||
type Bot struct {
|
||||
API *api.BotAPI
|
||||
WaitingForApproval map[int64]*ExtendedChatJoinRequest
|
||||
Config Config
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := &Bot{}
|
||||
b.Config = Config{}
|
||||
b := &handlers.Bot{}
|
||||
b.Config = config.Config{}
|
||||
err := b.Config.LoadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
bot, err := api.NewBotAPI(b.Config.BotToken)
|
||||
if b.Config.BotToken == nil {
|
||||
log.Fatal("Edit config.yaml and fill out bot token")
|
||||
}
|
||||
bot, err := api.NewBotAPI(*b.Config.BotToken)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b.API = bot
|
||||
b.WaitingForApproval = make(map[int64]*ExtendedChatJoinRequest)
|
||||
b.WaitingForApproval = make(map[int64]*handlers.ExtendedChatJoinRequest)
|
||||
|
||||
log.Printf("Authorized on account %s", bot.Self.UserName)
|
||||
|
||||
|
|
@ -53,14 +35,14 @@ func main() {
|
|||
|
||||
for update := range updatesChannel {
|
||||
if update.ChatJoinRequest != nil {
|
||||
if update.ChatJoinRequest.Chat.ID == b.Config.TargetChatId {
|
||||
b.handleJoinRequest(update.ChatJoinRequest)
|
||||
if update.ChatJoinRequest.Chat.ID == *b.Config.TargetChatId {
|
||||
b.HandleJoinRequest(update.ChatJoinRequest)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if update.CallbackQuery != nil {
|
||||
b.handleCallbackQuery(update.CallbackQuery)
|
||||
b.HandleCallbackQuery(update.CallbackQuery)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -70,257 +52,12 @@ func main() {
|
|||
|
||||
if user, ok := b.WaitingForApproval[update.Message.From.ID]; ok {
|
||||
if update.Message.Chat.ID == update.Message.From.ID {
|
||||
b.handleJoinRequestResponse(user, update.Message)
|
||||
b.HandleJoinRequestResponse(user, update.Message)
|
||||
}
|
||||
}
|
||||
|
||||
if update.Message.Chat.ID == b.Config.AdminChatId && update.Message.ReplyToMessage != nil {
|
||||
// handle admin declineion reason
|
||||
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)") {
|
||||
// now we need to make sure the one that declined it is the one replying
|
||||
lines := strings.Split(repliedMsg.Text, "\n")
|
||||
userString := ""
|
||||
if update.Message.From.UserName != "" {
|
||||
userString = "@" + update.Message.From.UserName
|
||||
} else {
|
||||
userString = fmt.Sprintf("%s %s", update.Message.From.FirstName, update.Message.From.LastName)
|
||||
}
|
||||
if strings.TrimPrefix(lines[3], "Declined by: ") == userString {
|
||||
reason := EscapeHTML(update.Message.Text)
|
||||
userID, username, joinReason, declinedBy, declinedAt := GetInfoFromMsg(repliedMsg.Text)
|
||||
if strings.HasPrefix(update.Message.Text, "+") {
|
||||
reason = EscapeHTML(update.Message.Text[1:])
|
||||
m := api.NewMessage(userID, fmt.Sprintf("Your join request was declined for the following reason:\n\n%s",
|
||||
reason))
|
||||
b.API.Send(m)
|
||||
}
|
||||
edit := api.NewEditMessageText(b.Config.AdminChatId, repliedMsg.MessageID,
|
||||
fmt.Sprintf(AdminDeclinedMsg,
|
||||
username, userID, joinReason, declinedBy, declinedAt, reason))
|
||||
edit.ParseMode = api.ModeHTML
|
||||
b.API.Send(edit)
|
||||
}
|
||||
}
|
||||
if update.Message.Chat.ID == *b.Config.AdminChatId && update.Message.ReplyToMessage != nil {
|
||||
b.HandleDeclineReason(&update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) handleJoinRequestResponse(user *ExtendedChatJoinRequest, update *api.Message) {
|
||||
if user.JoinReason == "" {
|
||||
user.JoinReason = EscapeHTML(update.Text)
|
||||
|
||||
userString := ""
|
||||
if user.From.UserName != "" {
|
||||
userString = "@" + user.From.UserName
|
||||
} else {
|
||||
userString = fmt.Sprintf("%s %s", user.From.FirstName, user.From.LastName)
|
||||
}
|
||||
edit := api.NewEditMessageText(bot.Config.AdminChatId, user.JoinRequestMessageID,
|
||||
fmt.Sprintf(AdminJoinRequestMsg,
|
||||
userString, user.From.ID, user.JoinReason))
|
||||
keyboard := NewApprovalKeyboard(user.From.ID)
|
||||
edit.ReplyMarkup = &keyboard
|
||||
edit.ParseMode = api.ModeHTML
|
||||
bot.API.Send(edit)
|
||||
|
||||
ack := api.NewMessage(update.From.ID, "Thank you! Your request has been sent to the admins for review.")
|
||||
bot.API.Send(ack)
|
||||
} else {
|
||||
m := api.NewMessage(update.From.ID, "Your request is already pending approval.")
|
||||
bot.API.Send(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) handleJoinRequest(request *api.ChatJoinRequest) {
|
||||
bot.WaitingForApproval[request.From.ID] = &ExtendedChatJoinRequest{
|
||||
ChatJoinRequest: request,
|
||||
JoinReason: "",
|
||||
}
|
||||
m := api.NewMessage(request.From.ID, bot.Config.EntryMessage)
|
||||
m.ParseMode = api.ModeHTML
|
||||
bot.API.Send(m)
|
||||
|
||||
userString := ""
|
||||
if request.From.UserName != "" {
|
||||
userString = "@" + request.From.UserName
|
||||
} else {
|
||||
userString = fmt.Sprintf("%s %s", request.From.FirstName, request.From.LastName)
|
||||
}
|
||||
m = api.NewMessage(bot.Config.AdminChatId,
|
||||
fmt.Sprintf(AdminJoinRequestMsg, userString, request.From.ID, "(awaiting user response)"))
|
||||
m.ReplyMarkup = NewApprovalKeyboard(request.From.ID)
|
||||
m.ParseMode = api.ModeHTML
|
||||
if bot.Config.AdminChatTopicId != 0 {
|
||||
m.MessageThreadID = bot.Config.AdminChatTopicId
|
||||
}
|
||||
r, err := bot.API.Send(m)
|
||||
if err != nil {
|
||||
log.Printf("Failed to send join request to admin chat: %v", err)
|
||||
return
|
||||
}
|
||||
bot.WaitingForApproval[request.From.ID].JoinRequestMessageID = r.MessageID
|
||||
}
|
||||
|
||||
func (bot *Bot) handleCallbackQuery(query *api.CallbackQuery) {
|
||||
data := strings.Join(strings.Split(query.Data, "_"), " ")
|
||||
var userId int64
|
||||
var action string
|
||||
_, err := fmt.Sscanf(data, "%s %d", &action, &userId)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse callback data: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// handle callbacks from admin chat
|
||||
if query.Message.Chat.ID == bot.Config.AdminChatId {
|
||||
user, exists := bot.WaitingForApproval[userId]
|
||||
if !exists {
|
||||
log.Printf("No pending request for user ID %d", userId)
|
||||
return
|
||||
}
|
||||
|
||||
userString := ""
|
||||
if user.From.UserName != "" {
|
||||
userString = "@" + user.From.UserName
|
||||
} else {
|
||||
userString = fmt.Sprintf("%s %s (no username)", user.From.FirstName, user.From.LastName)
|
||||
}
|
||||
|
||||
adminUserString := ""
|
||||
if query.From.UserName != "" {
|
||||
adminUserString = "@" + query.From.UserName
|
||||
} else {
|
||||
adminUserString = fmt.Sprintf("%s %s", query.From.FirstName, query.From.LastName)
|
||||
}
|
||||
|
||||
switch action {
|
||||
case "approve":
|
||||
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())
|
||||
bot.sendFailureMessage(user, query, userString)
|
||||
return
|
||||
}
|
||||
|
||||
edit := api.NewEditMessageText(bot.Config.AdminChatId, user.JoinRequestMessageID,
|
||||
fmt.Sprintf(AdminApprovedMsg,
|
||||
userString, user.From.ID, user.JoinReason, adminUserString, time.Now().Format("2006-01-02 15:04:05")))
|
||||
edit.ParseMode = api.ModeHTML
|
||||
bot.API.Send(edit)
|
||||
|
||||
if bot.Config.ApprovalMessage != "" {
|
||||
m := api.NewMessage(user.From.ID, bot.Config.ApprovalMessage)
|
||||
m.ParseMode = api.ModeHTML
|
||||
bot.API.Send(m)
|
||||
}
|
||||
|
||||
callback := api.NewCallback(query.ID, "Join request approved.")
|
||||
bot.API.Request(callback)
|
||||
|
||||
case "decline":
|
||||
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())
|
||||
bot.sendFailureMessage(user, query, userString)
|
||||
return
|
||||
}
|
||||
|
||||
edit := api.NewEditMessageText(bot.Config.AdminChatId, user.JoinRequestMessageID,
|
||||
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)"))
|
||||
edit.ParseMode = api.ModeHTML
|
||||
bot.API.Send(edit)
|
||||
|
||||
callback := api.NewCallback(query.ID, "Join request declined.")
|
||||
bot.API.Request(callback)
|
||||
}
|
||||
|
||||
if bot.Config.DeleteRequestAfterDecision {
|
||||
deleteTimer := time.NewTimer(10 * time.Second)
|
||||
go func() {
|
||||
<-deleteTimer.C
|
||||
|
||||
del := api.NewDeleteMessage(bot.Config.AdminChatId, user.JoinRequestMessageID)
|
||||
bot.API.Send(del)
|
||||
}()
|
||||
}
|
||||
|
||||
delete(bot.WaitingForApproval, userId)
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) sendFailureMessage(user *ExtendedChatJoinRequest, query *api.CallbackQuery, userString string) {
|
||||
edit := api.NewEditMessageText(bot.Config.AdminChatId, user.JoinRequestMessageID,
|
||||
fmt.Sprintf(AdminFailedMsg, userString, user.From.ID, user.JoinReason, "User not found in requests."))
|
||||
edit.ParseMode = api.ModeHTML
|
||||
bot.API.Send(edit)
|
||||
|
||||
callback := api.NewCallback(query.ID, "Join request failed.")
|
||||
bot.API.Request(callback)
|
||||
}
|
||||
|
||||
func EscapeMarkdown(s string) string {
|
||||
toEscape := []string{"*", "_", "`", "[", "]", "(", ")", "\\", "#", "-"}
|
||||
|
||||
replacements := make([]string, 0, len(toEscape)*2)
|
||||
for _, char := range toEscape {
|
||||
replacements = append(replacements, char, "\\"+char)
|
||||
}
|
||||
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
return replacer.Replace(s)
|
||||
}
|
||||
|
||||
func EscapeHTML(s string) string {
|
||||
toEscape := []string{"&", "<", ">", "\"", "'"}
|
||||
|
||||
replacements := make([]string, 0, len(toEscape)*2)
|
||||
replacements = append(replacements, "&", "&")
|
||||
replacements = append(replacements, "<", "<")
|
||||
replacements = append(replacements, ">", ">")
|
||||
replacements = append(replacements, "\"", """)
|
||||
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
return replacer.Replace(s)
|
||||
}
|
||||
|
||||
func NewApprovalKeyboard(userID int64) api.InlineKeyboardMarkup {
|
||||
approveBtn := api.NewInlineKeyboardButtonData("Approve", fmt.Sprintf("approve_%d", userID))
|
||||
declineBtn := api.NewInlineKeyboardButtonData("Decline", fmt.Sprintf("decline_%d", userID))
|
||||
return api.NewInlineKeyboardMarkup([]api.InlineKeyboardButton{approveBtn, declineBtn})
|
||||
}
|
||||
|
||||
func GetInfoFromMsg(msg string) (userId int64, username, joinReason, declinedBy, declinedAt string) {
|
||||
lines := strings.Split(msg, "\n")
|
||||
|
||||
joinReason = string([]rune(lines[2])[len([]rune("Join reason: ")):])
|
||||
declinedBy = string([]rune(lines[3])[len([]rune("Declined by: ")):])
|
||||
declinedAt = string([]rune(lines[4])[len([]rune("Declined at: ")):])
|
||||
index := LastIndexRuneInRunes([]rune(lines[0]), '[')
|
||||
userID, _ := strconv.Atoi(string([]rune(lines[0])[index+1 : len([]rune(lines[0]))-1]))
|
||||
|
||||
return int64(userID), username, joinReason, declinedBy, declinedAt
|
||||
}
|
||||
|
||||
func LastIndexRuneInRunes(runes []rune, r rune) int {
|
||||
for i := len(runes) - 1; i >= 0; i-- {
|
||||
if runes[i] == r {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue