191 lines
5.4 KiB
Go
191 lines
5.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
api "github.com/OvyFlash/telegram-bot-api"
|
|
)
|
|
|
|
const (
|
|
AdminJoinRequestMsg = "New join #request from _%s_\n\nJoin reason: %s"
|
|
AdminApprovedMsg = "✅ Join #request approved for _%s_\n\nJoin reason: %s\nApproved by: %s\nApproved at: %s"
|
|
AdminRejectedMsg = "❌ Join #request rejected for _%s_\n\nJoin reason: %s\nRejected by: %s\nRejected at: %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{}
|
|
err := b.Config.LoadConfig()
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
bot, err := api.NewBotAPI(b.Config.BotToken)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
b.API = bot
|
|
b.WaitingForApproval = make(map[int64]*ExtendedChatJoinRequest)
|
|
|
|
log.Printf("Authorized on account %s", bot.Self.UserName)
|
|
|
|
updateConfig := api.NewUpdate(0)
|
|
updateConfig.Timeout = 60
|
|
updatesChannel := b.API.GetUpdatesChan(updateConfig)
|
|
|
|
for update := range updatesChannel {
|
|
if update.ChatJoinRequest != nil {
|
|
b.handleJoinRequest(update.ChatJoinRequest)
|
|
continue
|
|
}
|
|
|
|
if update.CallbackQuery != nil {
|
|
b.handleCallbackQuery(update.CallbackQuery)
|
|
continue
|
|
}
|
|
|
|
if update.Message == nil {
|
|
continue
|
|
}
|
|
|
|
if user, ok := b.WaitingForApproval[update.Message.From.ID]; ok {
|
|
if update.Message.Chat.ID == update.Message.From.ID {
|
|
b.handleJoinRequestResponse(user, update.Message)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (bot *Bot) handleJoinRequestResponse(user *ExtendedChatJoinRequest, update *api.Message) {
|
|
if user.JoinReason == "" {
|
|
user.JoinReason = escapeMarkdown(update.Text)
|
|
|
|
edit := api.NewEditMessageText(bot.Config.AdminChatId, user.JoinRequestMessageID,
|
|
fmt.Sprintf(AdminJoinRequestMsg,
|
|
user.From.String(), user.JoinReason))
|
|
keyboard := newApprovalKeyboard(user.From.ID)
|
|
edit.ReplyMarkup = &keyboard
|
|
edit.ParseMode = api.ModeMarkdown
|
|
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) {
|
|
if bot.Config.TargetChatId == request.Chat.ID {
|
|
bot.WaitingForApproval[request.From.ID] = &ExtendedChatJoinRequest{
|
|
ChatJoinRequest: request,
|
|
JoinReason: "",
|
|
}
|
|
m := api.NewMessage(request.From.ID, bot.Config.EntryMessage)
|
|
m.ParseMode = api.ModeMarkdown
|
|
bot.API.Send(m)
|
|
|
|
m = api.NewMessage(bot.Config.AdminChatId,
|
|
fmt.Sprintf(AdminJoinRequestMsg, request.From.String(), "(awaiting user response)"))
|
|
m.ReplyMarkup = newApprovalKeyboard(request.From.ID)
|
|
m.ParseMode = api.ModeMarkdown
|
|
if bot.Config.AdminChatTopicId != 0 {
|
|
m.MessageThreadID = bot.Config.AdminChatTopicId
|
|
}
|
|
r, _ := bot.API.Send(m)
|
|
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
|
|
}
|
|
|
|
switch action {
|
|
case "approve":
|
|
r := api.ApproveChatJoinRequestConfig{
|
|
ChatConfig: api.ChatConfig{
|
|
ChatID: user.ChatJoinRequest.Chat.ID,
|
|
},
|
|
UserID: user.ChatJoinRequest.From.ID,
|
|
}
|
|
bot.API.Send(r)
|
|
edit := api.NewEditMessageText(bot.Config.AdminChatId, user.JoinRequestMessageID,
|
|
fmt.Sprintf(AdminApprovedMsg,
|
|
user.From.String(), user.JoinReason, query.From.String(), time.Now().Format("2006-01-02 15:04:05")))
|
|
edit.ParseMode = api.ModeMarkdown
|
|
bot.API.Send(edit)
|
|
|
|
if bot.Config.ApprovalMessage != "" {
|
|
m := api.NewMessage(user.From.ID, bot.Config.ApprovalMessage)
|
|
m.ParseMode = api.ModeMarkdown
|
|
bot.API.Send(m)
|
|
}
|
|
|
|
case "reject":
|
|
r := api.DeclineChatJoinRequest{
|
|
ChatConfig: api.ChatConfig{
|
|
ChatID: user.ChatJoinRequest.Chat.ID,
|
|
},
|
|
UserID: user.ChatJoinRequest.From.ID,
|
|
}
|
|
bot.API.Send(r)
|
|
edit := api.NewEditMessageText(bot.Config.AdminChatId, user.JoinRequestMessageID,
|
|
fmt.Sprintf(AdminRejectedMsg,
|
|
user.From.String(), user.JoinReason, query.From.String(), time.Now().Format("2006-01-02 15:04:05")))
|
|
edit.ParseMode = api.ModeMarkdown
|
|
bot.API.Send(edit)
|
|
}
|
|
|
|
delete(bot.WaitingForApproval, userId)
|
|
}
|
|
}
|
|
|
|
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 newApprovalKeyboard(userID int64) api.InlineKeyboardMarkup {
|
|
approveBtn := api.NewInlineKeyboardButtonData("✅ Approve", fmt.Sprintf("approve_%d", userID))
|
|
rejectBtn := api.NewInlineKeyboardButtonData("❌ Reject", fmt.Sprintf("reject_%d", userID))
|
|
return api.NewInlineKeyboardMarkup([]api.InlineKeyboardButton{approveBtn, rejectBtn})
|
|
}
|