127 lines
3.3 KiB
Go
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)
|
|
}
|
|
}
|