Add IRC connector
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
const (
|
||||
// Connectors
|
||||
ConnectorIRC = "irc"
|
||||
ConnectorTelegram = "telegram"
|
||||
ConnectorSlack = "slack"
|
||||
ConnectorDiscord = "discord"
|
||||
@@ -19,6 +20,7 @@ const (
|
||||
)
|
||||
|
||||
var AvailableConnectors = []string{
|
||||
ConnectorIRC,
|
||||
ConnectorTelegram,
|
||||
ConnectorSlack,
|
||||
ConnectorDiscord,
|
||||
@@ -52,6 +54,8 @@ func Connectors(a *state.AgentConfig) []state.Connector {
|
||||
conns = append(conns, connectors.NewGithubIssueWatcher(config))
|
||||
case ConnectorGithubPRs:
|
||||
conns = append(conns, connectors.NewGithubPRWatcher(config))
|
||||
case ConnectorIRC:
|
||||
conns = append(conns, connectors.NewIRC(config))
|
||||
}
|
||||
}
|
||||
return conns
|
||||
|
||||
170
services/connectors/irc.go
Normal file
170
services/connectors/irc.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package connectors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mudler/LocalAgent/core/agent"
|
||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||
"github.com/mudler/LocalAgent/services/actions"
|
||||
"github.com/thoj/go-ircevent"
|
||||
)
|
||||
|
||||
type IRC struct {
|
||||
server string
|
||||
port string
|
||||
nickname string
|
||||
channel string
|
||||
conn *irc.Connection
|
||||
alwaysReply bool
|
||||
}
|
||||
|
||||
func NewIRC(config map[string]string) *IRC {
|
||||
return &IRC{
|
||||
server: config["server"],
|
||||
port: config["port"],
|
||||
nickname: config["nickname"],
|
||||
channel: config["channel"],
|
||||
alwaysReply: config["alwaysReply"] == "true",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *IRC) AgentResultCallback() func(state agent.ActionState) {
|
||||
return func(state agent.ActionState) {
|
||||
// Send the result to the bot
|
||||
}
|
||||
}
|
||||
|
||||
func (i *IRC) AgentReasoningCallback() func(state agent.ActionCurrentState) bool {
|
||||
return func(state agent.ActionCurrentState) bool {
|
||||
// Send the reasoning to the bot
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// cleanUpUsernameFromMessage removes the bot's nickname from the message
|
||||
func cleanUpMessage(message string, nickname string) string {
|
||||
cleaned := strings.ReplaceAll(message, nickname+":", "")
|
||||
cleaned = strings.ReplaceAll(cleaned, nickname+",", "")
|
||||
cleaned = strings.TrimSpace(cleaned)
|
||||
return cleaned
|
||||
}
|
||||
|
||||
// isMentioned checks if the bot is mentioned in the message
|
||||
func isMentioned(message string, nickname string) bool {
|
||||
return strings.Contains(message, nickname+":") ||
|
||||
strings.Contains(message, nickname+",") ||
|
||||
strings.HasPrefix(message, nickname)
|
||||
}
|
||||
|
||||
// Start connects to the IRC server and starts listening for messages
|
||||
func (i *IRC) Start(a *agent.Agent) {
|
||||
i.conn = irc.IRC(i.nickname, i.nickname)
|
||||
i.conn.UseTLS = false
|
||||
i.conn.AddCallback("001", func(e *irc.Event) {
|
||||
xlog.Info("Connected to IRC server", "server", i.server)
|
||||
i.conn.Join(i.channel)
|
||||
xlog.Info("Joined channel", "channel", i.channel)
|
||||
})
|
||||
|
||||
i.conn.AddCallback("JOIN", func(e *irc.Event) {
|
||||
if e.Nick == i.nickname {
|
||||
xlog.Info("Bot joined channel", "channel", e.Arguments[0])
|
||||
time.Sleep(1 * time.Second) // Small delay to ensure join is complete
|
||||
i.conn.Privmsg(e.Arguments[0], "Hello! I've just (re)started and am ready to assist.")
|
||||
}
|
||||
})
|
||||
|
||||
i.conn.AddCallback("PRIVMSG", func(e *irc.Event) {
|
||||
message := e.Message()
|
||||
sender := e.Nick
|
||||
channel := e.Arguments[0]
|
||||
isDirect := false
|
||||
|
||||
if channel == i.nickname {
|
||||
channel = sender
|
||||
isDirect = true
|
||||
}
|
||||
|
||||
// Skip messages from ourselves
|
||||
if sender == i.nickname {
|
||||
return
|
||||
}
|
||||
|
||||
if !(i.alwaysReply || isMentioned(message, i.nickname) || isDirect) {
|
||||
return
|
||||
}
|
||||
|
||||
xlog.Info("Recv message", "message", message, "sender", sender, "channel", channel)
|
||||
cleanedMessage := "I am " + sender + ". " + cleanUpMessage(message, i.nickname)
|
||||
|
||||
go func() {
|
||||
res := a.Ask(
|
||||
agent.WithText(cleanedMessage),
|
||||
)
|
||||
|
||||
xlog.Info("Sending message", "message", res.Response, "channel", channel)
|
||||
|
||||
// Split the response into multiple messages if it's too long
|
||||
// IRC typically has a message length limit
|
||||
maxLength := 400 // Safe limit for most IRC servers
|
||||
response := res.Response
|
||||
|
||||
// Handle multiline responses
|
||||
lines := strings.Split(response, "\n")
|
||||
for _, line := range lines {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Split long lines
|
||||
for len(line) > 0 {
|
||||
var chunk string
|
||||
if len(line) > maxLength {
|
||||
chunk = line[:maxLength]
|
||||
line = line[maxLength:]
|
||||
} else {
|
||||
chunk = line
|
||||
line = ""
|
||||
}
|
||||
|
||||
// Send the message to the channel
|
||||
i.conn.Privmsg(channel, chunk)
|
||||
|
||||
// Small delay to prevent flooding
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any attachments or special content from actions
|
||||
for _, state := range res.State {
|
||||
// Handle URLs from search action
|
||||
if urls, exists := state.Metadata[actions.MetadataUrls]; exists {
|
||||
for _, url := range urls.([]string) {
|
||||
i.conn.Privmsg(channel, fmt.Sprintf("URL: %s", url))
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle image URLs
|
||||
if imagesUrls, exists := state.Metadata[actions.MetadataImages]; exists {
|
||||
for _, url := range imagesUrls.([]string) {
|
||||
i.conn.Privmsg(channel, fmt.Sprintf("Image: %s", url))
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
// Connect to the server
|
||||
err := i.conn.Connect(i.server + ":" + i.port)
|
||||
if err != nil {
|
||||
xlog.Error("Failed to connect to IRC server", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Start the IRC client in a goroutine
|
||||
go i.conn.Loop()
|
||||
}
|
||||
Reference in New Issue
Block a user