mirror of
https://github.com/go-i2p/go-connfilter.git
synced 2025-07-13 11:54:44 -04:00
function filters, regex filters
This commit is contained in:
44
irc/example/example.go
Normal file
44
irc/example/example.go
Normal 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
178
irc/ircinspector.go
Normal 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
44
irc/types.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user