go-irc

A basic SSL-supporting IRC client written in golang.
git clone http://git.hanabi.in/repos/go-irc.git
Log | Files | Refs | LICENSE

commit 5a357be8433a60a0599ba44b307d0db72c9af89a
Author: Agastya Chandrakant <me@hanabi.in>
Date:   Mon,  5 Jul 2021 16:24:56 +0000

Created basic SSL-supporting IRC client in golang.

Most of the implementation was derived from
<https://gist.github.com/jim3ma/00523f865b8801390475c4e2049fe8c3>

Diffstat:
A.env.example | 9+++++++++
A.gitignore | 2++
AREADME.asc | 15+++++++++++++++
Ago.mod | 9+++++++++
Ago.sum | 5+++++
Ahelper-fns.go | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Airc-config.go | 41+++++++++++++++++++++++++++++++++++++++++
Airc.go | 18++++++++++++++++++
Amakecert.sh | 9+++++++++
Atls-fns.go | 48++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 242 insertions(+), 0 deletions(-)

diff --git a/.env.example b/.env.example @@ -0,0 +1,9 @@ +IRC_NICK="mybotname" +IRC_PASS="mybotpass" +IRC_USER="ircusername" +IRC_NAME="ircname" +SSL_CERT="path/to/client.pem" +SSL_KEY="path/to/client.key" +IRC_SERVER="irc.libera.chat:7000" +IRC_CHANNELS="##foo,#bar" +MAINTAINERS="acagastya,aytsaga" diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +.env +.certs/ diff --git a/README.asc b/README.asc @@ -0,0 +1,15 @@ + _ ___ ____ ____ _ _ _ + __ _ ___ | | __ _ _ __ __ _ |_ _| _ \ / ___| ___| (_) ___ _ __ | |_ + / _` |/ _ \| |/ _` | '_ \ / _` | | || |_) | | / __| | |/ _ \ '_ \| __| + | (_| | (_) | | (_| | | | | (_| | | || _ <| |___ | (__| | | __/ | | | |_ + \__, |\___/|_|\__,_|_| |_|\__, | |___|_| \_\\____| \___|_|_|\___|_| |_|\__| + |___/ |___/ + +A basic SSL-supporting IRC client written in golang. + +Usage +===== + +0. Run `./makecert.sh` to generate OpenSSL certificates. +1. Copy `.env.example` to `.env` and update the fields accordingly. +2. `go run *.go` will connect the bot to IRC. diff --git a/go.mod b/go.mod @@ -0,0 +1,9 @@ +module git.hanabi.in/go-irc + +go 1.16 + +require ( + github.com/go-irc/irc v2.1.0+incompatible // indirect + github.com/joho/godotenv v1.3.0 // indirect + github.com/n7st/go-ircformat v1.0.0 // indirect +) diff --git a/go.sum b/go.sum @@ -0,0 +1,5 @@ +github.com/go-irc/irc v2.1.0+incompatible h1:pg7pMVq5OYQbqTxceByD/EN8VIsba7DtKn49rsCnG8Y= +github.com/go-irc/irc v2.1.0+incompatible/go.mod h1:jJILTRy8s/qOvusiKifAEfhQMVwft1ZwQaVJnnzmyX4= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/n7st/go-ircformat v1.0.0/go.mod h1:CkKjJGioYFrkniV8TtaLs9rRk58vIagqkdXngLw4d/Q= diff --git a/helper-fns.go b/helper-fns.go @@ -0,0 +1,86 @@ +package main + +import ( + "log" + "os" + "strings" + "time" + + "github.com/go-irc/irc" + "github.com/joho/godotenv" +) + +func IsPM(receiver string) bool { + return !strings.HasPrefix(receiver, "#") +} + +func GetMaintainers() []string { + err := godotenv.Load() + if err != nil { + log.Fatal(err) + } + MAINTAINERS := os.Getenv("MAINTAINERS") + maintainers := strings.Split(MAINTAINERS, ",") + return maintainers +} + +func InformErrAndQuit(c *irc.Client, m *irc.Message) { + maintainers := GetMaintainers() + for _, maintainer := range maintainers { + SendMsg(c, maintainer, "Error") + } + time.Sleep(5 * time.Second) + os.Exit(1) +} + +func HandlePM(msg string, sender string) { + maintainers := GetMaintainers() + if msg != "KILL" { + return + } + for _, maintainer := range maintainers { + if maintainer == sender { + os.Exit(1) + } + } +} + +func GetChans() []string { + err := godotenv.Load() + if err != nil { + log.Fatal(err) + } + IRC_CHANNELS := os.Getenv("IRC_CHANNELS") + channels := strings.Split(IRC_CHANNELS, ",") + return channels +} + +func GetIRCEnvVars() (string, string, string, string) { + err := godotenv.Load() + if err != nil { + log.Fatal(err) + } + IRC_NICK := os.Getenv("IRC_NICK") + IRC_PASS := os.Getenv("IRC_PASS") + IRC_USER := os.Getenv("IRC_USER") + IRC_NAME := os.Getenv("IRC_NAME") + return IRC_NICK, IRC_PASS, IRC_USER, IRC_NAME +} + +func GetMsgDetails(m *irc.Message) (string, string, string) { + from := m.Name + to := m.Params[0] + msg := m.Params[1] + return from, to, msg +} + +func JoinChannels(c *irc.Client) { + IRC_CHANNELS := GetChans() + for _, channel := range IRC_CHANNELS { + c.Writef("JOIN %s", channel) + } +} + +func SendMsg(c *irc.Client, to string, msg string) { + c.Writef("%s %s %s", "PRIVMSG", to, msg) +} diff --git a/irc-config.go b/irc-config.go @@ -0,0 +1,41 @@ +package main + +import ( + "crypto/tls" + "fmt" + + "github.com/go-irc/irc" +) + +func GetIRCConfig() irc.ClientConfig { + IRC_NICK, IRC_PASS, IRC_USER, IRC_NAME := GetIRCEnvVars() + ircConfig := irc.ClientConfig{ + Nick: IRC_NICK, + Pass: IRC_PASS, + User: IRC_USER, + Name: IRC_NAME, + Handler: irc.HandlerFunc(Handler), + } + return ircConfig +} + +func GetIRCClient(conn *tls.Conn) *irc.Client { + ircConfig := GetIRCConfig() // IRC configuration + client := irc.NewClient(conn, ircConfig) // Create IRC Client + return client +} + +func Handler(c *irc.Client, m *irc.Message) { + if m.Command == "001" { + JoinChannels(c) + } else if m.Command == "PRIVMSG" { + from, to, msg := GetMsgDetails(m) + if IsPM(to) { + HandlePM(msg, from) + } else { + fmt.Println(msg, from, to) + } + } else if m.Command == "ERROR" { + InformErrAndQuit(c, m) + } +} diff --git a/irc.go b/irc.go @@ -0,0 +1,18 @@ +package main + +import ( + "log" +) + +func main() { + conn := GetTLSConn() + defer conn.Close() + + log.Println("client: connected to: ", conn.RemoteAddr()) + + ircClient := GetIRCClient(conn) + err := ircClient.Run() // Run IRC client + if err != nil { + log.Fatal(err) + } +} diff --git a/makecert.sh b/makecert.sh @@ -0,0 +1,9 @@ +#!/bin/zsh +# call this script with an email address (valid or not). +# like: +# ./makecert.sh joe@random.com +mkdir .certs +rm .certs/* +echo "make client cert" +openssl req -new -nodes -x509 -out .certs/client.pem -keyout .certs/client.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=www.random.com/emailAddress=$1" + diff --git a/tls-fns.go b/tls-fns.go @@ -0,0 +1,48 @@ +package main + +import ( + "crypto/tls" + "log" + "os" + + "github.com/joho/godotenv" +) + +func GetCert() tls.Certificate { + err := godotenv.Load() + if err != nil { + log.Fatal(err) + } + + SSL_CERT := os.Getenv("SSL_CERT") + SSL_KEY := os.Getenv("SSL_KEY") + + cert, err := tls.LoadX509KeyPair(SSL_CERT, SSL_KEY) + if err != nil { + log.Fatal(err) + } + return cert +} + +func GetTLSConfig(cert tls.Certificate) tls.Config { + config := tls.Config{ + Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: false, + } + return config +} + +func GetTLSConn() *tls.Conn { + err := godotenv.Load() + if err != nil { + log.Fatal(err) + } + IRC_SERVER := os.Getenv("IRC_SERVER") + cert := GetCert() // Load certs + config := GetTLSConfig(cert) // Create TLS configuration + conn, err := tls.Dial("tcp", IRC_SERVER, &config) // Connect TLS connection + if err != nil { + log.Fatalf("Client: dial: %s", err) + } + return conn +}