plugin: search and replace
This commit is contained in:
parent
4a154f16f9
commit
a9b4ad52cb
4 changed files with 450 additions and 0 deletions
182
internal/plugin/searchreplace/searchreplace.go
Normal file
182
internal/plugin/searchreplace/searchreplace.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue