Skip to content

Instantly share code, notes, and snippets.

@FSX
Last active December 20, 2015 11:39
Show Gist options
  • Save FSX/6125218 to your computer and use it in GitHub Desktop.
Save FSX/6125218 to your computer and use it in GitHub Desktop.
Simple IRC bot in Go.

Installation

$ mkdir Go && cd Go
$ mkdir -p {bin,pkg,src,src/pikabot,src/pikachu}
# Put content of pikachu.go in src/pikachu
# Put content of pikabot.go in src/pikabot and edit pikabot.go
$ go install pikabot
$ ./bin/pikabot
package main
import (
"fmt"
"math/rand"
"os"
"os/signal"
"pikachu"
"strings"
"time"
)
// https://en.wikiquote.org/wiki/Pok%C3%A9mon
// http://www.pokemonquotes.com/brock-quotes/
var QUOTES []string = []string{
// Pikachu
"“PIKA!” — Pikachu",
"“Pika Pika” — Pikachu",
"“Pika Pi” — Pikachu",
"“Chuuuuu” — Pikachu",
"“Pi-pi-pi” — Pikachu",
"“PIKA-CHU-UUUUUUU!” — Pikachu",
"“Pi Pikachu!” — Pikachu",
// Brock
"“Hey I know! I'll use my trusty frying pan as a drying pan!” — Brock",
"“How about some prune juice!?” — Brock",
"“I believe in rock-hard defense and determination!” — Brock",
"“Her temper could sure use a little evolution.” — Brock",
"“Don't you worry, Totodile, I'm gonna teach you how to be successful in love, just like me!” — Brock",
"“When you have lemons, you make lemonade; and when you have rice, you make rice balls.” — Brock",
"“For me, summer means bathing suits and girls to wear them!” — Brock",
"“DON'T YOU GET IT?!! If two Butterfree fall in love, their trainers can meet, and THEY CAN FALL IN LOVE, TOO!” — Brock",
"“AHH! I wasted a donut!” — Brock",
"“Ash and Misty, sittin' in a tree... Ha ha ha ha ha!” — Brock",
"“Time to dry up, water girl!” — Brock",
"“I never forget the face of a pretty girl! This book helps me remember their names.” — Brock",
"“This is the only song I know.” — Brock",
// Misty
"“If I want your opinion I'LL ASK FOR IT!!!” — Misty",
"“HE'S NOT MY BOYFRIEND!” — Misty",
"“Well, Ash Ketchum... finally, I know how you feel about me.” — Misty",
"“Pikachu, you're a Pika-pal!” — Misty",
"“Well, my name.....My name is Anne Chovie.” — Misty",
// Ash Ketchum
"“Choose it or lose it!” — Ash Ketchum",
"“I'm twice as good as Gary!” — Ash Ketchum",
"“It was an egg-cident! Get it? 'Egg?'” — Ash Ketchum",
"“I have my own method of bending spoons. (bends the spoon with his hands) Ha! Muscle over mind!” — Ash Ketchum",
"“Please don't eat my hat.” — Ash Ketchum",
"“I'm having a major hat crisis. Could you try to steal Pikachu some other time?” — Ash Ketchum",
"“Uh, my... name... is... Ketchup! No, wait! My name is really Tom Ato!” — Ash Ketchum",
"“Let's eat fast so we can eat again!” — Ash Ketchum",
"“That mini-Misty is even more scary than she is.” — Ash Ketchum",
"“You look like a guy anyway!” — Ash Ketchum",
"“Please don't stare at me like that! I'm a very shy little girl!” — Ash Ketchum",
"“You guys go, I'll be fine. Just don't bring my mom home too late.” — Ash Ketchum",
"“Yo, Brocko!” — Ash Ketchum",
"“I choose you pikachu!” — Ash Ketchum",
// Prof. Oak
"“So, tell me about yourself. Are you a boy or a girl?” — Prof. Oak",
"“This is my grandson. He's been your rival since you were both babies. Err... what was his name again...?” — Prof. Oak",
"“You look more like you're ready for bed than Pokémon training.” — Prof. Oak",
"“There's an ongoing debate in the academic community as to whether these Pidgey represent evolution, devolution, or some mutated strain.” — Prof. Oak",
}
func main() {
rand.Seed(time.Now().UnixNano())
l := len(QUOTES)
// Setup IRC bot
p, err := pikachu.Connect(
"irc.zeronode.net:6667",
"Pikachu",
"Pikachu", "",
"#Hive5")
if err != nil {
panic(err)
}
defer p.Close()
// Graceful shutdown for Ctrl+C
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
p.Close()
os.Exit(0)
}()
// Listen to receiving channel
for {
message, ok := <-p.Receive
if ok == false {
break
}
fmt.Println("IN: ", message)
if message.Command == "PRIVMSG" && strings.HasPrefix(message.Final, "!pokemon") {
p.Privmsg(message.Arguments[0], QUOTES[rand.Intn(l)])
}
}
}
package pikachu
import (
"bufio"
"fmt"
"net"
"strings"
)
type Message struct {
Prefix string
Command string
Arguments []string
Final string
}
type Pikachu struct {
address string
nickname string
username string
password string
channel string // Maybe list?
connection net.Conn
Send chan string // Messages to the server
Receive chan Message // Messages from the server
}
func Connect(address, nickname, username, password, channel string) (p *Pikachu, err error) {
p = &Pikachu{address, nickname, username, password, channel, nil, nil, nil}
err = p.Connect()
return
}
func (p *Pikachu) Connect() (err error) {
p.Send = make(chan string)
p.Receive = make(chan Message)
if p.connection, err = net.Dial("tcp", p.address); err != nil {
return
}
go func() {
var (
message Message
init bool = true
)
reader := bufio.NewReader(p.connection)
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
message = p.ParseMessage(line)
p.Receive <- message
if message.Command == "PING" {
p.Pong(message.Final)
}
if init {
if message.Command == "266" {
init = false
p.Join(p.channel)
}
}
}
}()
p.User(p.username, p.nickname)
p.Nick(p.nickname)
return
}
// [:prefix ](command )[arguments ]*[ :final_argument]
func (p *Pikachu) ParseMessage(line string) Message {
var (
prefix string
command string
arguments []string
final string
)
if pos := strings.Index(line, " :"); pos != -1 {
arguments, final = strings.Split(line[:pos], " "), line[pos+2:len(line)-2]
} else {
arguments = strings.Split(line, " ")
}
if arguments[0][0] == ':' && len(arguments) >= 2 {
prefix = arguments[0]
command = arguments[1]
arguments = arguments[2:]
} else {
command = arguments[0]
arguments = arguments[1:]
}
return Message{prefix, command, arguments, final}
}
// server_or_nick[!user[@hostname]]
func (p *Pikachu) ParsePrefix(source string) (server_or_nick, user, host string) {
if n := strings.IndexRune(source, '!'); n != -1 {
server_or_nick = source[:n]
if m := strings.IndexRune(source, '@'); m != -1 {
user = source[n:m]
host = source[m:]
}
} else {
server_or_nick = source
}
return
}
func (p *Pikachu) Close() {
p.connection.Close()
close(p.Send)
close(p.Receive)
}
func (p *Pikachu) Write(data string) {
fmt.Println("OUT: ", data)
if _, err := p.connection.Write([]byte(data + "\r\n")); err != nil {
panic(err)
}
}
func (p *Pikachu) User(username, realname string) {
p.Write(fmt.Sprintf("USER %s +iw * :%s", username, realname))
}
func (p *Pikachu) Nick(nickname string) {
p.Write(fmt.Sprintf("NICK %s", nickname))
}
func (p *Pikachu) Join(channel string) {
p.Write(fmt.Sprintf("JOIN %s", channel))
}
func (p *Pikachu) Pong(payload string) {
p.Write(fmt.Sprintf("PONG :%s", payload))
}
func (p *Pikachu) Privmsg(target, message string) {
p.Write(fmt.Sprintf("PRIVMSG %s :%s", target, message))
}
func (p *Pikachu) Quit(message string) {
p.Write(fmt.Sprintf("QUIT :%s", message))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment