package main import ( "fmt" "io" "log" "net/http" "os" "strconv" "strings" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) var adminCommands = map[string]func(BotController, tgbotapi.Update, User){ "/secret": handleSecretCommand, // activate admin mode via /secret `AdminPass` "/panel": handlePanelCommand, // open bot settings "/usermode": handleDefaultMessage, // temporarly disable admin mode to test ui "/id": handleDefaultMessage, // to check id of chat "/setchannelid": handleDefaultMessage, // just type it in channel which one is supposed to be lined with bot } func main() { var bc = GetBotController() for update := range bc.updates { go ProcessUpdate(bc, update) } } func ProcessUpdate(bc BotController, update tgbotapi.Update) { if update.Message != nil { var UserID = update.Message.From.ID user := bc.GetUser(UserID) bc.LogMessage(update) text := update.Message.Text if strings.HasPrefix(text, "/") { handleCommand(bc, update, user) } else { handleDefaultMessage(bc, update, user) } } else if update.CallbackQuery != nil { handleCallbackQuery(bc, update) } else if update.ChannelPost != nil { handleChannelPost(bc, update) } } func handleCommand(bc BotController, update tgbotapi.Update, user User) { msg := update.Message log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) log.Printf("[Entities] %s", update.Message.Entities) log.Printf("[COMMAND] %s", update.Message.Command()) command := "/" + msg.Command() // if it is not a command, then it will simply be "/" if user.IsAdmin() { f, exists := adminCommands[command] // f is a function that handles specified command if exists { f(bc, update, user) return } } // commands for non-admins switch command { case "/start": handleStartCommand(bc, update, user) case "/secret": handleSecretCommand(bc, update, user) } } func handleCallbackQuery(bc BotController, update tgbotapi.Update) { user := bc.GetUser(update.CallbackQuery.From.ID) if update.CallbackQuery.Data == "leave_ticket_button" { handleLeaveTicketButton(bc, update, user) } else if user.IsEffectiveAdmin() { handleAdminCallback(bc, update, user) } if user.IsAdmin() && update.CallbackQuery.Data == "panel" { handlePanelCallback(bc, update, user) } canswer := tgbotapi.NewCallback(update.CallbackQuery.ID, "") bc.bot.Send(canswer) } func handleChannelPost(bc BotController, update tgbotapi.Update) { post := update.ChannelPost if post.Text == "setchannelid" { bc.SetBotContent("channelid", strconv.FormatInt(post.SenderChat.ID, 10)) var admins []User bc.db.Where("role_bitmask & 1 = ?", 1).Find(&admins) for _, admin := range admins { bc.bot.Send(tgbotapi.NewMessage(admin.ID, "ChannelID is set to "+strconv.FormatInt(post.SenderChat.ID, 10))) delcmd := tgbotapi.NewDeleteMessage(post.SenderChat.ID, post.MessageID) bc.bot.Send(delcmd) } } } // Helper functions for specific commands func handleStartCommand(bc BotController, update tgbotapi.Update, user User) { bc.db.Model(&user).Update("state", "start") kbd := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(bc.GetBotContent("leave_ticket_button"), "leave_ticket_button"), ), ) img, err := bc.GetBotContentVerbose("preview_image") if err != nil || img == "" { msg := tgbotapi.NewMessage(update.Message.Chat.ID, bc.GetBotContent("start")) msg.ParseMode = "markdown" msg.ReplyMarkup = kbd bc.bot.Send(msg) } else { msg := tgbotapi.NewPhoto(update.Message.Chat.ID, tgbotapi.FileID(img)) msg.Caption = bc.GetBotContent("start") msg.ReplyMarkup = kbd bc.bot.Send(msg) } } func handleSecretCommand(bc BotController, update tgbotapi.Update, user User) { if update.Message.CommandArguments() == bc.cfg.AdminPass || user.IsAdmin() { bc.db.Model(&user).Update("state", "start") bc.db.Model(&user).Update("RoleBitmask", user.RoleBitmask|0b11) // set real admin ID (0b1) and effective admin toggle (0b10) msg := tgbotapi.NewMessage(update.Message.Chat.ID, "You are admin now!") bc.bot.Send(msg) } } func handlePanelCommand(bc BotController, update tgbotapi.Update, user User) { if !user.IsAdmin() {return} if !user.IsEffectiveAdmin() { bc.db.Model(&user).Update("RoleBitmask", user.RoleBitmask|0b10) msg := tgbotapi.NewMessage(update.Message.Chat.ID, "You was in usermode, turned back to admin mode...") bc.bot.Send(msg) } kbd := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Стартовая картинка", "update:preview_image")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Приветственный текст", "update:start")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Кнопка для заявки", "update:leave_ticket_button")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("ID чата", "update:supportchatid")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("ID канала", "update:channelid")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Уведомление об отправке тикета", "update:sended_notify")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Просьба оставить тикет", "update:leaveticket_message")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Просьба подписаться на канал", "update:subscribe_message")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Ссылка на канал", "update:channel_link")), ) msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Выберите пункт для изменения") msg.ReplyMarkup = kbd bc.bot.Send(msg) } func handleUserModeCommand(bc BotController, update tgbotapi.Update, user User) { bc.db.Model(&user).Update("RoleBitmask", user.RoleBitmask&(^uint(0b10))) log.Printf("Set role bitmask (%b) for user: %d", user.RoleBitmask, user.ID) msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Simulating user experience!") bc.bot.Send(msg) } func handleDefaultMessage(bc BotController, update tgbotapi.Update, user User) { if user.State == "leaveticket" { f := update.Message.From ticket := fmt.Sprintf("User: %s %s\nUsername: %s\nText:\n", f.FirstName, f.LastName, f.UserName) ticket += update.Message.Text chatidstr, err := bc.GetBotContentVerbose("supportchatid") if err != nil { var admins []User bc.db.Where("role_bitmask & 1 = ?", 1).Find(&admins) for _, admin := range admins { msg := tgbotapi.NewMessage(admin.ID, "Support ChatID is not set!!!") msg.Entities = []tgbotapi.MessageEntity{tgbotapi.MessageEntity{ Type: "code", Offset: 1, Length: 2, }} bc.bot.Send(msg) } } chatid, _ := strconv.ParseInt(chatidstr, 10, 64) _, err = bc.bot.Send(tgbotapi.NewMessage(chatid, ticket)) if err != nil { msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Something went wrong, try again...") bc.bot.Send(msg) return } bc.db.Model(&user).Update("state", "start") msg := tgbotapi.NewMessage(update.Message.Chat.ID, bc.GetBotContent("sended_notify")) bc.bot.Send(msg) } else if user.IsEffectiveAdmin() { if user.State != "start" { if strings.HasPrefix(user.State, "imgset:") { Literal := strings.Split(user.State, ":")[1] if update.Message.Text == "unset" { var l BotContent bc.db.First(&l, "Literal", Literal) bc.SetBotContent(Literal, "") } maxsize := 0 fileid := "" for _, p := range update.Message.Photo { if p.FileSize > maxsize { fileid = p.FileID maxsize = p.FileSize } } bc.SetBotContent(Literal, fileid) bc.db.Model(&user).Update("state", "start") msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Successfully set new image!") bc.bot.Send(msg) } else if strings.HasPrefix(user.State, "stringset:") { Literal := strings.Split(user.State, ":")[1] bc.SetBotContent(Literal, update.Message.Text) bc.db.Model(&user).Update("state", "start") msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Successfully set new text!") bc.bot.Send(msg) } } } } func handleLeaveTicketButton(bc BotController, update tgbotapi.Update, user User) { chatidstr, err := bc.GetBotContentVerbose("channelid") if err != nil { var admins []User bc.db.Where("role_bitmask & 1 = ?", 1).Find(&admins) for _, admin := range admins { bc.bot.Send(tgbotapi.NewMessage(admin.ID, "ChannelID is not set!!!")) } } chatid, _ := strconv.ParseInt(chatidstr, 10, 64) member, err := bc.bot.GetChatMember(tgbotapi.GetChatMemberConfig{ ChatConfigWithUser: tgbotapi.ChatConfigWithUser{ UserID: update.CallbackQuery.From.ID, SuperGroupUsername: chatidstr, ChatID: chatid, }, }) if err != nil { if strings.Contains(err.Error(), "chat not found") { bc.bot.Send(tgbotapi.NewMessage(user.ID, "No channel ID is set!!!")) } } log.Printf("M: %s, E: %s", member, err) s := member.Status if s == "member" || s == "creator" || s == "admin" { bc.db.Model(&user).Update("state", "leaveticket") bc.bot.Send(tgbotapi.NewMessage(user.ID, bc.GetBotContent("leaveticket_message"))) } else { link, err := bc.GetBotContentVerbose("channel_link") msg := tgbotapi.NewMessage(user.ID, bc.GetBotContent("subscribe_message")) if err == nil { kbd := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonURL("Канал", link)), ) msg.ReplyMarkup = kbd } if err != nil { log.Printf("NO LINK!!!") var admins []User bc.db.Where("role_bitmask & 1 = ?", 1).Find(&admins) for _, admin := range admins { msg := tgbotapi.NewMessage(admin.ID, "Channel link is not set!!!") msg.Entities = []tgbotapi.MessageEntity{tgbotapi.MessageEntity{ Type: "code", Offset: 1, Length: 2, }} bc.bot.Send(msg) } } bc.bot.Send(msg) } } func handleAdminCallback(bc BotController, update tgbotapi.Update, user User) { if strings.HasPrefix(update.CallbackQuery.Data, "update:") { Label := strings.Split(update.CallbackQuery.Data, ":")[1] if Label == "preview_image" { bc.db.Model(&user).Update("state", "imgset:"+Label) } else { bc.db.Model(&user).Update("state", "stringset:"+Label) } bc.bot.Send(tgbotapi.NewMessage(user.ID, "Send me asset (text or picture (NOT as file))")) } } func handlePanelCallback(bc BotController, update tgbotapi.Update, user User) { if !user.IsAdmin() {return} if !user.IsEffectiveAdmin() { bc.db.Model(&user).Update("RoleBitmask", user.RoleBitmask|0b10) msg := tgbotapi.NewMessage(user.ID, "You was in usermode, turned back to admin mode...") bc.bot.Send(msg) } kbd := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Стартовая картинка", "update:preview_image")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Приветственный текст", "update:start")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Кнопка для заявки", "update:leave_ticket_button")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("ID чата", "update:supportchatid")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("ID канала", "update:channelid")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Уведомление об отправке тикета", "update:sended_notify")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Просьба оставить тикет", "update:leaveticket_message")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Просьба подписаться на канал", "update:subscribe_message")), tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Ссылка на канал", "update:channel_link")), ) msg := tgbotapi.NewMessage(user.ID, "Выберите пункт для изменения") msg.ReplyMarkup = kbd bc.bot.Send(msg) } func DownloadFile(filepath string, url string) error { // Get the data resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() // Create the file out, err := os.Create(filepath) if err != nil { return err } defer out.Close() // Write the body to file _, err = io.Copy(out, resp.Body) return err } func notifyAdminAboutError(bc BotController, errorMessage string) { // Check if AdminID is set in the config adminID := *bc.cfg.AdminID if adminID == 0 { log.Println("AdminID is not set in the configuration.") } msg := tgbotapi.NewMessage( adminID, fmt.Sprintf("Error occurred: %s", errorMessage), ) bc.bot.Send(msg) }