Compare commits
No commits in common. "master" and "v0.8.0" have entirely different histories.
10 changed files with 22 additions and 152 deletions
10
CLAUDE.md
10
CLAUDE.md
|
@ -18,12 +18,10 @@ When creating, modifying, or removing plugins:
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
**CRITICAL**: After making ANY changes to code files, you MUST run these commands in order:
|
Before committing plugin changes:
|
||||||
|
|
||||||
1. **Format code**: `make format` - Format all code according to project standards
|
1. Check files are properly formatted: Run `make format`
|
||||||
2. **Lint code**: `make lint` - Check code style and quality (must show "0 issues")
|
2. Check code style and linting: Run `make lint`
|
||||||
3. **Run tests**: `make test` - Run all tests to ensure functionality works
|
3. Test the plugin functionality: Run `make test`
|
||||||
4. Verify documentation accuracy
|
4. Verify documentation accuracy
|
||||||
5. Ensure all examples work as described
|
5. Ensure all examples work as described
|
||||||
|
|
||||||
**These commands are MANDATORY after every code change, no exceptions.**
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
<input class="form-check-input" type="checkbox" name="enable_all_plugins" value="true" {{if .Channel.EnableAllPlugins}}checked{{end}}>
|
<input class="form-check-input" type="checkbox" name="enable_all_plugins" value="true" {{if .Channel.EnableAllPlugins}}checked{{end}}>
|
||||||
<span class="form-check-label">Enable All Plugins</span>
|
<span class="form-check-label">Enable All Plugins</span>
|
||||||
</label>
|
</label>
|
||||||
<div>
|
<div class="form-help">
|
||||||
When enabled, all registered plugins will be automatically enabled for this channel. Individual plugin settings will be ignored.
|
When enabled, all registered plugins will be automatically enabled for this channel. Individual plugin settings will be ignored.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -124,4 +124,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
|
@ -200,4 +200,4 @@ func TestEnableAllPlugins(t *testing.T) {
|
||||||
t.Errorf("EnableAllPlugins should be true after update")
|
t.Errorf("EnableAllPlugins should be true after update")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -231,4 +231,4 @@ func TestChannelName(t *testing.T) {
|
||||||
t.Errorf("Expected channel name to fallback to 'fallback-id', got '%s'", result)
|
t.Errorf("Expected channel name to fallback to 'fallback-id', got '%s'", result)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -233,17 +233,9 @@ func (t *TelegramPlatform) SendMessage(msg *model.Message) error {
|
||||||
|
|
||||||
// Prepare payload
|
// Prepare payload
|
||||||
payload := map[string]interface{}{
|
payload := map[string]interface{}{
|
||||||
"chat_id": chatID,
|
"chat_id": chatID,
|
||||||
"text": msg.Text,
|
"text": msg.Text,
|
||||||
}
|
"parse_mode": "Markdown",
|
||||||
|
|
||||||
// Set parse_mode based on plugin preference or default to empty string
|
|
||||||
if msg.Raw != nil && msg.Raw["parse_mode"] != nil {
|
|
||||||
// Plugin explicitly set parse_mode
|
|
||||||
payload["parse_mode"] = msg.Raw["parse_mode"]
|
|
||||||
} else {
|
|
||||||
// Default to empty string (no formatting)
|
|
||||||
payload["parse_mode"] = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add reply if needed
|
// Add reply if needed
|
||||||
|
|
|
@ -131,15 +131,12 @@ func (p *HLTBPlugin) OnMessage(msg *model.Message, config map[string]interface{}
|
||||||
Channel: msg.Channel,
|
Channel: msg.Channel,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set parse mode for markdown formatting
|
|
||||||
if responseMsg.Raw == nil {
|
|
||||||
responseMsg.Raw = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
responseMsg.Raw["parse_mode"] = "Markdown"
|
|
||||||
|
|
||||||
// Add game cover as attachment if available
|
// Add game cover as attachment if available
|
||||||
if game.GameImage != "" {
|
if game.GameImage != "" {
|
||||||
imageURL := p.getFullImageURL(game.GameImage)
|
imageURL := p.getFullImageURL(game.GameImage)
|
||||||
|
if responseMsg.Raw == nil {
|
||||||
|
responseMsg.Raw = make(map[string]interface{})
|
||||||
|
}
|
||||||
responseMsg.Raw["image_url"] = imageURL
|
responseMsg.Raw["image_url"] = imageURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,6 @@ func (p *HelpPlugin) OnMessage(msg *model.Message, config map[string]interface{}
|
||||||
Chat: msg.Chat,
|
Chat: msg.Chat,
|
||||||
ReplyTo: msg.ID,
|
ReplyTo: msg.ID,
|
||||||
Channel: msg.Channel,
|
Channel: msg.Channel,
|
||||||
Raw: map[string]interface{}{"parse_mode": "Markdown"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return []*model.MessageAction{
|
return []*model.MessageAction{
|
||||||
|
@ -152,7 +151,6 @@ func (p *HelpPlugin) OnMessage(msg *model.Message, config map[string]interface{}
|
||||||
Chat: msg.Chat,
|
Chat: msg.Chat,
|
||||||
ReplyTo: msg.ID,
|
ReplyTo: msg.ID,
|
||||||
Channel: msg.Channel,
|
Channel: msg.Channel,
|
||||||
Raw: map[string]interface{}{"parse_mode": "Markdown"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return []*model.MessageAction{
|
return []*model.MessageAction{
|
||||||
|
|
|
@ -328,4 +328,4 @@ func TestPluginRegistry(t *testing.T) {
|
||||||
t.Errorf("Expected error when getting plugin after clearing registry, got nil")
|
t.Errorf("Expected error when getting plugin after clearing registry, got nil")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -54,12 +54,17 @@ func (p *TwitterExpander) OnMessage(msg *model.Message, config map[string]interf
|
||||||
// Parse the URL
|
// Parse the URL
|
||||||
parsedURL, err := url.Parse(link)
|
parsedURL, err := url.Parse(link)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// If parsing fails, just do the simple replacement
|
||||||
|
link = strings.Replace(link, "twitter.com", replacementDomain, 1)
|
||||||
|
link = strings.Replace(link, "x.com", replacementDomain, 1)
|
||||||
return link
|
return link
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the host to the configured domain
|
// Change the host to the configured domain
|
||||||
if strings.Contains(parsedURL.Host, "twitter.com") || strings.Contains(parsedURL.Host, "x.com") {
|
if strings.Contains(parsedURL.Host, "twitter.com") {
|
||||||
parsedURL.Host = replacementDomain
|
parsedURL.Host = strings.Replace(parsedURL.Host, "twitter.com", replacementDomain, 1)
|
||||||
|
} else if strings.Contains(parsedURL.Host, "x.com") {
|
||||||
|
parsedURL.Host = strings.Replace(parsedURL.Host, "x.com", replacementDomain, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove query parameters
|
// Remove query parameters
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
package social
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.nakama.town/fmartingr/butterrobot/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTwitterExpander_OnMessage(t *testing.T) {
|
|
||||||
plugin := NewTwitterExpander()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
config map[string]interface{}
|
|
||||||
expected string
|
|
||||||
hasReply bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Twitter URL with default domain",
|
|
||||||
input: "https://twitter.com/user/status/123456789",
|
|
||||||
config: map[string]interface{}{},
|
|
||||||
expected: "https://fxtwitter.com/user/status/123456789",
|
|
||||||
hasReply: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "X.com URL with custom domain",
|
|
||||||
input: "https://x.com/elonmusk/status/987654321",
|
|
||||||
config: map[string]interface{}{"domain": "vxtwitter.com"},
|
|
||||||
expected: "https://vxtwitter.com/elonmusk/status/987654321",
|
|
||||||
hasReply: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Twitter URL with tracking parameters",
|
|
||||||
input: "https://twitter.com/openai/status/555?ref_src=twsrc%5Etfw&s=20",
|
|
||||||
config: map[string]interface{}{},
|
|
||||||
expected: "https://fxtwitter.com/openai/status/555",
|
|
||||||
hasReply: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "www.twitter.com URL",
|
|
||||||
input: "https://www.twitter.com/user/status/789",
|
|
||||||
config: map[string]interface{}{"domain": "nitter.net"},
|
|
||||||
expected: "https://nitter.net/user/status/789",
|
|
||||||
hasReply: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Mixed text with Twitter URL",
|
|
||||||
input: "Check this out: https://twitter.com/user/status/123 amazing!",
|
|
||||||
config: map[string]interface{}{},
|
|
||||||
expected: "Check this out: https://fxtwitter.com/user/status/123 amazing!",
|
|
||||||
hasReply: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "No Twitter URLs",
|
|
||||||
input: "Just some regular text https://youtube.com/watch?v=abc",
|
|
||||||
config: map[string]interface{}{},
|
|
||||||
expected: "",
|
|
||||||
hasReply: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Empty message",
|
|
||||||
input: "",
|
|
||||||
config: map[string]interface{}{},
|
|
||||||
expected: "",
|
|
||||||
hasReply: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
msg := &model.Message{
|
|
||||||
ID: "test_msg",
|
|
||||||
Text: tt.input,
|
|
||||||
Chat: "test_chat",
|
|
||||||
Channel: &model.Channel{
|
|
||||||
ID: 1,
|
|
||||||
Platform: "telegram",
|
|
||||||
PlatformChannelID: "test_chat",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
actions := plugin.OnMessage(msg, tt.config, nil)
|
|
||||||
|
|
||||||
if !tt.hasReply {
|
|
||||||
if len(actions) != 0 {
|
|
||||||
t.Errorf("Expected no actions, got %d", len(actions))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(actions) != 1 {
|
|
||||||
t.Errorf("Expected 1 action, got %d", len(actions))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
action := actions[0]
|
|
||||||
if action.Type != model.ActionSendMessage {
|
|
||||||
t.Errorf("Expected ActionSendMessage, got %s", action.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action.Message == nil {
|
|
||||||
t.Error("Expected message in action, got nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if action.Message.Text != tt.expected {
|
|
||||||
t.Errorf("Expected '%s', got '%s'", tt.expected, action.Message.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action.Message.ReplyTo != msg.ID {
|
|
||||||
t.Errorf("Expected ReplyTo '%s', got '%s'", msg.ID, action.Message.ReplyTo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action.Message.Raw == nil || action.Message.Raw["parse_mode"] != "" {
|
|
||||||
t.Error("Expected parse_mode to be empty string to disable markdown parsing")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue