all repos — paprika @ 014e7ec5f807bba7c2d6ccb9b6fcb699b0f7e185

go rewrite of taigabot

plugins/tell.go (view raw)

  1package plugins
  2
  3import (
  4	"bytes"
  5	"crypto/rand"
  6	"encoding/gob"
  7	"fmt"
  8	"io"
  9	"sort"
 10	"strings"
 11	"time"
 12
 13	"git.icyphox.sh/paprika/database"
 14	"github.com/dgraph-io/badger/v3"
 15	"github.com/dustin/go-humanize"
 16	"gopkg.in/irc.v3"
 17)
 18
 19func init() {
 20	Register(Tell{})
 21}
 22
 23type Tell struct {
 24	From    string
 25	To      string
 26	Message string
 27	Time    time.Time
 28}
 29
 30func (Tell) Triggers() []string {
 31	return []string{".tell", ""}
 32}
 33
 34// Encodes message into encoding/gob for storage.
 35func (t *Tell) saveTell() error {
 36	data := bytes.Buffer{}
 37	enc := gob.NewEncoder(&data)
 38
 39	if err := enc.Encode(t); err != nil {
 40		return err
 41	}
 42	// Store key as 'tell/nick/randbytes'; should help with
 43	// easy prefix scans for tells.
 44	rnd := make([]byte, 8)
 45	rand.Read(rnd)
 46
 47	key := []byte(fmt.Sprintf("tell/%s/", t.To))
 48	key = append(key, rnd...)
 49	err := database.DB.Set(key, data.Bytes())
 50	if err != nil {
 51		return err
 52	}
 53	return nil
 54}
 55
 56// Decodes tell data from encoding/gob into a Tell.
 57func getTell(data io.Reader) (*Tell, error) {
 58	dec := gob.NewDecoder(data)
 59	t := Tell{}
 60	if err := dec.Decode(&t); err != nil {
 61		return nil, err
 62	}
 63
 64	return &t, nil
 65}
 66
 67func (t Tell) Execute(m *irc.Message) (string, error) {
 68	parts := strings.SplitN(m.Trailing(), " ", 3)
 69
 70	if parts[0] == ".tell" {
 71		// No message passed.
 72		if len(parts) == 2 {
 73			return "Usage: .tell <nick> <message>", nil
 74		}
 75
 76		t.From = strings.ToLower(m.Prefix.Name)
 77		t.To = strings.ToLower(parts[1])
 78		t.Message = parts[2]
 79		t.Time = time.Now()
 80
 81		if err := t.saveTell(); err != nil {
 82			return "Error saving message", err
 83		}
 84
 85		return "Your message will be sent!", &IsPrivateNotice{t.From}
 86	} else {
 87		// React to all other messages here.
 88		// Iterate over key prefixes to check if our tell
 89		// recepient has shown up. Then send his tell and delete
 90		// the keys.
 91
 92		// All pending tells.
 93		tells := []Tell{}
 94
 95		err := database.DB.Update(func(txn *badger.Txn) error {
 96			it := txn.NewIterator(badger.DefaultIteratorOptions)
 97			defer it.Close()
 98			prefix := []byte("tell/" + m.Prefix.Name)
 99			for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
100				item := it.Item()
101				k := item.Key()
102				err := item.Value(func(v []byte) error {
103					var r bytes.Reader
104					tell, err := getTell(&r)
105					if err != nil {
106						return err
107					}
108					tells = append(tells, *tell)
109					return nil
110				})
111				if err != nil {
112					return err
113				}
114				err = txn.Delete(k)
115				if err != nil {
116					return err
117				}
118			}
119			return nil
120		})
121		if err != nil {
122			return "", err
123		}
124
125		// Sort tells by time.
126		sort.Slice(tells, func(i, j int) bool {
127			return tells[i].Time.Before(tells[j].Time)
128		})
129
130		// Formatted tells in a slice, for joining into a string
131		// later.
132		tellsFmtd := []string{}
133		for _, tell := range tells {
134			tellsFmtd = append(
135				tellsFmtd,
136				fmt.Sprintf(
137					"%s sent you a message %s: %s",
138					tell.From, humanize.Time(tell.Time), tell.Message,
139				),
140			)
141		}
142
143		return strings.Join(tellsFmtd, "\n"), &IsPrivateNotice{To: t.To}
144	}
145}