Skip to content

Instantly share code, notes, and snippets.

@kirkegaard
Last active September 15, 2024 00:53
Show Gist options
  • Save kirkegaard/dfd1dbd481b0a0ef957862e4dd387769 to your computer and use it in GitHub Desktop.
Save kirkegaard/dfd1dbd481b0a0ef957862e4dd387769 to your computer and use it in GitHub Desktop.
Irc bot written in go
package main
import (
"bufio"
"fmt"
"net"
"strings"
)
// Config holds the configuration for the IRC Client
type Config struct {
Host string
Port int
Nick string
RealName string
Ident string
Channels []string
}
// Client handles the IRC client configuration and connection
type Client struct {
conn net.Conn
reader *bufio.Reader
writer *bufio.Writer
isConnected bool
config Config
}
type Message struct {
Author string
Prefix string
Command string
Content string
Params []string
}
// ParseIRCMessage parses a raw IRC message line and returns an IRCMessage.
func parseMessage(line string) (*Message, error) {
if len(line) == 0 {
return nil, fmt.Errorf("invalid message format")
}
var author, prefix, command, content string
var params []string
// Check for prefix
if line[0] == ':' {
endOfPrefix := strings.Index(line, " ")
if endOfPrefix == -1 {
return nil, fmt.Errorf("invalid message format")
}
prefix = line[1:endOfPrefix]
author = strings.Split(prefix, "!")[0]
line = line[endOfPrefix+1:]
}
// Check for trailing part
if idx := strings.Index(line, " :"); idx != -1 {
content = line[idx+2:]
line = line[:idx]
}
// Remaining parts are command and params
parts := strings.Fields(line)
if len(parts) == 0 {
return nil, fmt.Errorf("invalid message format")
}
command = parts[0]
if len(parts) > 1 {
params = parts[1:]
}
return &Message{
Author: author,
Prefix: prefix,
Command: command,
Params: params,
Content: content,
}, nil
}
// send a formatted IRC message to the server
func (client *Client) send(message string) {
if _, err := fmt.Fprintln(client.writer, message); err != nil {
fmt.Println("Error sending message:", err)
}
if err := client.writer.Flush(); err != nil {
fmt.Println("Error flushing message:", err)
}
}
// readLine reads a line from the server
func (client *Client) readLine() (string, error) {
line, err := client.reader.ReadString('\n')
if err != nil {
return "", err
}
return strings.TrimSpace(line), nil
}
// NewClient creates a new Client with the given configuration
func NewClient(config Config) *Client {
return &Client{
config: config,
}
}
// Connect establishes the connection to the IRC server
func (client *Client) Connect() error {
address := fmt.Sprintf("%s:%d", client.config.Host, client.config.Port)
conn, err := net.Dial("tcp", address)
if err != nil {
return fmt.Errorf("error connecting to server: %w", err)
}
client.conn = conn
client.reader = bufio.NewReader(conn)
client.writer = bufio.NewWriter(conn)
// Send NICK and USER commands
client.send("NICK " + client.config.Nick)
client.send("USER " + client.config.Ident + " 0 * :" + client.config.RealName)
return nil
}
// Disconnect closes the connection to the IRC server
func (client *Client) Disconnect() {
if client.conn != nil {
client.conn.Close()
}
}
// Listen for incomming message
func (client *Client) listen() {
// Loop forever, reading from the server
for {
line, err := client.readLine()
if err != nil {
fmt.Println("Error reading from server:", err)
return
}
msg, err := parseMessage(line)
if err != nil {
fmt.Println("Error parsing message:", err)
continue
}
if msg.Command == "PING" {
client.send("PONG " + strings.TrimPrefix(line, "PING"))
}
// Listen for MOTD message and join channels
if msg.Command == "001" {
client.isConnected = true
for _, channel := range client.config.Channels {
client.send("JOIN " + channel)
fmt.Printf("Joined %s\n", channel)
}
}
// Listen for PRIVMSG
if msg.Command == "PRIVMSG" {
if msg.Content == "hello" {
client.send("PRIVMSG " + msg.Params[0] + " :Hello, " + msg.Author)
}
}
}
}
func main() {
client := NewClient(Config{
Host: "irc.quakenet.org",
Port: 6667,
Nick: "EnGoBot",
RealName: "Christian Kirkegaard",
Ident: "christian",
Channels: []string{"#engobot"},
})
fmt.Println("Connecting to server...")
// Connect to the IRC server
if err := client.Connect(); err != nil {
panic(err)
}
defer client.Disconnect()
client.listen()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment