CTF2021/irc_bot
Archived
4
0
This repository has been archived on 2021-02-12. You can view files and clone it, but cannot push or open issues or pull requests.
irc_bot/main.go

383 lines
7.9 KiB
Go
Raw Permalink Normal View History

2021-01-16 00:20:50 +03:00
package main
import (
2021-01-16 17:55:13 +03:00
"encoding/json"
2021-01-16 00:20:50 +03:00
"fmt"
2021-01-16 17:55:13 +03:00
"io/ioutil"
2021-01-16 13:33:30 +03:00
"math/rand"
"net/http"
2021-01-16 00:20:50 +03:00
"os"
"regexp"
2021-01-19 11:50:20 +03:00
"strings"
2021-01-16 17:55:13 +03:00
"sync"
2021-01-16 13:33:30 +03:00
"time"
2021-01-16 00:20:50 +03:00
2021-01-16 17:55:13 +03:00
"log"
2021-01-19 11:50:20 +03:00
"strconv"
2021-01-16 00:20:50 +03:00
irc "github.com/thoj/go-ircevent"
)
2021-01-16 18:23:56 +03:00
var channel = "#ctf"
2021-01-17 12:50:12 +03:00
var sendHi = ""
2021-01-27 12:32:04 +03:00
var writeHistory = false
2021-01-16 00:20:50 +03:00
const (
2021-01-19 11:50:20 +03:00
pattern = "(?i)\\b(cat|gato|miau|meow|garfield|lolcat)[s|z]{0,1}\\b"
2021-01-16 17:55:13 +03:00
nyastat = "nyastat"
2021-01-16 13:33:30 +03:00
msgPrefix = "I love cats! Here's a fact: %s ^_^"
gifPrefix = "Meow! Here's a gif: %s ^_^"
2021-01-16 00:20:50 +03:00
)
2021-01-17 12:50:12 +03:00
// environment variables
const (
2021-01-27 12:32:04 +03:00
userEnv = "IRC_USER"
nickEnv = "IRC_NICK"
himsgEnv = "IRC_SAY_HI"
passEnv = "IRC_PASS"
chanEnv = "IRC_CHAN"
urlEnv = "IRC_URL"
invitionsEnv = "IRC_ENABLE_INVITIONS"
chanHistoryEnv = "IRC_CHAN_HISTORY"
dbPathEnv = "IRC_DB_PATH"
2021-01-17 12:50:12 +03:00
)
2021-01-16 00:20:50 +03:00
type catFact struct {
Fact string `json:"fact"`
Length int `json:"length"`
}
var (
re = regexp.MustCompile(pattern)
catFactsURL = "http://catfact.ninja/fact"
)
2021-01-16 17:55:13 +03:00
type stats struct {
userMessages map[string]uint
2021-01-19 11:50:20 +03:00
invites map[string]bool
2021-01-27 12:32:04 +03:00
history *history
2021-01-16 17:55:13 +03:00
mux *sync.Mutex
}
func newStats() *stats {
return &stats{
userMessages: make(map[string]uint),
2021-01-19 11:50:20 +03:00
invites: make(map[string]bool),
2021-01-16 17:55:13 +03:00
mux: &sync.Mutex{},
}
}
func (s *stats) addUser(user string) {
s.mux.Lock()
defer s.mux.Unlock()
if v, ok := s.userMessages[user]; ok {
log.Printf("User %s writes %d message", user, v+1)
s.userMessages[user]++
return
}
log.Printf("First message from user %s", user)
s.userMessages[user] = 1
}
func (s *stats) online(e *irc.Event) int {
e.Connection.Mutex.Lock()
defer e.Connection.Mutex.Unlock()
return -1
}
func (s *stats) realStats(e *irc.Event) {
2021-01-19 11:50:20 +03:00
e.Connection.Privmsg(e.Nick, "🙀🙀🙀 WoW 🙀🙀🙀 NYAStat 🙀🙀🙀")
2021-01-16 17:55:13 +03:00
t := "User %s sent %d messages"
for u, v := range s.userMessages {
2021-01-19 11:50:20 +03:00
e.Connection.Privmsg(e.Nick, fmt.Sprintf(t, u, v))
2021-01-16 17:55:13 +03:00
}
}
func (s *stats) printStats(e *irc.Event) {
s.online(e)
s.realStats(e)
}
2021-01-19 11:50:20 +03:00
func (s *stats) invitionRequest(e *irc.Event) {
s.mux.Lock()
defer s.mux.Unlock()
if !isInvitionsEnabled() {
return
}
if sended, ok := s.invites[e.Nick]; !ok || !sended {
log.Printf("Sending invite request to %s", e.Nick)
s.invites[e.Nick] = true
e.Connection.Privmsg(e.Nick, fmt.Sprintf("Привет, %s! В тиму 0х0 нужны два опытных человека. + в приват @difrex", e.Nick))
}
}
2021-01-17 12:50:12 +03:00
func addCallbacks(irccon *irc.Connection, s *stats) {
2021-01-16 00:20:50 +03:00
irccon.AddCallback("001", func(e *irc.Event) {
log.Println("Welcome message:", e.Message())
2021-01-17 12:50:12 +03:00
// Login
e.Connection.Privmsg("nickserv", "identify "+os.Getenv(passEnv))
2021-01-16 00:20:50 +03:00
2021-01-17 12:50:12 +03:00
// Join to channel
e.Connection.Join(channel)
2021-01-19 11:50:20 +03:00
e.Connection.Action(channel, "=^_^= https://gitea.difrex.ru/CTF2021/irc_bot")
2021-01-27 12:32:04 +03:00
e.Connection.Action(channel, "😸🦄😸 Now with HiStOrY!!! 😸🦄😸")
2021-01-19 11:50:20 +03:00
2021-01-17 12:50:12 +03:00
// Greetings
if sendHi != "" {
e.Connection.Privmsg(channel, `Hi!`)
e.Connection.Privmsg(channel, `I'm a cat facts bot!`)
e.Connection.Privmsg(channel, `Source code and issue tracker: https://gitea.difrex.ru/CTF2021/irc_bot`)
e.Connection.Privmsg(channel, `Now with gifs! ^_^`)
}
2021-01-16 00:20:50 +03:00
})
2021-01-19 11:50:20 +03:00
irccon.AddCallback("JOIN", s.invitionRequest)
2021-01-16 00:20:50 +03:00
irccon.AddCallback("PRIVMSG", func(e *irc.Event) {
log.Printf("Message from %s received: %s", e.Nick, e.Message())
2021-01-16 17:55:13 +03:00
s.addUser(e.Nick)
doCommand(e.Message(), e, s)
2021-01-16 00:20:50 +03:00
})
2021-01-17 12:50:12 +03:00
}
func main() {
nick := os.Getenv(nickEnv)
irccon := irc.IRC(nick, os.Getenv(userEnv))
2021-01-27 12:32:04 +03:00
if irccon == nil {
log.Fatal("Connection is nil")
}
2021-01-17 12:50:12 +03:00
s := newStats()
2021-01-27 12:32:04 +03:00
history, err := Open(os.Getenv(dbPathEnv))
if err != nil {
writeHistory = false
}
s.history = history
2021-01-17 12:50:12 +03:00
2021-01-27 12:50:34 +03:00
if history != nil {
defer history.db.Close()
}
2021-01-17 12:50:12 +03:00
irccon.VerboseCallbackHandler = false
irccon.Debug = false
irccon.UseTLS = false
addCallbacks(irccon, s)
2021-01-16 00:20:50 +03:00
2021-01-27 12:32:04 +03:00
if err := irccon.Connect(os.Getenv(urlEnv)); err != nil {
2021-01-16 00:20:50 +03:00
fmt.Printf("Err %s", err)
return
}
irccon.Loop()
}
2021-01-19 11:50:20 +03:00
func isHelpCommand(command string, e *irc.Event) bool {
if e.Nick != channel && strings.ToLower(command) == "help" {
return true
}
return false
}
2021-01-27 12:32:04 +03:00
func isHistoryCommand(command string, e *irc.Event) bool {
if e.Nick != channel && strings.HasPrefix(e.Message(), "history") {
return true
}
return false
}
func parseHistoryCommandArgs(e *irc.Event) [2]string {
var args [2]string
message := strings.Split(e.Message(), " ")
if len(message) == 3 {
args[0] = message[1]
args[1] = message[2]
}
if len(message) == 2 {
args[0] = message[1]
}
return args
}
func showLogs(e *irc.Event, s *stats) {
var logs []LogLine
args := parseHistoryCommandArgs(e)
if args[0] == "all" {
l, err := s.history.GetAllLog()
if err != nil {
log.Println("Error:", err)
return
}
logs = l
}
if args[0] != "" && args[1] != "" {
l, err := s.history.GetRangedLogs(args[0], args[1])
if err != nil {
log.Println("Error:", err)
return
}
logs = l
}
if len(logs) == 0 {
e.Connection.Privmsg(e.Nick, ":( such empty")
}
e.Connection.Privmsg(e.Nick, "WOW!!! HiStOrY!!!")
for _, l := range logs {
e.Connection.Privmsg(e.Nick, fmt.Sprintf("%s [%s] %s", l.Time.Format("2006-01-02T15:04:05"), l.Nick, l.Message))
}
}
2021-01-19 11:50:20 +03:00
func showHelp(e *irc.Event) {
e.Connection.Privmsg(e.Nick, "HELP -- show this help")
e.Connection.Privmsg(e.Nick, "cat|meow|etc -- show cat fact or gif")
e.Connection.Privmsg(e.Nick, "nyastat -- show #ctf channel stats")
2021-01-27 12:32:04 +03:00
e.Connection.Privmsg(e.Nick, "history -- show #ctf channel history logs")
2021-01-27 12:35:14 +03:00
e.Connection.Privmsg(e.Nick, " -- EXAMPLE")
e.Connection.Privmsg(e.Nick, "history all -- show all logs")
e.Connection.Privmsg(e.Nick, "history 2021-01-27T12:19:55 2021-01-27T12:20:00 -- show date ranged logs")
2021-01-19 11:50:20 +03:00
}
2021-01-16 17:55:13 +03:00
func doCommand(command string, e *irc.Event, s *stats) {
2021-01-19 11:50:20 +03:00
if ok := statCommand(command); ok {
s.printStats(e)
return
}
if isHelpCommand(command, e) {
showHelp(e)
return
}
2021-01-16 17:55:13 +03:00
2021-01-27 12:32:04 +03:00
if isHistoryCommand(command, e) {
showLogs(e, s)
return
}
2021-01-16 17:55:13 +03:00
if ok := checkCommad(command, e.Connection); !ok {
2021-01-27 12:32:04 +03:00
err := s.history.WriteLog(&LogLine{
Nick: e.Nick,
Message: e.Message(),
Time: time.Now(),
})
if err != nil {
log.Println("Error:", err)
}
2021-01-16 13:33:30 +03:00
return
}
rand.Seed(time.Now().UnixNano())
i := rand.Intn(50)
fmt.Println(i)
if i > 30 {
2021-01-19 11:50:20 +03:00
catGif(e)
2021-01-16 13:33:30 +03:00
return
}
2021-01-19 11:50:20 +03:00
catFacts(e)
2021-01-16 17:55:13 +03:00
}
func statCommand(command string) bool {
if command == nyastat {
return true
}
return false
2021-01-16 13:33:30 +03:00
}
func checkCommad(command string, con *irc.Connection) bool {
2021-01-19 11:50:20 +03:00
return re.MatchString(command)
2021-01-16 13:33:30 +03:00
}
2021-01-19 11:50:20 +03:00
func catGif(e *irc.Event) (string, error) {
2021-01-16 13:33:30 +03:00
res, err := http.Get("http://thecatapi.com/api/images/get?format=src&type=gif")
if err != nil {
return "", err
}
2021-01-19 11:50:20 +03:00
log.Println("Send gif")
e.Connection.Privmsg(e.Nick, fmt.Sprintf(gifPrefix, res.Request.URL.String()))
2021-01-16 13:33:30 +03:00
return fmt.Sprintf(gifPrefix, res.Request.URL.String()), nil
}
2021-01-16 17:55:13 +03:00
func getJson(url string, v interface{}) error {
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
return json.Unmarshal(body, v)
}
2021-01-19 11:50:20 +03:00
func catFacts(e *irc.Event) (string, error) {
log.Println("Send fact")
2021-01-16 00:20:50 +03:00
data := &catFact{}
2021-01-16 17:55:13 +03:00
err := getJson(catFactsURL, data)
2021-01-16 00:20:50 +03:00
if err != nil {
return "", err
}
if len(data.Fact) == 0 {
return "", nil
}
e.Connection.Privmsg(e.Nick, fmt.Sprintf(msgPrefix, data.Fact))
2021-01-16 00:20:50 +03:00
return fmt.Sprintf(msgPrefix, data.Fact), nil
}
2021-01-16 18:23:56 +03:00
2021-01-19 11:50:20 +03:00
func getChanName(e *irc.Event) string {
c := channel
if _, err := strconv.Atoi(e.Nick); err != nil {
c = e.Nick
}
return c
}
2021-01-27 12:32:04 +03:00
func isFetureEnabled(feature string) bool {
if feature != "" && strings.ToLower(feature) == "yes" {
2021-01-19 11:50:20 +03:00
return true
}
2021-01-27 12:32:04 +03:00
2021-01-19 11:50:20 +03:00
return false
}
2021-01-27 12:32:04 +03:00
func isInvitionsEnabled() bool {
return isFetureEnabled(os.Getenv(invitionsEnv))
}
func isHistoryEnabled() bool {
return isFetureEnabled(os.Getenv(chanHistoryEnv))
}
2021-01-19 11:50:20 +03:00
func getNick() string {
return os.Getenv(nickEnv)
}
2021-01-16 18:23:56 +03:00
func init() {
2021-01-17 12:50:12 +03:00
ch := os.Getenv(chanEnv)
2021-01-16 18:23:56 +03:00
if ch != "" {
channel = ch
}
2021-01-17 12:50:12 +03:00
sendHi = os.Getenv(himsgEnv)
2021-01-27 12:32:04 +03:00
writeHistory = isFetureEnabled(os.Getenv(chanHistoryEnv))
2021-01-16 18:23:56 +03:00
}