package social import ( "net/url" "regexp" "strings" "git.nakama.town/fmartingr/butterrobot/internal/model" "git.nakama.town/fmartingr/butterrobot/internal/plugin" ) // TwitterExpander transforms twitter.com links to fxtwitter.com links type TwitterExpander struct { plugin.BasePlugin } // New creates a new TwitterExpander instance func NewTwitterExpander() *TwitterExpander { return &TwitterExpander{ BasePlugin: plugin.BasePlugin{ ID: "social.twitter", Name: "Twitter Link Expander", Help: "Automatically converts twitter.com and x.com links to alternative domain links and removes tracking parameters. Configure 'domain' option to set replacement domain (default: fxtwitter.com)", ConfigRequired: true, }, } } // OnMessage handles incoming messages func (p *TwitterExpander) OnMessage(msg *model.Message, config map[string]interface{}, cache model.CacheInterface) []*model.MessageAction { // Skip empty messages if strings.TrimSpace(msg.Text) == "" { return nil } // Get replacement domain from config, default to fxtwitter.com replacementDomain := "fxtwitter.com" if domain, ok := config["domain"].(string); ok && domain != "" { replacementDomain = domain } // Regex to match twitter.com links // Match both http://twitter.com and https://twitter.com formats // Also match www.twitter.com twitterRegex := regexp.MustCompile(`https?://(www\.)?(twitter\.com|x\.com)/[^\s]+`) // Check if the message contains a Twitter link if !twitterRegex.MatchString(msg.Text) { return nil } // Replace twitter.com/x.com with configured domain in the message and clean query parameters transformed := twitterRegex.ReplaceAllStringFunc(msg.Text, func(link string) string { // Parse the URL parsedURL, err := url.Parse(link) if err != nil { return link } // Change the host to the configured domain if strings.Contains(parsedURL.Host, "twitter.com") || strings.Contains(parsedURL.Host, "x.com") { parsedURL.Host = replacementDomain } // Remove query parameters parsedURL.RawQuery = "" // Return the cleaned URL return parsedURL.String() }) // Create response message response := &model.Message{ Text: transformed, Chat: msg.Chat, ReplyTo: msg.ID, Channel: msg.Channel, } action := &model.MessageAction{ Type: model.ActionSendMessage, Message: response, Chat: msg.Chat, Channel: msg.Channel, } return []*model.MessageAction{action} }