lysenko/handlers/quotedb/handlers.go

242 lines
6.2 KiB
Go

// Package quotedb implements a plugin to store quotes for channels. We pop them
// into a database to aid indexing / etc. Recommend SQLite.
package quotedb
import (
"context"
"fmt"
"log"
"math/rand"
"regexp"
"sort"
"strconv"
"strings"
"github.com/tcolgate/hugot"
)
type AddQuoteHandler struct {
QuoteDB *QuoteDB
}
type LastQuoteHandler struct {
QuoteDB *QuoteDB
}
type FindQuoteHandler struct {
QuoteDB *QuoteDB
}
type RandQuoteHandler struct {
QuoteDB *QuoteDB
}
type QuoteHandler struct {
QuoteDB *QuoteDB
}
func (h *AddQuoteHandler) Describe() (string, string) {
return "addquote", "Add a quote to the database"
}
func (h *AddQuoteHandler) do(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, quote string) error {
rsp, err := h.QuoteDB.AddQuote(m.Channel, quote, m.From)
if err != nil {
log.Printf("%s: AddQuote error: %v", m.Channel, err)
return fmt.Errorf("Failed to add quote, sorry!")
}
w.Send(ctx, m.Reply(fmt.Sprintf("Quote %d added", rsp.QuoteId)))
return nil
}
func (h *AddQuoteHandler) Hears() *regexp.Regexp {
return regexp.MustCompile(`^!addquote (.*)$`)
}
func (h *AddQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
parts := strings.SplitN(m.Text, " ", 2)
if len(parts) != 2 {
return nil
}
return h.do(ctx, w, m, parts[1])
}
func (h *AddQuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
h.do(ctx, w, m, submatches[0][1])
}
func (h *LastQuoteHandler) Describe() (string, string) {
return "lastquote", "Show the last-added quote"
}
func (h *LastQuoteHandler) do(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
quote, err := h.QuoteDB.FindLastQuote(m.Channel)
if err != nil {
log.Printf("%s: Error in LastQuote: %v", m.Channel, err)
return nil
}
w.Send(ctx, m.Reply(fmt.Sprintf("Quote %d by %s: %s", quote.QuoteId, quote.Author, quote.Data)))
return nil
}
func (h *LastQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
return h.do(ctx, w, m)
}
func (h *LastQuoteHandler) Hears() *regexp.Regexp {
return regexp.MustCompile(`^!lastquote$`)
}
func (h *LastQuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
h.do(ctx, w, m)
}
func (h *FindQuoteHandler) Describe() (string, string) {
return "findquote", "Search for a quote by substring"
}
func (h *FindQuoteHandler) find(channel, term string) string {
quotes, err := h.QuoteDB.FindQuotesByString(term, channel)
if err != nil {
return "Couldn't find any quotes matching search term"
}
switch len(quotes) {
case 0:
return "No results"
case 1:
return fmt.Sprintf("1 match. ID %d: %s: %s", quotes[0].QuoteId, quotes[0].Author, quotes[0].Data)
}
quote := quotes[0]
quoteCount := strconv.Itoa(len(quotes))
if len(quotes) > 5 {
quoteCount = "More than 5"
}
quoteIds := make([]int, 0)
max := 5
if len(quotes) < max {
max = len(quotes)
}
indices := rand.Perm(len(quotes))[0:max]
for _, i := range indices {
if quotes[i] == quote || quotes[i] == nil {
continue
}
quoteIds = append(quoteIds, quotes[i].QuoteId)
}
quoteIds = append(quoteIds, quote.QuoteId)
sort.Ints(quoteIds)
quoteIdStrs := make([]string, len(quoteIds))
for i, d := range quoteIds {
quoteIdStrs[i] = strconv.Itoa(d)
}
idStr := strings.Join(quoteIdStrs, ", ")
return fmt.Sprintf("%s matches. IDs: %s. Most recent: %s: %s", quoteCount, idStr, quote.Author, quote.Data)
}
func (h *FindQuoteHandler) do(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, term string) error {
response := h.find(m.Channel, term)
w.Send(ctx, m.Reply(response))
return nil
}
func (h *FindQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
parts := strings.SplitN(m.Text, " ", 2)
if len(parts) != 2 {
return nil
}
return h.do(ctx, w, m, parts[1])
}
func (h *FindQuoteHandler) Hears() *regexp.Regexp {
return regexp.MustCompile(`^!findquote (.*)$`)
}
func (h *FindQuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
h.do(ctx, w, m, submatches[0][1])
}
func (h *RandQuoteHandler) Describe() (string, string) {
return "randquote", "Show a random quote"
}
func (h *RandQuoteHandler) do(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
quote, err := h.QuoteDB.FindRandomQuote(m.Channel)
if err != nil {
log.Printf("%s: Error in RandQuote: %v", m.Channel, err)
return nil
}
w.Send(ctx, m.Reply(fmt.Sprintf("Quote %d by %s: %s", quote.QuoteId, quote.Author, quote.Data)))
return nil
}
func (h *RandQuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
return h.do(ctx, w, m)
}
func (h *RandQuoteHandler) Hears() *regexp.Regexp {
return regexp.MustCompile(`^!randquote$`)
}
func (h *RandQuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
h.do(ctx, w, m)
}
func (h *QuoteHandler) Describe() (string, string) {
return "quote", "show a specific quote, identified by ID"
}
func (h *QuoteHandler) show(channel, qid string) string {
id, err := strconv.Atoi(qid)
if err != nil {
return "invalid ID"
}
quote, err := h.QuoteDB.FindQuoteByQuoteId(id, channel)
if err != nil {
return fmt.Sprintf("Couldn't find quote %d", id)
}
if quote.DeletedAt.Valid {
delName := "(unknown)"
if quote.DeletedBy.Valid {
delName = quote.DeletedBy.String
}
return fmt.Sprintf("Quote %d was deleted by %s at %s", quote.Id, delName, quote.DeletedAt.Time.String())
}
return fmt.Sprintf("%s: %s", quote.Author, quote.Data)
}
func (h *QuoteHandler) do(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, qid string) error {
w.Send(ctx, m.Reply(h.show(m.Channel, qid)))
return nil
}
func (h *QuoteHandler) Command(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message) error {
parts := strings.SplitN(m.Text, " ", 2)
if len(parts) != 2 {
return nil
}
return h.do(ctx, w, m, parts[1])
}
func (h *QuoteHandler) Hears() *regexp.Regexp {
return regexp.MustCompile(`^!quote (.*)$`)
}
func (h *QuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
h.do(ctx, w, m, submatches[0][1])
}