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:
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
+}