function filters, regex filters

This commit is contained in:
eyedeekay
2025-02-03 21:39:04 -05:00
parent 37f0cb852d
commit a47f72acbb
7 changed files with 357 additions and 2 deletions

44
irc/example/example.go Normal file
View File

@ -0,0 +1,44 @@
package main
import (
"log"
"net"
ircinspector "github.com/go-i2p/go-connfilter/irc"
)
func main() {
listener, err := net.Listen("tcp", ":6667")
if err != nil {
log.Fatal(err)
}
inspector := ircinspector.New(listener, ircinspector.Config{
OnMessage: func(msg *ircinspector.Message) error {
log.Printf("Received message: %s", msg.Raw)
return nil
},
OnNumeric: func(numeric int, msg *ircinspector.Message) error {
log.Printf("Received numeric response: %d", numeric)
return nil
},
})
inspector.AddFilter(ircinspector.Filter{
Command: "PRIVMSG",
Channel: "#mychannel",
Callback: func(msg *ircinspector.Message) error {
msg.Trailing = "[modified] " + msg.Trailing
return nil
},
})
for {
conn, err := inspector.Accept()
if err != nil {
log.Printf("Accept error: %v", err)
continue
}
go ircinspector.HandleConnection(conn)
}
}

178
irc/ircinspector.go Normal file
View File

@ -0,0 +1,178 @@
package ircinspector
import (
"bufio"
"fmt"
"log"
"net"
"strings"
)
type defaultLogger struct{}
// Debug implements Logger.
func (d *defaultLogger) Debug(format string, args ...interface{}) {
log.Printf("DBG:"+format, args)
}
// Error implements Logger.
func (d *defaultLogger) Error(format string, args ...interface{}) {
log.Printf("ERR:"+format, args)
}
// New creates a new IRC inspector wrapping an existing listener
func New(listener net.Listener, config Config) *Inspector {
if config.Logger == nil {
config.Logger = &defaultLogger{}
}
return &Inspector{
listener: listener,
config: config,
filters: make([]Filter, 0),
}
}
// Accept implements net.Listener Accept method
func (i *Inspector) Accept() (net.Conn, error) {
conn, err := i.listener.Accept()
if err != nil {
return nil, err
}
return &ircConn{
Conn: conn,
inspector: i,
}, nil
}
// Close implements net.Listener Close method
func (i *Inspector) Close() error {
return i.listener.Close()
}
// Addr implements net.Listener Addr method
func (i *Inspector) Addr() net.Addr {
return i.listener.Addr()
}
type ircConn struct {
net.Conn
inspector *Inspector
reader *bufio.Reader
writer *bufio.Writer
}
func (c *ircConn) Read(b []byte) (n int, err error) {
if c.reader == nil {
c.reader = bufio.NewReader(c.Conn)
}
line, err := c.reader.ReadString('\n')
if err != nil {
return 0, err
}
msg, err := parseMessage(line)
if err != nil {
c.inspector.config.Logger.Error("parse error: %v", err)
copy(b, line)
return len(line), nil
}
if err := c.inspector.processMessage(msg); err != nil {
c.inspector.config.Logger.Error("process error: %v", err)
}
modified := msg.String()
copy(b, modified)
return len(modified), nil
}
func (c *ircConn) Write(b []byte) (n int, err error) {
if c.writer == nil {
c.writer = bufio.NewWriter(c.Conn)
}
msg, err := parseMessage(string(b))
if err != nil {
return c.writer.Write(b)
}
if err := c.inspector.processMessage(msg); err != nil {
c.inspector.config.Logger.Error("process error: %v", err)
}
return c.writer.Write([]byte(msg.String()))
}
func parseMessage(raw string) (*Message, error) {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil, fmt.Errorf("empty message")
}
msg := &Message{Raw: raw}
if raw[0] == ':' {
parts := strings.SplitN(raw[1:], " ", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid message format")
}
msg.Prefix = parts[0]
raw = parts[1]
}
parts := strings.SplitN(raw, " :", 2)
if len(parts) > 1 {
msg.Trailing = parts[1]
}
words := strings.Fields(parts[0])
if len(words) == 0 {
return nil, fmt.Errorf("no command found")
}
msg.Command = words[0]
if len(words) > 1 {
msg.Params = words[1:]
}
return msg, nil
}
func (i *Inspector) AddFilter(filter Filter) {
i.mu.Lock()
defer i.mu.Unlock()
i.filters = append(i.filters, filter)
}
func (i *Inspector) processMessage(msg *Message) error {
i.mu.RLock()
defer i.mu.RUnlock()
// Process global message handler
if i.config.OnMessage != nil {
if err := i.config.OnMessage(msg); err != nil {
return err
}
}
// Process numeric responses
if numeric, err := parseNumeric(msg.Command); err == nil && i.config.OnNumeric != nil {
if err := i.config.OnNumeric(numeric, msg); err != nil {
return err
}
}
// Process filters
for _, filter := range i.filters {
if matchesFilter(msg, filter) {
if err := filter.Callback(msg); err != nil {
return err
}
}
}
return nil
}

44
irc/types.go Normal file
View File

@ -0,0 +1,44 @@
package ircinspector
import (
"net"
"sync"
)
// Message represents a parsed IRC message
type Message struct {
Raw string
Prefix string
Command string
Params []string
Trailing string
}
// Filter defines criteria for message filtering
type Filter struct {
Command string
Channel string
Prefix string
Callback func(*Message) error
}
// Config contains inspector configuration
type Config struct {
OnMessage func(*Message) error
OnNumeric func(int, *Message) error
Logger Logger
}
// Logger interface for customizable logging
type Logger interface {
Debug(format string, args ...interface{})
Error(format string, args ...interface{})
}
// Inspector implements the net.Listener interface with IRC inspection
type Inspector struct {
listener net.Listener
config Config
filters []Filter
mu sync.RWMutex
}