sinkbot/cmd/app/main.go
2025-03-27 22:01:34 +08:00

354 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
return
}
}
// commands for non-admins
switch command {
case "/start":
handleStartCommand(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.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.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)
}