Files
hbot/cmd/irc-simple-responder/main.go
Jason A. Donenfeld b52ec14d2d Initial commit of utilities
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-06-03 18:38:26 +02:00

127 lines
3.3 KiB
Go

/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2021 Jason A. Donenfeld. All Rights Reserved.
*/
package main
import (
"bufio"
"flag"
"fmt"
"log"
"net"
"os"
"regexp"
"strings"
"sync"
"time"
"golang.zx2c4.com/irc/hbot"
)
func main() {
channelsArg := flag.String("channels", "", "channels to join, separated by commas")
serverArg := flag.String("server", "", "server and port")
nickArg := flag.String("nick", "", "nickname")
passwordArg := flag.String("password-file", "", "optional file with password")
messageArg := flag.String("message", "", "message with which to respond")
flag.Parse()
if matched, _ := regexp.MatchString(`^(#[a-zA-Z0-9_-]+,)*(#[a-zA-Z0-9_-]+)$`, *channelsArg); !matched {
fmt.Fprintln(os.Stderr, "Invalid channels")
flag.Usage()
os.Exit(1)
}
if _, _, err := net.SplitHostPort(*serverArg); err != nil {
fmt.Fprintln(os.Stderr, "Invalid server")
flag.Usage()
os.Exit(1)
}
if matched, _ := regexp.MatchString(`^[a-zA-Z0-9\[\]_-]{1,16}$`, *nickArg); !matched {
fmt.Fprintln(os.Stderr, "Invalid nick")
flag.Usage()
os.Exit(1)
}
password := ""
if len(*passwordArg) > 0 {
f, err := os.Open(*passwordArg)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to open password file: %v\n", err)
os.Exit(1)
}
password, err = bufio.NewReader(f).ReadString('\n')
f.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to read password file: %v\n", err)
os.Exit(1)
}
if len(password) > 0 && password[len(password)-1] == '\n' {
password = password[:len(password)-1]
}
}
if len(*messageArg) == 0 {
fmt.Fprintln(os.Stderr, "Missing message")
flag.Usage()
os.Exit(1)
}
const messengerTimeout = time.Minute * 10
messengers := make(map[string]time.Time, 1024)
var messengersMu sync.Mutex
go func() {
for range time.Tick(messengerTimeout / 20) {
messengersMu.Lock()
for nick, last := range messengers {
if time.Since(last) >= messengerTimeout {
delete(messengers, nick)
}
}
messengersMu.Unlock()
}
}()
bot := hbot.NewBot(&hbot.Config{
Host: *serverArg,
Nick: *nickArg,
User: hbot.CommonBotUserPrefix + *nickArg,
Channels: strings.Split(*channelsArg, ","),
Logger: hbot.Logger{Verbosef: log.Printf, Errorf: log.Printf},
Password: password,
})
bot.AddTrigger(hbot.Trigger{
Condition: func(b *hbot.Bot, m *hbot.Message) bool {
if m.Command != "PRIVMSG" || strings.HasPrefix(m.Prefix.User, hbot.CommonBotUserPrefix) || strings.HasPrefix(m.Prefix.User, "~"+hbot.CommonBotUserPrefix) {
return false
}
nick := strings.ToLower(m.Prefix.Name)
messengersMu.Lock()
defer messengersMu.Unlock()
if last, ok := messengers[nick]; ok && time.Since(last) < messengerTimeout {
return false
}
if len(messengers) > 1024*1024*1024 {
return false
}
messengers[nick] = time.Now()
return true
},
Action: func(b *hbot.Bot, m *hbot.Message) {
message := *messageArg
target := m.Prefix.Name
if strings.Contains(m.Param(0), "#") {
target = m.Param(0)
if target[0] == '@' || target[0] == '+' {
target = target[1:]
}
message = m.Prefix.Name + ": " + message
}
log.Printf("Responding to %q in %q", m.Prefix.String(), target)
b.Msg(target, message)
},
})
for {
bot.Run()
time.Sleep(time.Second * 5)
}
}