mirror of
https://code.dnix.de/da/flokatirc
synced 2026-07-27 07:49:44 +02:00
Merged branch master into master
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2015,2016 Andreas Neue
|
Copyright (c) 2015,2016 Andreas Neue, Daniel Aberger
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ var (
|
|||||||
channels = flag.String("chan", "#test", "Channels to join")
|
channels = flag.String("chan", "#test", "Channels to join")
|
||||||
nsname = flag.String("nsname", "NickServ", "NickServ name")
|
nsname = flag.String("nsname", "NickServ", "NickServ name")
|
||||||
nspass = flag.String("nspass", "", "NickServ password")
|
nspass = flag.String("nspass", "", "NickServ password")
|
||||||
|
mods = flag.String("mods", "", "Modules to load")
|
||||||
params = flag.String("params", "", "Module params")
|
params = flag.String("params", "", "Module params")
|
||||||
|
autocmd = flag.String("autocmd", "", "Autosend IRC command")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -43,7 +45,7 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
sayCh = make(chan string, 1024)
|
sayCh = make(chan string, 1024)
|
||||||
|
|
||||||
xlog.Init(xlog.INFO)
|
xlog.Init(xlog.DEBUG)
|
||||||
|
|
||||||
//bot := ircx.Classic(*server, *name)
|
//bot := ircx.Classic(*server, *name)
|
||||||
cfg := ircx.Config{User: *name, MaxRetries: 1000}
|
cfg := ircx.Config{User: *name, MaxRetries: 1000}
|
||||||
@@ -57,7 +59,8 @@ func main() {
|
|||||||
//mods := strings.Split(*modules, ",")
|
//mods := strings.Split(*modules, ",")
|
||||||
//TODO: implement more robust list parsing
|
//TODO: implement more robust list parsing
|
||||||
|
|
||||||
modules.Init(sayCh, *params)
|
modules.Init(sayCh, *mods, *params)
|
||||||
|
modules.ModParams["_nick"] = *name
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@@ -82,6 +85,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
go Ping(bot)
|
||||||
|
if *autocmd != "" {
|
||||||
|
println(*autocmd)
|
||||||
|
bot.Sender.Send(&irc.Message{Command: *autocmd})
|
||||||
|
}
|
||||||
RegisterHandlers(bot)
|
RegisterHandlers(bot)
|
||||||
bot.HandleLoop()
|
bot.HandleLoop()
|
||||||
xlog.Info("Exiting")
|
xlog.Info("Exiting")
|
||||||
@@ -94,6 +102,13 @@ func RegisterHandlers(bot *ircx.Bot) {
|
|||||||
bot.HandleFunc(irc.PRIVMSG, PrivmsgHandler)
|
bot.HandleFunc(irc.PRIVMSG, PrivmsgHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Ping(bot *ircx.Bot) {
|
||||||
|
for {
|
||||||
|
time.Sleep(1 * time.Minute)
|
||||||
|
bot.Sender.Send(&irc.Message{Command: irc.PING})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ConnectHandler(s ircx.Sender, m *irc.Message) {
|
func ConnectHandler(s ircx.Sender, m *irc.Message) {
|
||||||
if *nspass != "" {
|
if *nspass != "" {
|
||||||
xlog.Info("Authenticating with NickServ: %v, %v", *name, *nspass)
|
xlog.Info("Authenticating with NickServ: %v, %v", *name, *nspass)
|
||||||
|
|||||||
@@ -5,16 +5,13 @@ package modules
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.dnix.de/an/xlog"
|
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ()
|
var ()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["announcements"] = anncouncementsHandleMessage
|
MsgFuncs["announcements"] = anncouncementsHandleMessage
|
||||||
xlog.Info("Announcements module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func anncouncementsHandleMessage(m *irc.Message) {
|
func anncouncementsHandleMessage(m *irc.Message) {
|
||||||
|
|||||||
+1
-5
@@ -12,8 +12,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.dnix.de/an/xlog"
|
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
"github.com/sorcix/irc/ctcp"
|
"github.com/sorcix/irc/ctcp"
|
||||||
)
|
)
|
||||||
@@ -29,9 +27,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["coffee"] = coffeeHandleMessage
|
MsgFuncs["coffee"] = coffeeHandleMessage
|
||||||
r.names = make(map[string]bool)
|
r.names = make(map[string]bool)
|
||||||
xlog.Info("Coffee module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func coffeeHandleMessage(m *irc.Message) {
|
func coffeeHandleMessage(m *irc.Message) {
|
||||||
@@ -108,4 +105,3 @@ func printAction(s string) {
|
|||||||
msg := ctcp.Encode(ctcp.ACTION, fmt.Sprintf(s))
|
msg := ctcp.Encode(ctcp.ACTION, fmt.Sprintf(s))
|
||||||
SayCh <- fmt.Sprintf("%s\n%s", "*", msg)
|
SayCh <- fmt.Sprintf("%s\n%s", "*", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -8,14 +8,11 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.dnix.de/an/xlog"
|
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["fortune"] = fortuneHandleMessage
|
MsgFuncs["fortune"] = fortuneHandleMessage
|
||||||
xlog.Info("Fortune module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fortuneHandleMessage(m *irc.Message) {
|
func fortuneHandleMessage(m *irc.Message) {
|
||||||
|
|||||||
@@ -7,14 +7,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.dnix.de/an/xlog"
|
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["fuzzytime"] = fuzzytimeHandleMessage
|
MsgFuncs["fuzzytime"] = fuzzytimeHandleMessage
|
||||||
xlog.Info("Fuzzytime module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fuzzytimeHandleMessage(m *irc.Message) {
|
func fuzzytimeHandleMessage(m *irc.Message) {
|
||||||
|
|||||||
+2
-4
@@ -8,8 +8,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.dnix.de/an/xlog"
|
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,8 +16,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["gogs"] = gogsHandleMessage
|
MsgFuncs["gogs"] = gogsHandleMessage
|
||||||
xlog.Info("Gogs module initialized")
|
RunFuncs["gogs"] = gogsConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func gogsConfig() {
|
func gogsConfig() {
|
||||||
|
|||||||
@@ -0,0 +1,273 @@
|
|||||||
|
// vi:ts=4:sts=4:sw=4:noet:tw=72
|
||||||
|
|
||||||
|
package modules
|
||||||
|
|
||||||
|
// This Markov chain code is taken from the "Generating arbitrary text"
|
||||||
|
// codewalk: http://golang.org/doc/codewalk/markov/
|
||||||
|
//
|
||||||
|
// Minor modifications have been made to make it easier to integrate
|
||||||
|
// with a webserver and to save/load state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.dnix.de/an/xlog"
|
||||||
|
|
||||||
|
"github.com/sorcix/irc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var markovChain *MarkovChain
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
MsgFuncs["markov"] = markovHandleMessage
|
||||||
|
RunFuncs["markov"] = markovRun
|
||||||
|
}
|
||||||
|
|
||||||
|
func markovHandleMessage(m *irc.Message) {
|
||||||
|
text := m.Trailing
|
||||||
|
if text == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text = markovParseText(text)
|
||||||
|
|
||||||
|
answerLen, _ := strconv.Atoi(ModParams["markov-answer-len"])
|
||||||
|
respChance, _ := strconv.Atoi(ModParams["markov-response-chance"])
|
||||||
|
if rand.Intn(100) <= respChance || strings.Index(text, strings.ToLower(ModParams["_nick"])) != -1 {
|
||||||
|
responseText := markovChain.Generate(answerLen, text)
|
||||||
|
if responseText != "" {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Duration(rand.Intn(8)+2) * time.Second)
|
||||||
|
SayCh <- "*\n" + responseText
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
markovChain.Write(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func markovRun() {
|
||||||
|
prefixLen, _ := strconv.Atoi(ModParams["markov-prefix-len"])
|
||||||
|
markovChain = markovNewChain(prefixLen)
|
||||||
|
err := markovChain.Load(ModParams["markov-state-file"])
|
||||||
|
if err != nil {
|
||||||
|
xlog.Error(err.Error())
|
||||||
|
}
|
||||||
|
filepath := ModParams["markov-import-file"]
|
||||||
|
if filepath != "-" {
|
||||||
|
file, _ := os.Open(filepath)
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
text := scanner.Text()
|
||||||
|
text = markovParseText(text)
|
||||||
|
if text != "" {
|
||||||
|
markovChain.Write(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(60 * time.Second)
|
||||||
|
markovChain.Save(ModParams["markov-state-file"])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func markovParseText(text string) string {
|
||||||
|
messageRegex := regexp.MustCompile(`<([^>]+)>`)
|
||||||
|
matches := messageRegex.FindAllStringSubmatch(text, -1)
|
||||||
|
for _, matches2 := range matches {
|
||||||
|
|
||||||
|
if strings.HasPrefix(matches2[1], "http") || strings.HasPrefix(matches2[1], "mailto") {
|
||||||
|
text = strings.Replace(text, matches2[0], "", -1)
|
||||||
|
|
||||||
|
} else if strings.HasPrefix(matches2[1], "@U") {
|
||||||
|
parts := strings.SplitN(matches2[1], "|", 2)
|
||||||
|
|
||||||
|
if len(parts) == 2 {
|
||||||
|
text = strings.Replace(text, matches2[0], "@"+parts[1], -1)
|
||||||
|
} else {
|
||||||
|
text = strings.Replace(text, matches2[0], "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if strings.HasPrefix(matches2[1], "@") {
|
||||||
|
text = strings.Replace(text, matches2[0], matches2[1], -1)
|
||||||
|
|
||||||
|
} else if strings.HasPrefix(matches2[1], "#") {
|
||||||
|
parts := strings.SplitN(matches2[1], "|", 2)
|
||||||
|
|
||||||
|
if len(parts) == 2 {
|
||||||
|
text = strings.Replace(text, matches2[0], "#"+parts[1], -1)
|
||||||
|
} else {
|
||||||
|
text = strings.Replace(text, matches2[0], "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text = strings.TrimSpace(text)
|
||||||
|
|
||||||
|
text = strings.Replace(text, "<", "<", -1)
|
||||||
|
text = strings.Replace(text, ">", ">", -1)
|
||||||
|
text = strings.Replace(text, "&", "&", -1)
|
||||||
|
|
||||||
|
return strings.ToLower(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix is a Markov chain prefix of one or more words.
|
||||||
|
type MarkovPrefix []string
|
||||||
|
|
||||||
|
// String returns the Prefix as a string (for use as a map key).
|
||||||
|
func (p MarkovPrefix) String() string {
|
||||||
|
return strings.Trim(strings.Join(p, " "), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift removes the first word from the Prefix and appends the given word.
|
||||||
|
func (p MarkovPrefix) Shift(word string) {
|
||||||
|
copy(p, p[1:])
|
||||||
|
p[len(p)-1] = word
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkovChain contains a map ("chain") of prefixes to a list of suffixes.
|
||||||
|
// A prefix is a string of prefixLen words joined with spaces.
|
||||||
|
// A suffix is a single word. A prefix can have multiple suffixes.
|
||||||
|
type MarkovChain struct {
|
||||||
|
MarkovChain map[string][]string
|
||||||
|
prefixLen int
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMarkovChain returns a new MarkovChain with prefixes of prefixLen words.
|
||||||
|
func markovNewChain(prefixLen int) *MarkovChain {
|
||||||
|
return &MarkovChain{
|
||||||
|
MarkovChain: make(map[string][]string),
|
||||||
|
prefixLen: prefixLen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write parses the bytes into prefixes and suffixes that are stored in MarkovChain.
|
||||||
|
func (c *MarkovChain) Write(in string) (int, error) {
|
||||||
|
in = strings.ToLower(in)
|
||||||
|
if strings.HasPrefix(in, strings.ToLower(ModParams["_nick"])) {
|
||||||
|
tok := strings.Split(in, " ")
|
||||||
|
in = strings.Replace(in, tok[0]+" ", "", 1)
|
||||||
|
}
|
||||||
|
sr := strings.NewReader(in)
|
||||||
|
p := make(MarkovPrefix, c.prefixLen)
|
||||||
|
for {
|
||||||
|
var s string
|
||||||
|
if _, err := fmt.Fscan(sr, &s); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
key := p.String()
|
||||||
|
c.mu.Lock()
|
||||||
|
c.MarkovChain[key] = append(c.MarkovChain[key], s)
|
||||||
|
c.mu.Unlock()
|
||||||
|
xlog.Debug("Chain len: %d, learned [%s] [%s]", len(c.MarkovChain), key, s)
|
||||||
|
p.Shift(s)
|
||||||
|
}
|
||||||
|
return len(in), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate returns a string of at most n words generated from MarkovChain.
|
||||||
|
func (c *MarkovChain) Generate(n int, in string) string {
|
||||||
|
in = strings.ToLower(in)
|
||||||
|
if strings.HasPrefix(in, strings.ToLower(ModParams["_nick"])) {
|
||||||
|
tok := strings.Split(in, " ")
|
||||||
|
in = strings.Replace(in, tok[0]+" ", "", 1)
|
||||||
|
}
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
p := make(MarkovPrefix, c.prefixLen)
|
||||||
|
p = strings.Split(in, " ")
|
||||||
|
if len(p) > c.prefixLen {
|
||||||
|
i := rand.Intn(len(p) - 1)
|
||||||
|
p = p[i : i+c.prefixLen]
|
||||||
|
}
|
||||||
|
prefix := p.String()
|
||||||
|
xlog.Debug("Looking for answer on [%s]", prefix)
|
||||||
|
var words []string
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
choices := c.MarkovChain[p.String()]
|
||||||
|
if len(choices) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next := choices[rand.Intn(len(choices))]
|
||||||
|
words = append(words, next)
|
||||||
|
if strings.HasSuffix(next, ".") || strings.HasSuffix(next, "!") || strings.HasSuffix(next, "?") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.Shift(next)
|
||||||
|
}
|
||||||
|
prefix = strings.Trim(prefix, " ")
|
||||||
|
if len(words) == 0 {
|
||||||
|
xlog.Debug("No answer found")
|
||||||
|
return prefix + " ... pfrrrz"
|
||||||
|
} else {
|
||||||
|
xlog.Debug("Found words: [%s]", strings.Join(words, " "))
|
||||||
|
return prefix + " " + strings.Join(words, " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the chain to a file
|
||||||
|
func (c *MarkovChain) Save(fileName string) error {
|
||||||
|
// Open the file for writing
|
||||||
|
fo, err := os.Create(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// close fo on exit and check for its returned error
|
||||||
|
defer func() {
|
||||||
|
if err := fo.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create an encoder and dump to it
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
enc := gob.NewEncoder(fo)
|
||||||
|
err = enc.Encode(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the chain from a file
|
||||||
|
func (c *MarkovChain) Load(fileName string) error {
|
||||||
|
// Open the file for reading
|
||||||
|
fi, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// close fi on exit and check for its returned error
|
||||||
|
defer func() {
|
||||||
|
if err := fi.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create a decoder and read from it
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
dec := gob.NewDecoder(fi)
|
||||||
|
err = dec.Decode(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+28
-3
@@ -8,26 +8,51 @@ package modules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SayCh chan string
|
SayCh chan string
|
||||||
MsgHandlers = make(map[string]func(*irc.Message))
|
MsgFuncs = make(map[string]func(*irc.Message))
|
||||||
|
RunFuncs = make(map[string]func())
|
||||||
ModParams = make(map[string]string)
|
ModParams = make(map[string]string)
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init(ch chan string, params string) {
|
func Init(ch chan string, mods, params string) {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
SayCh = ch
|
SayCh = ch
|
||||||
|
for mod, _ := range MsgFuncs {
|
||||||
|
if !contains(strings.Split(mods, ","), mod) {
|
||||||
|
delete(MsgFuncs, mod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for mod, _ := range RunFuncs {
|
||||||
|
if !contains(strings.Split(mods, ","), mod) {
|
||||||
|
delete(RunFuncs, mod)
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, param := range strings.Split(params, "!") {
|
for _, param := range strings.Split(params, "!") {
|
||||||
kv := strings.Split(param, ":")
|
kv := strings.Split(param, ":")
|
||||||
ModParams[kv[0]] = kv[1]
|
ModParams[kv[0]] = kv[1]
|
||||||
}
|
}
|
||||||
|
for _, fn := range RunFuncs {
|
||||||
|
go fn()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleMessage(m *irc.Message) {
|
func HandleMessage(m *irc.Message) {
|
||||||
for _, fn := range MsgHandlers {
|
for _, fn := range MsgFuncs {
|
||||||
fn(m)
|
fn(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func contains(sa []string, s string) bool {
|
||||||
|
for _, a := range sa {
|
||||||
|
if a == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
+247
@@ -0,0 +1,247 @@
|
|||||||
|
// vi:ts=4:sts=4:sw=4:noet:tw=72
|
||||||
|
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flokatirc/util"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sorcix/irc"
|
||||||
|
|
||||||
|
"code.dnix.de/an/xlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type quizQuestion struct {
|
||||||
|
category, question, answer, regexp, level string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
quizCtrlCh = make(chan string, 1024)
|
||||||
|
quizAnswerCh = make(chan *irc.Message, 1024)
|
||||||
|
quizQuestions []quizQuestion
|
||||||
|
quizQuestionValues = map[string]int{"extreme": 5, "hard": 4, "normal": 3, "easy": 2, "baby": 1}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
MsgFuncs["quiz"] = quizHandleMessage
|
||||||
|
RunFuncs["quiz"] = quiz
|
||||||
|
rand.Seed(time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func quizHandleMessage(m *irc.Message) {
|
||||||
|
tok := strings.Split(m.Trailing, " ")
|
||||||
|
if len(tok) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch tok[0] {
|
||||||
|
case "!quiz", "!quizstart":
|
||||||
|
quizCtrlCh <- "start"
|
||||||
|
break
|
||||||
|
case "!quizstop":
|
||||||
|
quizCtrlCh <- "stop"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
quizAnswerCh <- m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func quiz() {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
SayCh <- fmt.Sprintf("%s\nquiz mod test. !quizstart to start, !quizstop to end.", "*")
|
||||||
|
for {
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
select {
|
||||||
|
case s := <-quizCtrlCh:
|
||||||
|
if s == "start" {
|
||||||
|
quizRun()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func quizRun() {
|
||||||
|
quizQuestions = quizLoadQuestions("questions.txt")
|
||||||
|
ranklist := make(map[string]int)
|
||||||
|
SayCh <- fmt.Sprintf("%s\nQuiz gestartet.", "*")
|
||||||
|
for {
|
||||||
|
solved, cont, solver, gain := quizAskQuestion()
|
||||||
|
if !cont {
|
||||||
|
SayCh <- fmt.Sprintf("%s\nQuiz beendet.", "*")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if solved {
|
||||||
|
if _, exists := ranklist[solver]; exists {
|
||||||
|
ranklist[solver] += gain
|
||||||
|
} else {
|
||||||
|
ranklist[solver] = gain
|
||||||
|
}
|
||||||
|
if ranklist[solver] > 1000 {
|
||||||
|
bold := byte(0x02)
|
||||||
|
solver = string(bold) + solver + string(bold)
|
||||||
|
SayCh <- fmt.Sprintf("%s\n%s gewinnt!", "*", solver)
|
||||||
|
quizPrintRanklist(ranklist)
|
||||||
|
SayCh <- fmt.Sprintf("%s\nQuiz beendet.", "*")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quizPrintRanklist(ranklist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func quizPrintRanklist(ranklist map[string]int) {
|
||||||
|
SayCh <- fmt.Sprintf("%s\nAktueller Punktestand:", "*")
|
||||||
|
for k, v := range ranklist {
|
||||||
|
SayCh <- fmt.Sprintf("%s\n%s: %d", "*", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func quizAskQuestion() (bool, bool, string, int) {
|
||||||
|
q := quizQuestions[rand.Intn(len(quizQuestions))]
|
||||||
|
SayCh <- fmt.Sprintf("%s\nEine Frage aus der Kategorie \"%s\" (%s):", "*", q.category, q.level)
|
||||||
|
SayCh <- fmt.Sprintf("%s\n>>> %s <<<", "*", q.question)
|
||||||
|
solved, cont, solver, gain := quizWaitForAnswer(q)
|
||||||
|
if !solved {
|
||||||
|
SayCh <- fmt.Sprintf("%s\nDie richtige Antwort wäre gewesen:", "*")
|
||||||
|
SayCh <- fmt.Sprintf("%s\n%s [%s]", "*", q.answer, q.regexp)
|
||||||
|
}
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
return solved, cont, solver, gain
|
||||||
|
}
|
||||||
|
|
||||||
|
func quizWaitForAnswer(q quizQuestion) (bool, bool, string, int) {
|
||||||
|
i := 0
|
||||||
|
haveAnswer := false
|
||||||
|
timer := time.Now().Unix()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case m := <-quizAnswerCh:
|
||||||
|
haveAnswer = true
|
||||||
|
points := 10 - ((time.Now().Unix() - timer) / 10)
|
||||||
|
if points < 1 {
|
||||||
|
points = 1
|
||||||
|
}
|
||||||
|
re, err := regexp.Compile(q.regexp)
|
||||||
|
if err != nil {
|
||||||
|
xlog.Error(err.Error())
|
||||||
|
return false, false, "", 0
|
||||||
|
}
|
||||||
|
if re.MatchString(strings.ToLower(util.ReplaceUmlauts(m.Trailing))) {
|
||||||
|
bold := byte(0x02)
|
||||||
|
solver := strings.Split(m.Prefix.String(), "!")[0]
|
||||||
|
solver = string(bold) + solver + string(bold)
|
||||||
|
value := quizQuestionValues[q.level]
|
||||||
|
gain := int(points) * value
|
||||||
|
SayCh <- fmt.Sprintf("%s\n%s hat die Frage korrekt beantwortet und erhält dafür %d (%d * %d) Punkte.", "*", solver, gain, points, value)
|
||||||
|
return true, true, solver, gain
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case s := <-quizCtrlCh:
|
||||||
|
if s == "stop" {
|
||||||
|
return false, false, "", 0
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case <-time.After(15 * time.Second):
|
||||||
|
i++
|
||||||
|
if i > 3 {
|
||||||
|
if haveAnswer {
|
||||||
|
SayCh <- fmt.Sprintf("%s\nNiemand konnte die Frage korrekt beantworten.", "*")
|
||||||
|
return false, true, "", 0
|
||||||
|
} else {
|
||||||
|
SayCh <- fmt.Sprintf("%s\nNiemand hat auf die Frage geantwortet.", "*")
|
||||||
|
return false, false, "", 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quizGiveHint(q, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func quizGiveHint(q quizQuestion, n int) {
|
||||||
|
var hint string
|
||||||
|
haveHint := false
|
||||||
|
for {
|
||||||
|
hint = ""
|
||||||
|
for i := 0; i < len(q.answer); i++ {
|
||||||
|
if string(q.answer[i]) == " " {
|
||||||
|
hint += " "
|
||||||
|
} else {
|
||||||
|
if rand.Intn(10) < 2 {
|
||||||
|
haveHint = true
|
||||||
|
hint += string(q.answer[i])
|
||||||
|
} else {
|
||||||
|
hint += "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if haveHint {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SayCh <- fmt.Sprintf("%s\nTipp: %s", "*", hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func quizLoadQuestions(path string) []quizQuestion {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
xlog.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
questions := make([]quizQuestion, 0)
|
||||||
|
q := quizQuestion{"", "", "", "", ""}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if len(line) == 0 || line[0] == '#' || line[0] == '\n' {
|
||||||
|
if q.category != "" {
|
||||||
|
questions = append(questions, q)
|
||||||
|
q = quizQuestion{"", "", "", "", "normal"}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tok := strings.Split(line, ":: ")
|
||||||
|
switch tok[0] {
|
||||||
|
case "Category":
|
||||||
|
q.category = tok[1]
|
||||||
|
break
|
||||||
|
case "Question":
|
||||||
|
q.question = tok[1]
|
||||||
|
break
|
||||||
|
case "Answer":
|
||||||
|
q.answer = strings.Replace(util.ReplaceUmlauts(tok[1]), "#", "", -1)
|
||||||
|
if q.regexp == "" {
|
||||||
|
regexp := strings.Split(strings.ToLower(util.ReplaceUmlauts(tok[1])), "#")
|
||||||
|
if len(regexp) > 1 {
|
||||||
|
q.regexp = regexp[1]
|
||||||
|
} else {
|
||||||
|
q.regexp = regexp[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "Regexp":
|
||||||
|
q.regexp = util.ReplaceUmlauts(strings.ToLower(tok[1]))
|
||||||
|
break
|
||||||
|
case "Level":
|
||||||
|
q.level = tok[1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
xlog.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions
|
||||||
|
}
|
||||||
+2
-3
@@ -24,9 +24,8 @@ import (
|
|||||||
var hideOutput = true
|
var hideOutput = true
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["rss"] = rssHandleMessage
|
MsgFuncs["rss"] = rssHandleMessage
|
||||||
go rssRun()
|
RunFuncs["rss"] = rssRun
|
||||||
xlog.Info("RSS module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func rssRun() {
|
func rssRun() {
|
||||||
|
|||||||
+2
-3
@@ -48,9 +48,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["sc"] = scHandleMessage
|
MsgFuncs["sc"] = scHandleMessage
|
||||||
go scScrapeLoop()
|
RunFuncs["sc"] = scScrapeLoop
|
||||||
xlog.Info("SC module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func scHandleMessage(m *irc.Message) {
|
func scHandleMessage(m *irc.Message) {
|
||||||
|
|||||||
+5
-6
@@ -3,11 +3,10 @@
|
|||||||
package modules
|
package modules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flokatirc/util"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"code.dnix.de/an/xlog"
|
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
)
|
)
|
||||||
@@ -522,8 +521,8 @@ var quotes = [][]string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["stoll"] = stollHandleMessage
|
MsgFuncs["stoll"] = stollHandleMessage
|
||||||
xlog.Info("Stoll module initialized")
|
rand.Seed(time.Now().Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
func stollHandleMessage(m *irc.Message) {
|
func stollHandleMessage(m *irc.Message) {
|
||||||
@@ -534,7 +533,7 @@ func stollHandleMessage(m *irc.Message) {
|
|||||||
if tok[0] == "!stoll" {
|
if tok[0] == "!stoll" {
|
||||||
line := ""
|
line := ""
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
line += quotes[i][util.Random(0, len(quotes[i]))]
|
line += quotes[i][rand.Intn(len(quotes[i]))]
|
||||||
line += " "
|
line += " "
|
||||||
}
|
}
|
||||||
line += "[Dr. Axel Stoll, promovierter Naturwissenschaftler]"
|
line += "[Dr. Axel Stoll, promovierter Naturwissenschaftler]"
|
||||||
|
|||||||
+2
-3
@@ -137,9 +137,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["twitch"] = twitchHandleMessage
|
MsgFuncs["twitch"] = twitchHandleMessage
|
||||||
go pollStreamData()
|
RunFuncs["twitch"] = pollStreamData
|
||||||
xlog.Info("Twitch module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func twitchHandleMessage(m *irc.Message) {
|
func twitchHandleMessage(m *irc.Message) {
|
||||||
|
|||||||
+2
-3
@@ -71,9 +71,8 @@ type WeatherObject struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MsgHandlers["weather"] = weatherHandleMessage
|
MsgFuncs["weather"] = weatherHandleMessage
|
||||||
go weatherConfig()
|
RunFuncs["weather"] = weatherConfig
|
||||||
xlog.Info("Weather module initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func weatherConfig() {
|
func weatherConfig() {
|
||||||
|
|||||||
+10
-5
@@ -4,9 +4,8 @@ package util
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math/rand"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ToInt(v interface{}) int {
|
func ToInt(v interface{}) int {
|
||||||
@@ -48,7 +47,13 @@ func NumberToString(n int, sep rune) string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Random(min, max int) int {
|
func ReplaceUmlauts(s string) string {
|
||||||
rand.Seed(time.Now().Unix())
|
ret := strings.Replace(s, "Ä", "Ae", -1)
|
||||||
return rand.Intn(max-min) + min
|
ret = strings.Replace(ret, "Ö", "Oe", -1)
|
||||||
|
ret = strings.Replace(ret, "Ü", "Ue", -1)
|
||||||
|
ret = strings.Replace(ret, "ä", "ae", -1)
|
||||||
|
ret = strings.Replace(ret, "ö", "oe", -1)
|
||||||
|
ret = strings.Replace(ret, "ü", "ue", -1)
|
||||||
|
ret = strings.Replace(ret, "ß", "ss", -1)
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user