// Dynamic bot settings panel โ toggle all settings from within the bot.
// No more .env edits needed! All changes are saved to MongoDB and cached in memory.
package plugins
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/Jisin0/Go-Filter-Bot/database"
"github.com/Jisin0/Go-Filter-Bot/utils"
"github.com/PaulSonOfLars/gotgbot/v2"
"github.com/PaulSonOfLars/gotgbot/v2/ext"
)
var cbSettingsRegex = regexp.MustCompile(`settings\((.+)\)`)
func boolEmoji(v bool) string {
if v {
return "โ
"
}
return "โ"
}
// buildSettingsMarkup generates the inline keyboard for the settings panel.
func buildSettingsMarkup(s database.BotSettings) gotgbot.InlineKeyboardMarkup {
return gotgbot.InlineKeyboardMarkup{
InlineKeyboard: [][]gotgbot.InlineKeyboardButton{
{
{Text: fmt.Sprintf("%s Multi Filter", boolEmoji(s.MultiFilter)), CallbackData: "settings(toggle_multi_filter)"},
},
{
{Text: fmt.Sprintf("%s Search as Button", boolEmoji(s.SearchAsButton)), CallbackData: "settings(toggle_search_button)"},
},
{
{Text: fmt.Sprintf("โฑ Auto Delete: %dm", s.AutoDelete), CallbackData: "settings(auto_delete)"},
},
{
{Text: "๐ Shortener URL", CallbackData: "settings(shortener_url)"},
{Text: "๐ Shortener API", CallbackData: "settings(shortener_api)"},
},
{
{Text: fmt.Sprintf("๐ข Force-Sub (%d ch)", len(s.ForceSubChats)), CallbackData: "settings(fsub_info)"},
{Text: fmt.Sprintf("๐ Index (%d ch)", len(s.IndexChannels)), CallbackData: "settings(index_info)"},
},
{
{Text: fmt.Sprintf("๐ Admins (%d dynamic)", len(s.DynamicAdmins)), CallbackData: "settings(admin_info)"},
},
{
{Text: "๐ Refresh", CallbackData: "settings(refresh)"},
{Text: "โ Close", CallbackData: "close"},
},
},
}
}
// buildSettingsText generates the main settings panel text.
func buildSettingsText(s database.BotSettings) string {
shortURL := s.ShortenerURL
if shortURL == "" {
shortURL = "Not Set"
}
shortAPI := "โขโขโขโขโขโข"
if s.ShortenerAPI == "" {
shortAPI = "Not Set"
}
fsubList := "None"
if len(s.ForceSubChats) > 0 {
parts := make([]string, len(s.ForceSubChats))
for i, c := range s.ForceSubChats {
parts[i] = fmt.Sprintf("%d", c)
}
fsubList = strings.Join(parts, ", ")
}
indexList := "None"
if len(s.IndexChannels) > 0 {
parts := make([]string, len(s.IndexChannels))
for i, c := range s.IndexChannels {
parts[i] = fmt.Sprintf("%d", c)
}
indexList = strings.Join(parts, ", ")
}
return fmt.Sprintf(`โ๏ธ Bแดแด Sแดแดแดษชษดษขs Pแดษดแดส
Multi Filter : %s
Search Mode : %s
Auto Delete : %d minutes
Shortener URL : %s
Shortener API : %s
Force-Sub : %s
Auto-Index : %s
Dynamic Admins : %d
Tap the buttons below to change settings.
Changes take effect immediately โ no restart needed!`,
boolEmoji(s.MultiFilter),
func() string {
if s.SearchAsButton {
return "Button"
}
return "Hyperlink"
}(),
s.AutoDelete,
shortURL,
shortAPI,
fsubList,
indexList,
len(s.DynamicAdmins),
)
}
// Settings handles the /settings command.
//
//nolint:errcheck
func Settings(bot *gotgbot.Bot, ctx *ext.Context) error {
if !utils.IsAdmin(ctx.EffectiveUser.Id) {
ctx.EffectiveMessage.Reply(bot, "โ Only bot admins can use this command!", &gotgbot.SendMessageOpts{})
return nil
}
s := database.GetBotSettings()
_, err := ctx.Message.Reply(bot, buildSettingsText(s), &gotgbot.SendMessageOpts{
ParseMode: gotgbot.ParseModeHTML,
ReplyMarkup: buildSettingsMarkup(s),
})
if err != nil {
fmt.Printf("settings: %v\n", err)
}
return nil
}
// CbSettings handles all settings callbacks.
//
//nolint:errcheck
func CbSettings(bot *gotgbot.Bot, ctx *ext.Context) error {
update := ctx.CallbackQuery
if !utils.IsAdmin(update.From.Id) {
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: "โ Only bot admins can change settings!", ShowAlert: true})
return nil
}
match := cbSettingsRegex.FindStringSubmatch(update.Data)
if len(match) < 2 {
return nil
}
action := match[1]
s := database.GetBotSettings()
switch action {
case "toggle_multi_filter":
DB.SetBotSetting("multi_filter", !s.MultiFilter)
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{
Text: fmt.Sprintf("Multi Filter: %s", boolEmoji(!s.MultiFilter)),
})
case "toggle_search_button":
DB.SetBotSetting("search_as_button", !s.SearchAsButton)
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{
Text: fmt.Sprintf("Search as Button: %s", boolEmoji(!s.SearchAsButton)),
})
case "auto_delete":
// Cycle through: 0 โ 5 โ 10 โ 30 โ 60 โ 0
var next int64
switch {
case s.AutoDelete == 0:
next = 5
case s.AutoDelete < 10:
next = 10
case s.AutoDelete < 30:
next = 30
case s.AutoDelete < 60:
next = 60
default:
next = 0
}
DB.SetBotSetting("auto_delete", next)
if next == 0 {
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: "Auto Delete: OFF"})
} else {
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: fmt.Sprintf("Auto Delete: %d minutes", next)})
}
case "shortener_url":
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{})
// Ask in PM โ works from both group and PM contexts
pmChat := &gotgbot.Chat{Id: update.From.Id, Type: gotgbot.ChatTypePrivate}
m := utils.Ask(bot, "๐ Send the new Shortener URL\n\nExample: fpshortner.in\n\nSend off to disable.", pmChat, &update.From)
if m == nil {
return nil
}
val := strings.TrimSpace(m.Text)
if strings.ToLower(val) == "off" {
val = ""
}
DB.SetBotSetting("shortener_url", val)
if val == "" {
bot.SendMessage(update.From.Id, "โ
Shortener URL disabled.", &gotgbot.SendMessageOpts{})
} else {
bot.SendMessage(update.From.Id, fmt.Sprintf("โ
Shortener URL set to: %s", val), &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML})
}
case "shortener_api":
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{})
pmChat := &gotgbot.Chat{Id: update.From.Id, Type: gotgbot.ChatTypePrivate}
m := utils.Ask(bot, "๐ Send the new Shortener API Key\n\nSend off to disable.", pmChat, &update.From)
if m == nil {
return nil
}
val := strings.TrimSpace(m.Text)
if strings.ToLower(val) == "off" {
val = ""
}
DB.SetBotSetting("shortener_api", val)
if val == "" {
bot.SendMessage(update.From.Id, "โ
Shortener API disabled.", &gotgbot.SendMessageOpts{})
} else {
bot.SendMessage(update.From.Id, "โ
Shortener API Key updated!", &gotgbot.SendMessageOpts{})
}
case "fsub_info":
text := "Use /addfsub <channel_id> to add\nUse /delfsub <channel_id> to remove\nUse /fsub to list"
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: "Manage with /addfsub, /delfsub, /fsub", ShowAlert: true})
msg, ok := update.Message.(*gotgbot.Message)
if !ok {
return nil
}
bot.SendMessage(msg.Chat.Id, text, &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML})
return nil
case "index_info":
text := "Use /addindex <channel_id> to add\nUse /delindex <channel_id> to remove\nUse /bulkindex <channel_id> [from] [to] for bulk indexing"
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: "Manage with /addindex, /delindex, /bulkindex", ShowAlert: true})
msg2, ok2 := update.Message.(*gotgbot.Message)
if !ok2 {
return nil
}
bot.SendMessage(msg2.Chat.Id, text, &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML})
return nil
case "admin_info":
text := "Use /addadmin <user_id> to add a dynamic admin\nUse /deladmin <user_id> to remove\nUse /admins to list all admins"
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: "Manage with /addadmin, /deladmin, /admins", ShowAlert: true})
msg3, ok3 := update.Message.(*gotgbot.Message)
if !ok3 {
return nil
}
bot.SendMessage(msg3.Chat.Id, text, &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML})
return nil
case "refresh":
DB.RefreshBotSettings()
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: "โป๏ธ Settings refreshed!"})
default:
// Handle set_autodelete_ from potential custom input
if strings.HasPrefix(action, "set_autodelete_") {
valStr := strings.TrimPrefix(action, "set_autodelete_")
val, err := strconv.ParseInt(valStr, 10, 64)
if err == nil {
DB.SetBotSetting("auto_delete", val)
update.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{Text: fmt.Sprintf("Auto Delete: %d min", val)})
}
}
}
// Refresh the settings panel with updated values
newS := database.GetBotSettings()
update.Message.EditText(bot, buildSettingsText(newS), &gotgbot.EditMessageTextOpts{
ParseMode: gotgbot.ParseModeHTML,
ReplyMarkup: buildSettingsMarkup(newS),
})
return nil
}
// CbClose handles the close button on settings and other panels.
//
//nolint:errcheck
func CbClose(bot *gotgbot.Bot, ctx *ext.Context) error {
ctx.CallbackQuery.Message.Delete(bot, nil)
ctx.CallbackQuery.Answer(bot, &gotgbot.AnswerCallbackQueryOpts{})
return nil
}