182 lines
4.7 KiB
Go
182 lines
4.7 KiB
Go
package searchreplace
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"git.nakama.town/fmartingr/butterrobot/internal/model"
|
|
"git.nakama.town/fmartingr/butterrobot/internal/plugin"
|
|
)
|
|
|
|
// Regex pattern for search and replace operations: s/search/replace/[flags]
|
|
var searchReplacePattern = regexp.MustCompile(`^s/([^/]*)/([^/]*)(?:/([gimnsuy]*))?$`)
|
|
|
|
// SearchReplacePlugin is a plugin for performing search and replace operations on messages
|
|
type SearchReplacePlugin struct {
|
|
plugin.BasePlugin
|
|
}
|
|
|
|
// New creates a new SearchReplacePlugin instance
|
|
func New() *SearchReplacePlugin {
|
|
return &SearchReplacePlugin{
|
|
BasePlugin: plugin.BasePlugin{
|
|
ID: "util.searchreplace",
|
|
Name: "Search and Replace",
|
|
Help: "Reply to a message with a search and replace pattern (s/search/replace/[flags]) to create a modified message. " +
|
|
"Supported flags: g (global), i (case insensitive)",
|
|
},
|
|
}
|
|
}
|
|
|
|
// OnMessage handles incoming messages
|
|
func (p *SearchReplacePlugin) OnMessage(msg *model.Message, config map[string]interface{}) []*model.MessageAction {
|
|
// Only process replies to messages
|
|
if msg.ReplyTo == "" {
|
|
return nil
|
|
}
|
|
|
|
// Check if the message matches the search/replace pattern
|
|
match := searchReplacePattern.FindStringSubmatch(strings.TrimSpace(msg.Text))
|
|
if match == nil {
|
|
return nil
|
|
}
|
|
|
|
// Get the original message text from the reply_to_message structure in Telegram messages
|
|
var originalText string
|
|
|
|
// For Telegram messages
|
|
if msgData, ok := msg.Raw["message"].(map[string]interface{}); ok {
|
|
if replyMsg, ok := msgData["reply_to_message"].(map[string]interface{}); ok {
|
|
if text, ok := replyMsg["text"].(string); ok {
|
|
originalText = text
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generic fallback for other platforms or if the above method fails
|
|
if originalText == "" && msg.Raw["original_message"] != nil {
|
|
if original, ok := msg.Raw["original_message"].(map[string]interface{}); ok {
|
|
if text, ok := original["text"].(string); ok {
|
|
originalText = text
|
|
}
|
|
}
|
|
}
|
|
|
|
if originalText == "" {
|
|
// If we couldn't find the original message text, inform the user
|
|
return []*model.MessageAction{
|
|
{
|
|
Type: model.ActionSendMessage,
|
|
Message: &model.Message{
|
|
Text: "Sorry, I couldn't find the original message text to perform the replacement.",
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
ReplyTo: msg.ID,
|
|
},
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Extract search pattern, replacement and flags
|
|
searchPattern := match[1]
|
|
replacement := match[2]
|
|
flags := ""
|
|
if len(match) > 3 {
|
|
flags = match[3]
|
|
}
|
|
|
|
// Process the replacement
|
|
result, err := p.performReplacement(originalText, searchPattern, replacement, flags)
|
|
if err != nil {
|
|
return []*model.MessageAction{
|
|
{
|
|
Type: model.ActionSendMessage,
|
|
Message: &model.Message{
|
|
Text: fmt.Sprintf("Error performing replacement: %s", err.Error()),
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
ReplyTo: msg.ID,
|
|
},
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Only send a response if the text actually changed
|
|
if result == originalText {
|
|
return []*model.MessageAction{
|
|
{
|
|
Type: model.ActionSendMessage,
|
|
Message: &model.Message{
|
|
Text: "No changes were made to the original message.",
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
ReplyTo: msg.ID,
|
|
},
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Create a response with the modified text
|
|
return []*model.MessageAction{
|
|
{
|
|
Type: model.ActionSendMessage,
|
|
Message: &model.Message{
|
|
Text: result,
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
ReplyTo: msg.ReplyTo, // Reply to the original message
|
|
},
|
|
Chat: msg.Chat,
|
|
Channel: msg.Channel,
|
|
},
|
|
}
|
|
}
|
|
|
|
// performReplacement performs the search and replace operation on the given text
|
|
func (p *SearchReplacePlugin) performReplacement(text, search, replace, flags string) (string, error) {
|
|
// Process flags
|
|
globalReplace := strings.Contains(flags, "g")
|
|
caseInsensitive := strings.Contains(flags, "i")
|
|
|
|
// Create the regex pattern
|
|
pattern := search
|
|
regexFlags := ""
|
|
if caseInsensitive {
|
|
regexFlags += "(?i)"
|
|
}
|
|
|
|
// Escape special characters if we're not in a regular expression
|
|
if !strings.Contains(flags, "n") {
|
|
pattern = regexp.QuoteMeta(pattern)
|
|
}
|
|
|
|
// Compile the regex
|
|
reg, err := regexp.Compile(regexFlags + pattern)
|
|
if err != nil {
|
|
return "", fmt.Errorf("invalid search pattern: %v", err)
|
|
}
|
|
|
|
// Perform the replacement
|
|
var result string
|
|
if globalReplace {
|
|
result = reg.ReplaceAllString(text, replace)
|
|
} else {
|
|
// For non-global replace, only replace the first occurrence
|
|
indices := reg.FindStringIndex(text)
|
|
if indices == nil {
|
|
// No match found
|
|
return text, nil
|
|
}
|
|
|
|
result = text[:indices[0]] + replace + text[indices[1]:]
|
|
}
|
|
|
|
return result, nil
|
|
}
|