feat: domain blocker plugin
Some checks failed
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/push/ci Pipeline failed

This commit is contained in:
Felipe M 2025-04-22 18:09:27 +02:00
parent c9edb57505
commit 7dd02c0056
Signed by: fmartingr
GPG key ID: CCFBC5637D4000A8
25 changed files with 898 additions and 63 deletions

View file

@ -7,6 +7,7 @@ ButterRobot organizes plugins into different categories:
- **Development**: Utility plugins like `ping`
- **Fun**: Entertainment plugins like dice rolling, coin flipping
- **Social**: Social media related plugins like URL transformers/expanders
- **Security**: Moderation and protection features like domain blocking
When creating a new plugin, consider which category it fits into and place it in the appropriate directory.
@ -59,6 +60,91 @@ func (p *MarcoPlugin) OnMessage(msg *model.Message, config map[string]interface{
}
```
### Configuration-Enabled Plugin
This plugin requires configuration to be set in the admin interface. It demonstrates how to create plugins that need channel-specific configuration:
```go
package security
import (
"fmt"
"regexp"
"strings"
"git.nakama.town/fmartingr/butterrobot/internal/model"
"git.nakama.town/fmartingr/butterrobot/internal/plugin"
)
// DomainBlockPlugin is a plugin that blocks messages containing links from specific domains
type DomainBlockPlugin struct {
plugin.BasePlugin
}
// New creates a new DomainBlockPlugin instance
func New() *DomainBlockPlugin {
return &DomainBlockPlugin{
BasePlugin: plugin.BasePlugin{
ID: "security.domainblock",
Name: "Domain Blocker",
Help: "Blocks messages containing links from configured domains",
ConfigRequired: true, // Mark this plugin as requiring configuration
},
}
}
// OnMessage processes incoming messages
func (p *DomainBlockPlugin) OnMessage(msg *model.Message, config map[string]interface{}) []*model.Message {
// Get blocked domains from config
blockedDomainsStr, ok := config["blocked_domains"].(string)
if !ok || blockedDomainsStr == "" {
return nil // No blocked domains configured
}
// Split and clean blocked domains
blockedDomains := strings.Split(blockedDomainsStr, ",")
for i, domain := range blockedDomains {
blockedDomains[i] = strings.ToLower(strings.TrimSpace(domain))
}
// Extract domains from message
urlRegex := regexp.MustCompile(`https?://([^\s/$.?#].[^\s]*)`)
matches := urlRegex.FindAllStringSubmatch(msg.Text, -1)
// Check if any extracted domains are blocked
for _, match := range matches {
if len(match) < 2 {
continue
}
domain := strings.ToLower(match[1])
for _, blockedDomain := range blockedDomains {
if blockedDomain == "" {
continue
}
if strings.HasSuffix(domain, blockedDomain) || domain == blockedDomain {
// Domain is blocked, create warning message
response := &model.Message{
Text: fmt.Sprintf("⚠️ Message contained a link to blocked domain: %s", blockedDomain),
Chat: msg.Chat,
ReplyTo: msg.ID,
Channel: msg.Channel,
}
return []*model.Message{response}
}
}
}
return nil
}
func init() {
plugin.Register(New())
}
```
### Advanced Example: URL Transformer
This more complex plugin transforms URLs, useful for improving media embedding in chat platforms:
@ -143,6 +229,36 @@ func (p *TwitterExpander) OnMessage(msg *model.Message, config map[string]interf
}
```
## Enabling Configuration for Plugins
To indicate that your plugin requires configuration:
1. Set `ConfigRequired: true` in the BasePlugin struct:
```go
BasePlugin: plugin.BasePlugin{
ID: "myplugin.id",
Name: "Plugin Name",
Help: "Help text",
ConfigRequired: true,
},
```
2. Access the configuration in the OnMessage method:
```go
func (p *MyPlugin) OnMessage(msg *model.Message, config map[string]interface{}) []*model.Message {
// Extract configuration values
configValue, ok := config["some_config_key"].(string)
if !ok || configValue == "" {
// Handle missing or empty configuration
return nil
}
// Use the configuration...
}
```
3. The admin interface will show a "Configure" button for plugins that require configuration.
## Registering Plugins
To use the plugin, register it in your application:
@ -161,3 +277,11 @@ func (a *App) Run() error {
// ...
}
```
Alternatively, you can register your plugin in its init() function:
```go
func init() {
plugin.Register(New())
}
```