This plugin doesn't have specific configuration fields implemented yet.
diff --git a/internal/admin/templates/plugins/security.domainblock.html b/internal/admin/templates/plugins/security.domainblock.html
deleted file mode 100644
index 7ffcc48..0000000
--- a/internal/admin/templates/plugins/security.domainblock.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{{define "plugins/security.domainblock.html"}}
-
-
Blocked Domains
-
-
- Enter comma-separated list of domains to block (e.g., example.com, evil.org).
- Messages containing links to these domains will be blocked.
-
-
-{{end}}
\ No newline at end of file
diff --git a/internal/admin/templates/plugins/social.instagram.html b/internal/admin/templates/plugins/social.instagram.html
deleted file mode 100644
index a83485d..0000000
--- a/internal/admin/templates/plugins/social.instagram.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{{define "plugins/social.instagram.html"}}
-
-
Replacement Domain
-
-
- Enter the domain to replace instagram.com links with. Default is ddinstagram.com if left empty.
-
-
-{{end}}
\ No newline at end of file
diff --git a/internal/admin/templates/plugins/social.twitter.html b/internal/admin/templates/plugins/social.twitter.html
deleted file mode 100644
index cb4885f..0000000
--- a/internal/admin/templates/plugins/social.twitter.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{{define "plugins/social.twitter.html"}}
-
-
Replacement Domain
-
-
- Enter the domain to replace twitter.com and x.com links with. Default is fxtwitter.com if left empty.
-
-
-{{end}}
\ No newline at end of file
diff --git a/internal/app/app.go b/internal/app/app.go
index 037089f..9dc38c6 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -314,21 +314,11 @@ func (a *App) handleMessage(item queue.Item) {
}
// Process message with plugins
- var pluginsToProcess []string
-
- if channel.EnableAllPlugins {
- // If EnableAllPlugins is true, process all registered plugins
- pluginsToProcess = plugin.GetAvailablePluginIDs()
- } else {
- // Otherwise, process only explicitly enabled plugins
- for pluginID := range channel.Plugins {
- if channel.HasEnabledPlugin(pluginID) {
- pluginsToProcess = append(pluginsToProcess, pluginID)
- }
+ for pluginID, channelPlugin := range channel.Plugins {
+ if !channel.HasEnabledPlugin(pluginID) {
+ continue
}
- }
- for _, pluginID := range pluginsToProcess {
// Get plugin
p, err := plugin.Get(pluginID)
if err != nil {
@@ -336,19 +326,11 @@ func (a *App) handleMessage(item queue.Item) {
continue
}
- // Get plugin configuration (empty map if EnableAllPlugins and plugin not explicitly configured)
- var config map[string]interface{}
- if channelPlugin, exists := channel.Plugins[pluginID]; exists {
- config = channelPlugin.Config
- } else {
- config = make(map[string]interface{})
- }
-
// Create cache instance for this plugin
pluginCache := cache.New(a.db, pluginID)
// Process message and get actions
- actions := p.OnMessage(message, config, pluginCache)
+ actions := p.OnMessage(message, channelPlugin.Config, pluginCache)
// Get platform for processing actions
platform, err := platform.Get(item.Platform)
diff --git a/internal/db/db.go b/internal/db/db.go
index 1c54ad4..2ca767c 100644
--- a/internal/db/db.go
+++ b/internal/db/db.go
@@ -56,7 +56,7 @@ func (d *Database) Close() error {
// GetChannelByID retrieves a channel by ID
func (d *Database) GetChannelByID(id int64) (*model.Channel, error) {
query := `
- SELECT id, platform, platform_channel_id, enabled, enable_all_plugins, channel_raw
+ SELECT id, platform, platform_channel_id, enabled, channel_raw
FROM channels
WHERE id = ?
`
@@ -67,11 +67,10 @@ func (d *Database) GetChannelByID(id int64) (*model.Channel, error) {
platform string
platformChannelID string
enabled bool
- enableAllPlugins bool
channelRawJSON string
)
- err := row.Scan(&id, &platform, &platformChannelID, &enabled, &enableAllPlugins, &channelRawJSON)
+ err := row.Scan(&id, &platform, &platformChannelID, &enabled, &channelRawJSON)
if err == sql.ErrNoRows {
return nil, ErrNotFound
}
@@ -91,7 +90,6 @@ func (d *Database) GetChannelByID(id int64) (*model.Channel, error) {
Platform: platform,
PlatformChannelID: platformChannelID,
Enabled: enabled,
- EnableAllPlugins: enableAllPlugins,
ChannelRaw: channelRaw,
Plugins: make(map[string]*model.ChannelPlugin),
}
@@ -112,7 +110,7 @@ func (d *Database) GetChannelByID(id int64) (*model.Channel, error) {
// GetChannelByPlatform retrieves a channel by platform and platform channel ID
func (d *Database) GetChannelByPlatform(platform, platformChannelID string) (*model.Channel, error) {
query := `
- SELECT id, platform, platform_channel_id, enabled, enable_all_plugins, channel_raw
+ SELECT id, platform, platform_channel_id, enabled, channel_raw
FROM channels
WHERE platform = ? AND platform_channel_id = ?
`
@@ -120,13 +118,12 @@ func (d *Database) GetChannelByPlatform(platform, platformChannelID string) (*mo
row := d.db.QueryRow(query, platform, platformChannelID)
var (
- id int64
- enabled bool
- enableAllPlugins bool
- channelRawJSON string
+ id int64
+ enabled bool
+ channelRawJSON string
)
- err := row.Scan(&id, &platform, &platformChannelID, &enabled, &enableAllPlugins, &channelRawJSON)
+ err := row.Scan(&id, &platform, &platformChannelID, &enabled, &channelRawJSON)
if err == sql.ErrNoRows {
return nil, ErrNotFound
}
@@ -146,7 +143,6 @@ func (d *Database) GetChannelByPlatform(platform, platformChannelID string) (*mo
Platform: platform,
PlatformChannelID: platformChannelID,
Enabled: enabled,
- EnableAllPlugins: enableAllPlugins,
ChannelRaw: channelRaw,
Plugins: make(map[string]*model.ChannelPlugin),
}
@@ -174,11 +170,11 @@ func (d *Database) CreateChannel(platform, platformChannelID string, enabled boo
// Insert channel
query := `
- INSERT INTO channels (platform, platform_channel_id, enabled, enable_all_plugins, channel_raw)
- VALUES (?, ?, ?, ?, ?)
+ INSERT INTO channels (platform, platform_channel_id, enabled, channel_raw)
+ VALUES (?, ?, ?, ?)
`
- result, err := d.db.Exec(query, platform, platformChannelID, enabled, false, string(channelRawJSON))
+ result, err := d.db.Exec(query, platform, platformChannelID, enabled, string(channelRawJSON))
if err != nil {
return nil, err
}
@@ -195,7 +191,6 @@ func (d *Database) CreateChannel(platform, platformChannelID string, enabled boo
Platform: platform,
PlatformChannelID: platformChannelID,
Enabled: enabled,
- EnableAllPlugins: false,
ChannelRaw: channelRaw,
Plugins: make(map[string]*model.ChannelPlugin),
}
@@ -215,18 +210,6 @@ func (d *Database) UpdateChannel(id int64, enabled bool) error {
return err
}
-// UpdateChannelEnableAllPlugins updates a channel's enable_all_plugins status
-func (d *Database) UpdateChannelEnableAllPlugins(id int64, enableAllPlugins bool) error {
- query := `
- UPDATE channels
- SET enable_all_plugins = ?
- WHERE id = ?
- `
-
- _, err := d.db.Exec(query, enableAllPlugins, id)
- return err
-}
-
// DeleteChannel deletes a channel
func (d *Database) DeleteChannel(id int64) error {
// First delete all channel plugins
@@ -473,7 +456,7 @@ func (d *Database) DeleteChannelPluginsByChannel(channelID int64) error {
// GetAllChannels retrieves all channels
func (d *Database) GetAllChannels() ([]*model.Channel, error) {
query := `
- SELECT id, platform, platform_channel_id, enabled, enable_all_plugins, channel_raw
+ SELECT id, platform, platform_channel_id, enabled, channel_raw
FROM channels
`
@@ -495,11 +478,10 @@ func (d *Database) GetAllChannels() ([]*model.Channel, error) {
platform string
platformChannelID string
enabled bool
- enableAllPlugins bool
channelRawJSON string
)
- if err := rows.Scan(&id, &platform, &platformChannelID, &enabled, &enableAllPlugins, &channelRawJSON); err != nil {
+ if err := rows.Scan(&id, &platform, &platformChannelID, &enabled, &channelRawJSON); err != nil {
return nil, err
}
@@ -515,7 +497,6 @@ func (d *Database) GetAllChannels() ([]*model.Channel, error) {
Platform: platform,
PlatformChannelID: platformChannelID,
Enabled: enabled,
- EnableAllPlugins: enableAllPlugins,
ChannelRaw: channelRaw,
Plugins: make(map[string]*model.ChannelPlugin),
}
diff --git a/internal/db/db_test.go b/internal/db/db_test.go
deleted file mode 100644
index beb485d..0000000
--- a/internal/db/db_test.go
+++ /dev/null
@@ -1,203 +0,0 @@
-package db
-
-import (
- "fmt"
- "os"
- "testing"
- "time"
-
- "git.nakama.town/fmartingr/butterrobot/internal/model"
-)
-
-func TestEnableAllPlugins(t *testing.T) {
- // Create temporary database for testing with unique name
- dbFile := fmt.Sprintf("test_db_%d.db", time.Now().UnixNano())
- database, err := New(dbFile)
- if err != nil {
- t.Fatalf("Failed to create test database: %v", err)
- }
- defer func() {
- _ = database.Close()
- // Clean up test database file
- _ = os.Remove(dbFile)
- }()
-
- t.Run("CreateChannel with EnableAllPlugins default false", func(t *testing.T) {
- channelRaw := map[string]interface{}{
- "name": "test-channel",
- }
-
- channel, err := database.CreateChannel("telegram", "123456", true, channelRaw)
- if err != nil {
- t.Fatalf("Failed to create channel: %v", err)
- }
-
- if channel.EnableAllPlugins {
- t.Errorf("Expected EnableAllPlugins to be false by default, got true")
- }
-
- // Verify it's also false when retrieved from database
- retrieved, err := database.GetChannelByID(channel.ID)
- if err != nil {
- t.Fatalf("Failed to retrieve channel: %v", err)
- }
-
- if retrieved.EnableAllPlugins {
- t.Errorf("Expected EnableAllPlugins to be false when retrieved from DB, got true")
- }
- })
-
- t.Run("UpdateChannelEnableAllPlugins", func(t *testing.T) {
- // Create a channel
- channelRaw := map[string]interface{}{
- "name": "test-channel-2",
- }
-
- channel, err := database.CreateChannel("telegram", "123457", true, channelRaw)
- if err != nil {
- t.Fatalf("Failed to create channel: %v", err)
- }
-
- // Update EnableAllPlugins to true
- err = database.UpdateChannelEnableAllPlugins(channel.ID, true)
- if err != nil {
- t.Fatalf("Failed to update EnableAllPlugins: %v", err)
- }
-
- // Retrieve and verify
- retrieved, err := database.GetChannelByID(channel.ID)
- if err != nil {
- t.Fatalf("Failed to retrieve channel: %v", err)
- }
-
- if !retrieved.EnableAllPlugins {
- t.Errorf("Expected EnableAllPlugins to be true after update, got false")
- }
-
- // Update back to false
- err = database.UpdateChannelEnableAllPlugins(channel.ID, false)
- if err != nil {
- t.Fatalf("Failed to update EnableAllPlugins back to false: %v", err)
- }
-
- // Retrieve and verify again
- retrieved, err = database.GetChannelByID(channel.ID)
- if err != nil {
- t.Fatalf("Failed to retrieve channel: %v", err)
- }
-
- if retrieved.EnableAllPlugins {
- t.Errorf("Expected EnableAllPlugins to be false after second update, got true")
- }
- })
-
- t.Run("GetChannelByPlatform includes EnableAllPlugins", func(t *testing.T) {
- // Create a channel
- channelRaw := map[string]interface{}{
- "name": "test-channel-3",
- }
-
- channel, err := database.CreateChannel("slack", "C123456", true, channelRaw)
- if err != nil {
- t.Fatalf("Failed to create channel: %v", err)
- }
-
- // Enable all plugins
- err = database.UpdateChannelEnableAllPlugins(channel.ID, true)
- if err != nil {
- t.Fatalf("Failed to update EnableAllPlugins: %v", err)
- }
-
- // Retrieve by platform
- retrieved, err := database.GetChannelByPlatform("slack", "C123456")
- if err != nil {
- t.Fatalf("Failed to retrieve channel by platform: %v", err)
- }
-
- if !retrieved.EnableAllPlugins {
- t.Errorf("Expected EnableAllPlugins to be true when retrieved by platform, got false")
- }
- })
-
- t.Run("GetAllChannels includes EnableAllPlugins", func(t *testing.T) {
- // Create multiple channels with different EnableAllPlugins settings
- channelRaw1 := map[string]interface{}{"name": "channel-1"}
- channelRaw2 := map[string]interface{}{"name": "channel-2"}
-
- channel1, err := database.CreateChannel("platform1", "ch1", true, channelRaw1)
- if err != nil {
- t.Fatalf("Failed to create channel1: %v", err)
- }
-
- channel2, err := database.CreateChannel("platform2", "ch2", true, channelRaw2)
- if err != nil {
- t.Fatalf("Failed to create channel2: %v", err)
- }
-
- // Enable all plugins for channel2 only
- err = database.UpdateChannelEnableAllPlugins(channel2.ID, true)
- if err != nil {
- t.Fatalf("Failed to update EnableAllPlugins for channel2: %v", err)
- }
-
- // Get all channels
- channels, err := database.GetAllChannels()
- if err != nil {
- t.Fatalf("Failed to get all channels: %v", err)
- }
-
- // Find our test channels
- var foundChannel1, foundChannel2 *model.Channel
- for _, ch := range channels {
- if ch.ID == channel1.ID {
- foundChannel1 = ch
- }
- if ch.ID == channel2.ID {
- foundChannel2 = ch
- }
- }
-
- if foundChannel1 == nil {
- t.Fatalf("Channel1 not found in GetAllChannels result")
- }
- if foundChannel2 == nil {
- t.Fatalf("Channel2 not found in GetAllChannels result")
- }
-
- if foundChannel1.EnableAllPlugins {
- t.Errorf("Expected channel1 EnableAllPlugins to be false, got true")
- }
- if !foundChannel2.EnableAllPlugins {
- t.Errorf("Expected channel2 EnableAllPlugins to be true, got false")
- }
- })
-
- t.Run("Migration applied correctly", func(t *testing.T) {
- // Test that we can create a channel and the enable_all_plugins column exists
- // This implicitly tests that migration 4 was applied correctly
- channelRaw := map[string]interface{}{
- "name": "migration-test-channel",
- }
-
- channel, err := database.CreateChannel("test-platform", "migration-test", true, channelRaw)
- if err != nil {
- t.Fatalf("Failed to create channel after migration: %v", err)
- }
-
- // Try to update EnableAllPlugins - this would fail if the column doesn't exist
- err = database.UpdateChannelEnableAllPlugins(channel.ID, true)
- if err != nil {
- t.Fatalf("Failed to update EnableAllPlugins - migration may not have been applied: %v", err)
- }
-
- // Verify the value was set correctly
- retrieved, err := database.GetChannelByID(channel.ID)
- if err != nil {
- t.Fatalf("Failed to retrieve channel: %v", err)
- }
-
- if !retrieved.EnableAllPlugins {
- t.Errorf("EnableAllPlugins should be true after update")
- }
- })
-}
diff --git a/internal/migration/migrations.go b/internal/migration/migrations.go
index 11aa716..9004a9b 100644
--- a/internal/migration/migrations.go
+++ b/internal/migration/migrations.go
@@ -10,7 +10,6 @@ func init() {
Register(1, "Initial schema with bcrypt passwords", migrateInitialSchemaUp, migrateInitialSchemaDown)
Register(2, "Add reminders table", migrateRemindersUp, migrateRemindersDown)
Register(3, "Add cache table", migrateCacheUp, migrateCacheDown)
- Register(4, "Add enable_all_plugins column to channels", migrateEnableAllPluginsUp, migrateEnableAllPluginsDown)
}
// Initial schema creation with bcrypt passwords - version 1
@@ -155,60 +154,3 @@ func migrateCacheDown(db *sql.DB) error {
_, err := db.Exec(`DROP TABLE IF EXISTS cache`)
return err
}
-
-// Add enable_all_plugins column to channels table - version 4
-func migrateEnableAllPluginsUp(db *sql.DB) error {
- _, err := db.Exec(`
- ALTER TABLE channels ADD COLUMN enable_all_plugins BOOLEAN NOT NULL DEFAULT 0
- `)
- return err
-}
-
-func migrateEnableAllPluginsDown(db *sql.DB) error {
- // SQLite doesn't support DROP COLUMN, so we need to recreate the table
- tx, err := db.Begin()
- if err != nil {
- return err
- }
- defer func() {
- _ = tx.Rollback() // Ignore rollback errors
- }()
-
- // Create backup table
- _, err = tx.Exec(`
- CREATE TABLE channels_backup (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- platform TEXT NOT NULL,
- platform_channel_id TEXT NOT NULL,
- enabled BOOLEAN NOT NULL DEFAULT 0,
- channel_raw TEXT NOT NULL,
- UNIQUE(platform, platform_channel_id)
- )
- `)
- if err != nil {
- return err
- }
-
- // Copy data excluding enable_all_plugins column
- _, err = tx.Exec(`
- INSERT INTO channels_backup (id, platform, platform_channel_id, enabled, channel_raw)
- SELECT id, platform, platform_channel_id, enabled, channel_raw FROM channels
- `)
- if err != nil {
- return err
- }
-
- // Drop original table
- _, err = tx.Exec(`DROP TABLE channels`)
- if err != nil {
- return err
- }
-
- // Rename backup table
- _, err = tx.Exec(`ALTER TABLE channels_backup RENAME TO channels`)
- if err != nil {
- return err
- }
-
- return tx.Commit()
-}
diff --git a/internal/model/message.go b/internal/model/message.go
index 8b38830..26ec5da 100644
--- a/internal/model/message.go
+++ b/internal/model/message.go
@@ -44,17 +44,11 @@ type Channel struct {
PlatformChannelID string
ChannelRaw map[string]interface{}
Enabled bool
- EnableAllPlugins bool
Plugins map[string]*ChannelPlugin
}
// HasEnabledPlugin checks if a plugin is enabled for this channel
func (c *Channel) HasEnabledPlugin(pluginID string) bool {
- // If EnableAllPlugins is true, all plugins are considered enabled
- if c.EnableAllPlugins {
- return true
- }
-
plugin, exists := c.Plugins[pluginID]
if !exists {
return false
diff --git a/internal/model/message_test.go b/internal/model/message_test.go
deleted file mode 100644
index d2dfedc..0000000
--- a/internal/model/message_test.go
+++ /dev/null
@@ -1,234 +0,0 @@
-package model
-
-import (
- "testing"
-)
-
-func TestChannel_HasEnabledPlugin(t *testing.T) {
- t.Run("EnableAllPlugins false - plugin not in map", func(t *testing.T) {
- channel := &Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: false,
- Plugins: make(map[string]*ChannelPlugin),
- }
-
- // Plugin not in map should return false
- result := channel.HasEnabledPlugin("nonexistent.plugin")
- if result {
- t.Errorf("Expected HasEnabledPlugin to return false for nonexistent plugin, got true")
- }
- })
-
- t.Run("EnableAllPlugins false - plugin disabled", func(t *testing.T) {
- channel := &Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: false,
- Plugins: map[string]*ChannelPlugin{
- "test.plugin": {
- ID: 1,
- ChannelID: 1,
- PluginID: "test.plugin",
- Enabled: false,
- Config: make(map[string]any),
- },
- },
- }
-
- // Disabled plugin should return false
- result := channel.HasEnabledPlugin("test.plugin")
- if result {
- t.Errorf("Expected HasEnabledPlugin to return false for disabled plugin, got true")
- }
- })
-
- t.Run("EnableAllPlugins false - plugin enabled", func(t *testing.T) {
- channel := &Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: false,
- Plugins: map[string]*ChannelPlugin{
- "test.plugin": {
- ID: 1,
- ChannelID: 1,
- PluginID: "test.plugin",
- Enabled: true,
- Config: make(map[string]any),
- },
- },
- }
-
- // Enabled plugin should return true
- result := channel.HasEnabledPlugin("test.plugin")
- if !result {
- t.Errorf("Expected HasEnabledPlugin to return true for enabled plugin, got false")
- }
- })
-
- t.Run("EnableAllPlugins true - plugin not in map", func(t *testing.T) {
- channel := &Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: true,
- Plugins: make(map[string]*ChannelPlugin),
- }
-
- // When EnableAllPlugins is true, any plugin should be considered enabled
- result := channel.HasEnabledPlugin("nonexistent.plugin")
- if !result {
- t.Errorf("Expected HasEnabledPlugin to return true when EnableAllPlugins is true, got false")
- }
- })
-
- t.Run("EnableAllPlugins true - plugin disabled", func(t *testing.T) {
- channel := &Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: true,
- Plugins: map[string]*ChannelPlugin{
- "test.plugin": {
- ID: 1,
- ChannelID: 1,
- PluginID: "test.plugin",
- Enabled: false,
- Config: make(map[string]any),
- },
- },
- }
-
- // When EnableAllPlugins is true, even disabled plugins should be considered enabled
- result := channel.HasEnabledPlugin("test.plugin")
- if !result {
- t.Errorf("Expected HasEnabledPlugin to return true when EnableAllPlugins is true (even for disabled plugin), got false")
- }
- })
-
- t.Run("EnableAllPlugins true - plugin enabled", func(t *testing.T) {
- channel := &Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: true,
- Plugins: map[string]*ChannelPlugin{
- "test.plugin": {
- ID: 1,
- ChannelID: 1,
- PluginID: "test.plugin",
- Enabled: true,
- Config: make(map[string]any),
- },
- },
- }
-
- // When EnableAllPlugins is true, enabled plugins should also return true
- result := channel.HasEnabledPlugin("test.plugin")
- if !result {
- t.Errorf("Expected HasEnabledPlugin to return true when EnableAllPlugins is true, got false")
- }
- })
-
- t.Run("EnableAllPlugins true - multiple plugins", func(t *testing.T) {
- channel := &Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: true,
- Plugins: map[string]*ChannelPlugin{
- "plugin1": {
- ID: 1,
- ChannelID: 1,
- PluginID: "plugin1",
- Enabled: true,
- Config: make(map[string]any),
- },
- "plugin2": {
- ID: 2,
- ChannelID: 1,
- PluginID: "plugin2",
- Enabled: false,
- Config: make(map[string]any),
- },
- },
- }
-
- // All plugins should be enabled when EnableAllPlugins is true
- testCases := []string{"plugin1", "plugin2", "plugin3", "any.plugin"}
- for _, pluginID := range testCases {
- result := channel.HasEnabledPlugin(pluginID)
- if !result {
- t.Errorf("Expected HasEnabledPlugin('%s') to return true when EnableAllPlugins is true, got false", pluginID)
- }
- }
- })
-}
-
-func TestChannelName(t *testing.T) {
- t.Run("Returns PlatformChannelID when ChannelRaw is nil", func(t *testing.T) {
- channel := &Channel{
- PlatformChannelID: "test-id",
- ChannelRaw: nil,
- }
-
- result := channel.ChannelName()
- if result != "test-id" {
- t.Errorf("Expected channel name to be 'test-id', got '%s'", result)
- }
- })
-
- t.Run("Returns name from ChannelRaw when available", func(t *testing.T) {
- channel := &Channel{
- PlatformChannelID: "test-id",
- ChannelRaw: map[string]interface{}{
- "name": "Test Channel",
- },
- }
-
- result := channel.ChannelName()
- if result != "Test Channel" {
- t.Errorf("Expected channel name to be 'Test Channel', got '%s'", result)
- }
- })
-
- t.Run("Returns title from nested chat object (Telegram style)", func(t *testing.T) {
- channel := &Channel{
- PlatformChannelID: "test-id",
- ChannelRaw: map[string]interface{}{
- "chat": map[string]interface{}{
- "title": "Telegram Group",
- },
- },
- }
-
- result := channel.ChannelName()
- if result != "Telegram Group" {
- t.Errorf("Expected channel name to be 'Telegram Group', got '%s'", result)
- }
- })
-
- t.Run("Falls back to PlatformChannelID when no valid name found", func(t *testing.T) {
- channel := &Channel{
- PlatformChannelID: "fallback-id",
- ChannelRaw: map[string]interface{}{
- "other_field": "value",
- },
- }
-
- result := channel.ChannelName()
- if result != "fallback-id" {
- t.Errorf("Expected channel name to fallback to 'fallback-id', got '%s'", result)
- }
- })
-}
diff --git a/internal/platform/telegram/telegram.go b/internal/platform/telegram/telegram.go
index b015793..24714f2 100644
--- a/internal/platform/telegram/telegram.go
+++ b/internal/platform/telegram/telegram.go
@@ -233,17 +233,9 @@ func (t *TelegramPlatform) SendMessage(msg *model.Message) error {
// Prepare payload
payload := map[string]interface{}{
- "chat_id": chatID,
- "text": msg.Text,
- }
-
- // 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"] = ""
+ "chat_id": chatID,
+ "text": msg.Text,
+ "parse_mode": "Markdown",
}
// Add reply if needed
diff --git a/internal/plugin/fun/hltb.go b/internal/plugin/fun/hltb.go
index f94f2ba..227d637 100644
--- a/internal/plugin/fun/hltb.go
+++ b/internal/plugin/fun/hltb.go
@@ -131,15 +131,12 @@ func (p *HLTBPlugin) OnMessage(msg *model.Message, config map[string]interface{}
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
if game.GameImage != "" {
imageURL := p.getFullImageURL(game.GameImage)
+ if responseMsg.Raw == nil {
+ responseMsg.Raw = make(map[string]interface{})
+ }
responseMsg.Raw["image_url"] = imageURL
}
diff --git a/internal/plugin/help/help.go b/internal/plugin/help/help.go
index 4e6215a..88b25dd 100644
--- a/internal/plugin/help/help.go
+++ b/internal/plugin/help/help.go
@@ -74,7 +74,6 @@ func (p *HelpPlugin) OnMessage(msg *model.Message, config map[string]interface{}
Chat: msg.Chat,
ReplyTo: msg.ID,
Channel: msg.Channel,
- Raw: map[string]interface{}{"parse_mode": "Markdown"},
}
return []*model.MessageAction{
@@ -152,7 +151,6 @@ func (p *HelpPlugin) OnMessage(msg *model.Message, config map[string]interface{}
Chat: msg.Chat,
ReplyTo: msg.ID,
Channel: msg.Channel,
- Raw: map[string]interface{}{"parse_mode": "Markdown"},
}
return []*model.MessageAction{
diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go
index 8f8413a..8d1f970 100644
--- a/internal/plugin/plugin.go
+++ b/internal/plugin/plugin.go
@@ -47,19 +47,6 @@ func GetAvailablePlugins() map[string]model.Plugin {
return result
}
-// GetAvailablePluginIDs returns a slice of all registered plugin IDs
-func GetAvailablePluginIDs() []string {
- pluginsMu.RLock()
- defer pluginsMu.RUnlock()
-
- result := make([]string, 0, len(plugins))
- for pluginID := range plugins {
- result = append(result, pluginID)
- }
-
- return result
-}
-
// ClearRegistry clears all registered plugins (for testing)
func ClearRegistry() {
pluginsMu.Lock()
diff --git a/internal/plugin/plugin_test.go b/internal/plugin/plugin_test.go
deleted file mode 100644
index 0bfd207..0000000
--- a/internal/plugin/plugin_test.go
+++ /dev/null
@@ -1,331 +0,0 @@
-package plugin
-
-import (
- "testing"
-
- "git.nakama.town/fmartingr/butterrobot/internal/model"
-)
-
-// Mock plugin for testing
-type testPlugin struct {
- BasePlugin
-}
-
-func (p *testPlugin) OnMessage(msg *model.Message, config map[string]interface{}, cache model.CacheInterface) []*model.MessageAction {
- return []*model.MessageAction{
- {
- Type: model.ActionSendMessage,
- Message: &model.Message{
- Text: "test response",
- Chat: msg.Chat,
- Channel: msg.Channel,
- },
- },
- }
-}
-
-func TestGetAvailablePluginIDs(t *testing.T) {
- // Clear registry before test
- ClearRegistry()
-
- // Register test plugins
- testPlugin1 := &testPlugin{
- BasePlugin: BasePlugin{
- ID: "test.plugin1",
- Name: "Test Plugin 1",
- },
- }
- testPlugin2 := &testPlugin{
- BasePlugin: BasePlugin{
- ID: "test.plugin2",
- Name: "Test Plugin 2",
- },
- }
-
- Register(testPlugin1)
- Register(testPlugin2)
-
- // Test GetAvailablePluginIDs
- pluginIDs := GetAvailablePluginIDs()
-
- if len(pluginIDs) != 2 {
- t.Errorf("Expected 2 plugin IDs, got %d", len(pluginIDs))
- }
-
- // Check that both plugin IDs are present
- found1, found2 := false, false
- for _, id := range pluginIDs {
- if id == "test.plugin1" {
- found1 = true
- }
- if id == "test.plugin2" {
- found2 = true
- }
- }
-
- if !found1 {
- t.Errorf("Expected to find test.plugin1 in plugin IDs")
- }
- if !found2 {
- t.Errorf("Expected to find test.plugin2 in plugin IDs")
- }
-}
-
-func TestEnableAllPluginsProcessingLogic(t *testing.T) {
- // Clear registry before test
- ClearRegistry()
-
- // Register test plugins
- testPlugin1 := &testPlugin{
- BasePlugin: BasePlugin{
- ID: "ping",
- Name: "Ping Plugin",
- },
- }
- testPlugin2 := &testPlugin{
- BasePlugin: BasePlugin{
- ID: "echo",
- Name: "Echo Plugin",
- },
- }
- testPlugin3 := &testPlugin{
- BasePlugin: BasePlugin{
- ID: "help",
- Name: "Help Plugin",
- },
- }
-
- Register(testPlugin1)
- Register(testPlugin2)
- Register(testPlugin3)
-
- t.Run("EnableAllPlugins false - only explicitly enabled plugins", func(t *testing.T) {
- // Create a channel with EnableAllPlugins = false and only some plugins enabled
- channel := &model.Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: false,
- Plugins: map[string]*model.ChannelPlugin{
- "ping": {
- ID: 1,
- ChannelID: 1,
- PluginID: "ping",
- Enabled: true,
- Config: map[string]interface{}{"key": "value"},
- },
- "echo": {
- ID: 2,
- ChannelID: 1,
- PluginID: "echo",
- Enabled: false, // Disabled
- Config: map[string]interface{}{},
- },
- // help plugin not configured
- },
- }
-
- // Simulate the plugin processing logic from handleMessage
- var pluginsToProcess []string
-
- if channel.EnableAllPlugins {
- pluginsToProcess = GetAvailablePluginIDs()
- } else {
- for pluginID := range channel.Plugins {
- if channel.HasEnabledPlugin(pluginID) {
- pluginsToProcess = append(pluginsToProcess, pluginID)
- }
- }
- }
-
- // Should only have "ping" since echo is disabled and help is not configured
- if len(pluginsToProcess) != 1 {
- t.Errorf("Expected 1 plugin to process, got %d: %v", len(pluginsToProcess), pluginsToProcess)
- }
-
- if len(pluginsToProcess) > 0 && pluginsToProcess[0] != "ping" {
- t.Errorf("Expected ping plugin to be processed, got %s", pluginsToProcess[0])
- }
- })
-
- t.Run("EnableAllPlugins true - all registered plugins", func(t *testing.T) {
- // Create a channel with EnableAllPlugins = true
- channel := &model.Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: true,
- Plugins: map[string]*model.ChannelPlugin{
- "ping": {
- ID: 1,
- ChannelID: 1,
- PluginID: "ping",
- Enabled: true,
- Config: map[string]interface{}{"key": "value"},
- },
- "echo": {
- ID: 2,
- ChannelID: 1,
- PluginID: "echo",
- Enabled: false, // Disabled, but should still be processed
- Config: map[string]interface{}{},
- },
- // help plugin not configured, but should still be processed
- },
- }
-
- // Simulate the plugin processing logic from handleMessage
- var pluginsToProcess []string
-
- if channel.EnableAllPlugins {
- pluginsToProcess = GetAvailablePluginIDs()
- } else {
- for pluginID := range channel.Plugins {
- if channel.HasEnabledPlugin(pluginID) {
- pluginsToProcess = append(pluginsToProcess, pluginID)
- }
- }
- }
-
- // Should have all 3 registered plugins
- if len(pluginsToProcess) != 3 {
- t.Errorf("Expected 3 plugins to process, got %d: %v", len(pluginsToProcess), pluginsToProcess)
- }
-
- // Check that all plugins are included
- expectedPlugins := map[string]bool{"ping": false, "echo": false, "help": false}
- for _, pluginID := range pluginsToProcess {
- if _, exists := expectedPlugins[pluginID]; exists {
- expectedPlugins[pluginID] = true
- } else {
- t.Errorf("Unexpected plugin in processing list: %s", pluginID)
- }
- }
-
- for pluginID, found := range expectedPlugins {
- if !found {
- t.Errorf("Expected plugin %s to be in processing list", pluginID)
- }
- }
- })
-
- t.Run("Plugin configuration handling", func(t *testing.T) {
- // Test the configuration logic from handleMessage
- channel := &model.Channel{
- ID: 1,
- Platform: "telegram",
- PlatformChannelID: "123456",
- Enabled: true,
- EnableAllPlugins: true,
- Plugins: map[string]*model.ChannelPlugin{
- "ping": {
- ID: 1,
- ChannelID: 1,
- PluginID: "ping",
- Enabled: true,
- Config: map[string]interface{}{"configured": "value"},
- },
- },
- }
-
- testCases := []struct {
- pluginID string
- expectedConfig map[string]interface{}
- }{
- {
- pluginID: "ping",
- expectedConfig: map[string]interface{}{"configured": "value"},
- },
- {
- pluginID: "echo", // Not explicitly configured
- expectedConfig: map[string]interface{}{},
- },
- }
-
- for _, tc := range testCases {
- // Simulate the config retrieval logic from handleMessage
- var config map[string]interface{}
- if channelPlugin, exists := channel.Plugins[tc.pluginID]; exists {
- config = channelPlugin.Config
- } else {
- config = make(map[string]interface{})
- }
-
- if len(config) != len(tc.expectedConfig) {
- t.Errorf("Plugin %s: expected config length %d, got %d", tc.pluginID, len(tc.expectedConfig), len(config))
- }
-
- for key, expectedValue := range tc.expectedConfig {
- if actualValue, exists := config[key]; !exists || actualValue != expectedValue {
- t.Errorf("Plugin %s: expected config[%s] = %v, got %v", tc.pluginID, key, expectedValue, actualValue)
- }
- }
- }
- })
-}
-
-func TestPluginRegistry(t *testing.T) {
- // Clear registry before test
- ClearRegistry()
-
- testPlugin := &testPlugin{
- BasePlugin: BasePlugin{
- ID: "test.registry",
- Name: "Test Registry Plugin",
- },
- }
-
- t.Run("Register and Get plugin", func(t *testing.T) {
- Register(testPlugin)
-
- retrieved, err := Get("test.registry")
- if err != nil {
- t.Errorf("Failed to get registered plugin: %v", err)
- }
-
- if retrieved.GetID() != "test.registry" {
- t.Errorf("Expected plugin ID 'test.registry', got '%s'", retrieved.GetID())
- }
- })
-
- t.Run("Get nonexistent plugin", func(t *testing.T) {
- _, err := Get("nonexistent.plugin")
- if err == nil {
- t.Errorf("Expected error when getting nonexistent plugin, got nil")
- }
-
- if err != model.ErrPluginNotFound {
- t.Errorf("Expected ErrPluginNotFound, got %v", err)
- }
- })
-
- t.Run("GetAvailablePlugins", func(t *testing.T) {
- plugins := GetAvailablePlugins()
-
- if len(plugins) != 1 {
- t.Errorf("Expected 1 plugin in registry, got %d", len(plugins))
- }
-
- if plugin, exists := plugins["test.registry"]; !exists {
- t.Errorf("Expected to find test.registry in available plugins")
- } else if plugin.GetID() != "test.registry" {
- t.Errorf("Expected plugin ID 'test.registry', got '%s'", plugin.GetID())
- }
- })
-
- t.Run("ClearRegistry", func(t *testing.T) {
- ClearRegistry()
-
- plugins := GetAvailablePlugins()
- if len(plugins) != 0 {
- t.Errorf("Expected 0 plugins after clearing registry, got %d", len(plugins))
- }
-
- _, err := Get("test.registry")
- if err == nil {
- t.Errorf("Expected error when getting plugin after clearing registry, got nil")
- }
- })
-}
diff --git a/internal/plugin/social/instagram.go b/internal/plugin/social/instagram.go
index b423b45..6b7aa4c 100644
--- a/internal/plugin/social/instagram.go
+++ b/internal/plugin/social/instagram.go
@@ -18,10 +18,9 @@ type InstagramExpander struct {
func NewInstagramExpander() *InstagramExpander {
return &InstagramExpander{
BasePlugin: plugin.BasePlugin{
- ID: "social.instagram",
- Name: "Instagram Link Expander",
- Help: "Automatically converts instagram.com links to alternative domain links and removes tracking parameters. Configure 'domain' option to set replacement domain (default: ddinstagram.com)",
- ConfigRequired: true,
+ ID: "social.instagram",
+ Name: "Instagram Link Expander",
+ Help: "Automatically converts instagram.com links to ddinstagram.com links and removes tracking parameters",
},
}
}
@@ -33,12 +32,6 @@ func (p *InstagramExpander) OnMessage(msg *model.Message, config map[string]inte
return nil
}
- // Get replacement domain from config, default to ddinstagram.com
- replacementDomain := "ddinstagram.com"
- if domain, ok := config["domain"].(string); ok && domain != "" {
- replacementDomain = domain
- }
-
// Regex to match instagram.com links
// Match both http://instagram.com and https://instagram.com formats
// Also match www.instagram.com
@@ -49,7 +42,7 @@ func (p *InstagramExpander) OnMessage(msg *model.Message, config map[string]inte
return nil
}
- // Replace instagram.com with configured domain in the message and clean query parameters
+ // Replace instagram.com with ddinstagram.com in the message and clean query parameters
transformed := instagramRegex.ReplaceAllStringFunc(msg.Text, func(link string) string {
// Parse the URL
parsedURL, err := url.Parse(link)
@@ -58,13 +51,13 @@ func (p *InstagramExpander) OnMessage(msg *model.Message, config map[string]inte
return link
}
- // Ensure we don't change links that already come from the replacement domain
+ // Ensure we don't change links that already come from ddinstagram.com
if parsedURL.Host != "instagram.com" && parsedURL.Host != "www.instagram.com" {
return link
}
- // Change the host to the configured domain
- parsedURL.Host = replacementDomain
+ // Change the host
+ parsedURL.Host = "d.ddinstagram.com"
// Remove query parameters
parsedURL.RawQuery = ""
diff --git a/internal/plugin/social/twitter.go b/internal/plugin/social/twitter.go
index f2c6cc9..553bd07 100644
--- a/internal/plugin/social/twitter.go
+++ b/internal/plugin/social/twitter.go
@@ -18,10 +18,9 @@ type TwitterExpander struct {
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,
+ ID: "social.twitter",
+ Name: "Twitter Link Expander",
+ Help: "Automatically converts twitter.com links to fxtwitter.com links and removes tracking parameters",
},
}
}
@@ -33,12 +32,6 @@ func (p *TwitterExpander) OnMessage(msg *model.Message, config map[string]interf
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
@@ -49,17 +42,22 @@ func (p *TwitterExpander) OnMessage(msg *model.Message, config map[string]interf
return nil
}
- // Replace twitter.com/x.com with configured domain in the message and clean query parameters
+ // Replace twitter.com with fxtwitter.com 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 {
+ // If parsing fails, just do the simple replacement
+ link = strings.Replace(link, "twitter.com", "fxtwitter.com", 1)
+ link = strings.Replace(link, "x.com", "fxtwitter.com", 1)
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
+ // Change the host
+ if strings.Contains(parsedURL.Host, "twitter.com") {
+ parsedURL.Host = strings.Replace(parsedURL.Host, "twitter.com", "fxtwitter.com", 1)
+ } else if strings.Contains(parsedURL.Host, "x.com") {
+ parsedURL.Host = strings.Replace(parsedURL.Host, "x.com", "fxtwitter.com", 1)
}
// Remove query parameters
diff --git a/internal/plugin/social/twitter_test.go b/internal/plugin/social/twitter_test.go
deleted file mode 100644
index c0e1681..0000000
--- a/internal/plugin/social/twitter_test.go
+++ /dev/null
@@ -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")
- }
- })
- }
-}