Spaces:
Sleeping
Sleeping
Download plugins/index.go from Thaimozhi/Go-Filter-Bot: direct link, hf CLI and curl.
- Browser
- Download file 9.63 kB
-
https://huggingface.co/spaces/Thaimozhi/Go-Filter-Bot/resolve/main/plugins/index.go
- Command line
-
hf download hf://spaces/Thaimozhi/Go-Filter-Bot/plugins/index.go
-
curl -L -o index.go https://huggingface.co/spaces/Thaimozhi/Go-Filter-Bot/resolve/main/plugins/index.go
9.63 kB
| // Auto-index: listens to configured channels and saves file posts as searchable filters. | |
| 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" | |
| ) | |
| // cleanNameRegex: minimal, non-destructive cleaning for the searchable 'text' keyword. | |
| // VJ-style: normalise separators only β don't strip quality/codec words, since | |
| // search (searchVJ) does fuzzy regex matching against file_name anyway. | |
| // Previous greedy regexes (`@[a-zA-Z0-9_]+` + `(?:...).* `) could consume the | |
| // ENTIRE filename when underscores were used as separators β fixed here. | |
| var ( | |
| extRegex = regexp.MustCompile(`\.\w{2,4}$`) | |
| leadingAtRx = regexp.MustCompile(`^@+`) | |
| separatorRx = regexp.MustCompile(`[\x{00A0}_.\-]+`) // underscore, dot, dash, NBSP β space | |
| bracketRx = regexp.MustCompile(`[\[\](){}]`) | |
| multiSpaceRx = regexp.MustCompile(`\s{2,}`) | |
| ) | |
| // cleanFileName turns a raw file name into a searchable keyword. | |
| // Minimal/non-destructive: normalises separators, strips a leading "@", keeps content. | |
| func cleanFileName(name string) string { | |
| name = extRegex.ReplaceAllString(name, "") // remove file extension | |
| name = separatorRx.ReplaceAllString(name, " ") // underscores/dots/dashes/NBSP β space | |
| name = bracketRx.ReplaceAllString(name, " ") // remove bracket characters | |
| name = leadingAtRx.ReplaceAllString(name, "") // strip leading @ | |
| name = multiSpaceRx.ReplaceAllString(name, " ") | |
| name = strings.TrimSpace(name) | |
| return strings.ToLower(name) | |
| } | |
| // isIndexChannel checks whether a channel ID is in the configured index list. | |
| func isIndexChannel(chanID int64) bool { | |
| s := database.GetBotSettings() | |
| for _, id := range s.IndexChannels { | |
| if id == chanID { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // AutoIndex handles channel posts and saves media as global filters. | |
| // Supports both ctx.ChannelPost (native channel updates) and ctx.Message (forwarded/other). | |
| // | |
| //nolint:errcheck | |
| func AutoIndex(bot *gotgbot.Bot, ctx *ext.Context) error { | |
| // Prefer ChannelPost, fall back to Message | |
| msg := ctx.ChannelPost | |
| if msg == nil { | |
| msg = ctx.Message | |
| } | |
| if msg == nil { | |
| return nil | |
| } | |
| chanID := msg.Chat.Id | |
| // Only index from configured channels | |
| if !isIndexChannel(chanID) { | |
| fmt.Printf("auto-index: channel %d not in index list, skipping\n", chanID) | |
| return nil | |
| } | |
| fmt.Printf("auto-index: received post from channel %d\n", chanID) | |
| var ( | |
| fileID string | |
| mediaType string | |
| rawName string | |
| fileSize int64 | |
| mimeType string | |
| ) | |
| switch { | |
| case msg.Document != nil: | |
| fileID = msg.Document.FileId | |
| mediaType = "document" | |
| rawName = msg.Document.FileName | |
| fileSize = msg.Document.FileSize | |
| mimeType = msg.Document.MimeType | |
| case msg.Video != nil: | |
| fileID = msg.Video.FileId | |
| mediaType = "video" | |
| rawName = msg.Video.FileName | |
| fileSize = msg.Video.FileSize | |
| mimeType = msg.Video.MimeType | |
| if rawName == "" { | |
| rawName = msg.Caption | |
| } | |
| case msg.Audio != nil: | |
| fileID = msg.Audio.FileId | |
| mediaType = "audio" | |
| rawName = msg.Audio.Title | |
| fileSize = msg.Audio.FileSize | |
| mimeType = msg.Audio.MimeType | |
| if rawName == "" { | |
| rawName = msg.Audio.FileName | |
| } | |
| default: | |
| fmt.Printf("auto-index: non-media post in channel %d, skipping\n", chanID) | |
| return nil | |
| } | |
| if rawName == "" && msg.Caption != "" { | |
| rawName = msg.Caption | |
| } | |
| if fileID == "" { | |
| fmt.Printf("auto-index: no fileID in channel %d, skipping\n", chanID) | |
| return nil | |
| } | |
| if rawName == "" { | |
| // Use file_id as fallback name (rare case) | |
| rawName = fileID[:min(20, len(fileID))] | |
| } | |
| keyword := cleanFileName(rawName) | |
| if len(keyword) < 2 { | |
| fmt.Printf("auto-index: keyword too short for %q, skipping\n", rawName) | |
| return nil | |
| } | |
| caption := "" | |
| if msg.Caption != "" { | |
| caption = msg.OriginalCaptionHTML() | |
| } | |
| f := &database.Filter{ | |
| ChatID: globalNumber, | |
| Text: keyword, | |
| FileName: rawName, | |
| Content: caption, | |
| FileID: fileID, | |
| MediaType: mediaType, | |
| FileSize: fileSize, | |
| MimeType: mimeType, | |
| Length: len(keyword), | |
| } | |
| inserted, err := DB.SaveMfilterByFileID(f) | |
| if err != nil { | |
| fmt.Printf("auto-index: β save error for %q: %v\n", rawName, err) | |
| } else if inserted { | |
| fmt.Printf("auto-index: β indexed %q (type=%s) from channel %d\n", rawName, mediaType, chanID) | |
| } else { | |
| fmt.Printf("auto-index: β duplicate skipped %q from channel %d\n", rawName, chanID) | |
| } | |
| return nil | |
| } | |
| // min helper for int | |
| func min(a, b int) int { | |
| if a < b { | |
| return a | |
| } | |
| return b | |
| } | |
| // IndexNow handles the /index command β manually triggers indexing of a replied message. | |
| // | |
| //nolint:errcheck | |
| func IndexNow(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 | |
| } | |
| msg := ctx.Message | |
| if msg.ReplyToMessage == nil { | |
| msg.Reply(bot, "Reply to a media message to index it.", &gotgbot.SendMessageOpts{}) | |
| return nil | |
| } | |
| target := msg.ReplyToMessage | |
| var ( | |
| fileID string | |
| mediaType string | |
| rawName string | |
| ) | |
| switch { | |
| case target.Document != nil: | |
| fileID = target.Document.FileId | |
| mediaType = "document" | |
| rawName = target.Document.FileName | |
| case target.Video != nil: | |
| fileID = target.Video.FileId | |
| mediaType = "video" | |
| rawName = target.Video.FileName | |
| case target.Audio != nil: | |
| fileID = target.Audio.FileId | |
| mediaType = "audio" | |
| rawName = target.Audio.Title | |
| default: | |
| msg.Reply(bot, "β No supported media found in the replied message.", &gotgbot.SendMessageOpts{}) | |
| return nil | |
| } | |
| if rawName == "" { | |
| rawName = target.Caption | |
| } | |
| // Allow custom keyword as argument | |
| args := strings.SplitN(msg.Text, " ", 2) | |
| if len(args) >= 2 && strings.TrimSpace(args[1]) != "" { | |
| rawName = strings.TrimSpace(args[1]) | |
| } | |
| keyword := cleanFileName(rawName) | |
| if keyword == "" { | |
| msg.Reply(bot, "β Could not determine a keyword. Provide one: <code>/index keyword</code>", &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML}) | |
| return nil | |
| } | |
| caption := "" | |
| if target.Caption != "" { | |
| caption = target.OriginalCaptionHTML() | |
| } | |
| var fileSize int64 | |
| var mimeType string | |
| var origName string | |
| switch { | |
| case target.Document != nil: | |
| fileSize = target.Document.FileSize | |
| mimeType = target.Document.MimeType | |
| origName = target.Document.FileName | |
| case target.Video != nil: | |
| fileSize = target.Video.FileSize | |
| mimeType = target.Video.MimeType | |
| origName = target.Video.FileName | |
| case target.Audio != nil: | |
| fileSize = target.Audio.FileSize | |
| mimeType = target.Audio.MimeType | |
| origName = target.Audio.Title | |
| } | |
| if origName == "" { | |
| origName = rawName | |
| } | |
| f := &database.Filter{ | |
| ChatID: globalNumber, | |
| Text: keyword, | |
| FileName: origName, | |
| Content: caption, | |
| FileID: fileID, | |
| MediaType: mediaType, | |
| FileSize: fileSize, | |
| MimeType: mimeType, | |
| Length: len(keyword), | |
| } | |
| inserted, err := DB.SaveMfilterByFileID(f) | |
| if err != nil { | |
| msg.Reply(bot, fmt.Sprintf("β Save error: %v", err), &gotgbot.SendMessageOpts{}) | |
| return nil | |
| } | |
| if !inserted { | |
| msg.Reply(bot, fmt.Sprintf("β οΈ <code>%s</code> already indexed (duplicate file).", origName), &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML}) | |
| return nil | |
| } | |
| msg.Reply(bot, | |
| fmt.Sprintf("β Indexed: <code>%s</code>\nKeyword: <code>%s</code>", origName, keyword), | |
| &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML}, | |
| ) | |
| return nil | |
| } | |
| // AddIndexCmd handles the /addindex command to add a channel to auto-index | |
| // | |
| //nolint:errcheck | |
| func AddIndexCmd(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 | |
| } | |
| args := strings.Split(ctx.Message.Text, " ") | |
| if len(args) < 2 { | |
| ctx.Message.Reply(bot, "Usage: <code>/addindex <channel_id></code>", &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML}) | |
| return nil | |
| } | |
| chanID, err := strconv.ParseInt(args[1], 10, 64) | |
| if err != nil { | |
| ctx.Message.Reply(bot, "β Invalid channel ID.", &gotgbot.SendMessageOpts{}) | |
| return nil | |
| } | |
| if err := DB.AddIndexChannel(chanID); err != nil { | |
| ctx.Message.Reply(bot, fmt.Sprintf("β Failed to add: %v", err), &gotgbot.SendMessageOpts{}) | |
| return nil | |
| } | |
| ctx.Message.Reply(bot, fmt.Sprintf("β Channel <code>%d</code> added to Auto-Index list.", chanID), &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML}) | |
| return nil | |
| } | |
| // DelIndexCmd handles the /delindex command to remove a channel from auto-index | |
| // | |
| //nolint:errcheck | |
| func DelIndexCmd(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 | |
| } | |
| args := strings.Split(ctx.Message.Text, " ") | |
| if len(args) < 2 { | |
| ctx.Message.Reply(bot, "Usage: <code>/delindex <channel_id></code>", &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML}) | |
| return nil | |
| } | |
| chanID, err := strconv.ParseInt(args[1], 10, 64) | |
| if err != nil { | |
| ctx.Message.Reply(bot, "β Invalid channel ID.", &gotgbot.SendMessageOpts{}) | |
| return nil | |
| } | |
| if err := DB.RemoveIndexChannel(chanID); err != nil { | |
| ctx.Message.Reply(bot, fmt.Sprintf("β Failed to remove: %v", err), &gotgbot.SendMessageOpts{}) | |
| return nil | |
| } | |
| ctx.Message.Reply(bot, fmt.Sprintf("β Channel <code>%d</code> removed from Auto-Index list.", chanID), &gotgbot.SendMessageOpts{ParseMode: gotgbot.ParseModeHTML}) | |
| return nil | |
| } | |