Initial commit of utilities
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
177
cmd/ircmirror/ircwriters.go
Normal file
177
cmd/ircmirror/ircwriters.go
Normal file
@@ -0,0 +1,177 @@
|
||||
/* 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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user