178 lines
4.3 KiB
Go
178 lines
4.3 KiB
Go
/* SPDX-License-Identifier: GPL-2.0
|
|
*
|
|
* Copyright (C) 2021 Jason A. Donenfeld. All Rights Reserved.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"container/list"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.zx2c4.com/irc/hbot"
|
|
)
|
|
|
|
var words []string
|
|
|
|
func init() {
|
|
f, err := os.Open("/usr/share/dict/words")
|
|
if err != nil {
|
|
log.Fatalf("Unable to open dictionary: %v", err)
|
|
}
|
|
defer f.Close()
|
|
scanner := bufio.NewScanner(f)
|
|
matcher := regexp.MustCompile(`^[a-zA-Z0-9_-]{3,}$`)
|
|
for scanner.Scan() {
|
|
word := scanner.Text()
|
|
if !matcher.MatchString(word) {
|
|
continue
|
|
}
|
|
words = append(words, word)
|
|
}
|
|
if len(words) == 0 {
|
|
log.Fatalln("Did not find any words in dictionary")
|
|
}
|
|
}
|
|
|
|
func randomNick() string {
|
|
return words[rand.Intn(len(words))] + words[rand.Intn(len(words))] + strconv.Itoa(rand.Intn(10000))
|
|
}
|
|
|
|
type ircWriter struct {
|
|
mu sync.Mutex
|
|
bot *hbot.Bot
|
|
nick string
|
|
mungedNick string
|
|
usageElem *list.Element
|
|
name string
|
|
}
|
|
|
|
type ircWriters struct {
|
|
mu sync.Mutex
|
|
byNick map[string]*ircWriter
|
|
byUsage list.List
|
|
server string
|
|
channel string
|
|
}
|
|
|
|
func (writers *ircWriters) runWriter(name string, dialer func(network, address string) (net.Conn, error)) {
|
|
writer := &ircWriter{name: name}
|
|
startNick := randomNick()
|
|
logf := func(format string, args ...interface{}) {
|
|
log.Printf("[DST %s] "+format, append([]interface{}{name}, args...)...)
|
|
}
|
|
writer.bot = hbot.NewBot(&hbot.Config{
|
|
Host: writers.server,
|
|
Nick: startNick,
|
|
User: hbot.CommonBotUserPrefix + startNick,
|
|
Channels: []string{writers.channel},
|
|
Dial: dialer,
|
|
Logger: hbot.Logger{Verbosef: logf, Errorf: logf},
|
|
})
|
|
go func() {
|
|
<-writer.bot.Joined()
|
|
writers.mu.Lock()
|
|
defer writers.mu.Unlock()
|
|
writer.mu.Lock()
|
|
defer writer.mu.Unlock()
|
|
writer.usageElem = writers.byUsage.PushBack(writer)
|
|
}()
|
|
writer.bot.AddTrigger(hbot.Trigger{
|
|
Condition: func(bot *hbot.Bot, m *hbot.Message) bool {
|
|
return (m.Command == "436" || m.Command == "433") && len(writer.nick) > 0
|
|
},
|
|
Action: func(bot *hbot.Bot, m *hbot.Message) {
|
|
logf("Failed with nick %q, trying %q\n", writer.mungedNick, writer.mungedNick+"_")
|
|
writer.mungedNick += "_"
|
|
if len(writer.mungedNick) > 16 {
|
|
usidx := strings.IndexByte(writer.mungedNick, '_')
|
|
uslen := len(writer.mungedNick[usidx:])
|
|
if uslen >= 16 {
|
|
writer.mungedNick = writer.nick + "-"
|
|
if len(writer.mungedNick) > 16 {
|
|
writer.mungedNick = writer.nick[:15] + "-"
|
|
}
|
|
} else {
|
|
writer.mungedNick = writer.mungedNick[:16-uslen] + writer.mungedNick[usidx:]
|
|
}
|
|
}
|
|
bot.SetNick(writer.mungedNick)
|
|
},
|
|
})
|
|
writer.bot.Run()
|
|
writers.mu.Lock()
|
|
writer.mu.Lock()
|
|
if writer.usageElem != nil {
|
|
writers.byUsage.Remove(writer.usageElem)
|
|
delete(writers.byNick, writer.nick)
|
|
}
|
|
writer.mu.Unlock()
|
|
writers.mu.Unlock()
|
|
}
|
|
|
|
func (writers *ircWriters) getWriter(nick string) *ircWriter {
|
|
writers.mu.Lock()
|
|
if writer, ok := writers.byNick[nick]; ok {
|
|
writer.mu.Lock()
|
|
if writer.nick == nick {
|
|
writers.byUsage.MoveToFront(writer.usageElem)
|
|
writers.mu.Unlock()
|
|
if writer.bot.Nick() != writer.mungedNick {
|
|
log.Printf("[DST %s] Changing nick to %q\n", writer.name, nick)
|
|
writer.bot.SetNick(writer.mungedNick)
|
|
}
|
|
return writer
|
|
}
|
|
writer.mu.Unlock()
|
|
}
|
|
if writers.byUsage.Len() == 0 {
|
|
writers.mu.Unlock()
|
|
return nil
|
|
}
|
|
writer := writers.byUsage.Back().Value.(*ircWriter)
|
|
writer.mu.Lock()
|
|
delete(writers.byNick, writer.nick)
|
|
writer.nick = nick
|
|
writer.mungedNick = nick + "-"
|
|
if len(writer.mungedNick) > 16 {
|
|
writer.mungedNick = nick[:15] + "-"
|
|
}
|
|
writers.byNick[nick] = writer
|
|
writers.byUsage.MoveToFront(writer.usageElem)
|
|
writers.mu.Unlock()
|
|
log.Printf("[DST %s] Changing nick to %q\n", writer.name, nick)
|
|
writer.bot.SetNick(writer.mungedNick)
|
|
return writer
|
|
}
|
|
|
|
func (writers *ircWriters) queueMessage(from, message string) {
|
|
writer := writers.getWriter(from)
|
|
if writer == nil {
|
|
time.AfterFunc(time.Second*3, func() {
|
|
writers.queueMessage(from, message)
|
|
})
|
|
return
|
|
}
|
|
log.Printf("[DST %s] Queueing message from %q\n", writer.name, from)
|
|
writer.bot.Msg(writers.channel, message)
|
|
writer.mu.Unlock()
|
|
}
|
|
|
|
func newIrcWriterGroup(server, channel string) *ircWriters {
|
|
return &ircWriters{
|
|
mu: sync.Mutex{},
|
|
byNick: make(map[string]*ircWriter, 400),
|
|
server: server,
|
|
channel: channel,
|
|
}
|
|
}
|