212 lines
5.2 KiB
Go
212 lines
5.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 (
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"math/rand"
|
||
|
"regexp"
|
||
|
"sort"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/tcolgate/hugot"
|
||
|
"golang.org/x/net/context"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
hearAddQuote = regexp.MustCompile(`^\!addquote (.*)$`)
|
||
|
hearLastQuote = regexp.MustCompile(`^\!lastquote$`)
|
||
|
hearFindQuote = regexp.MustCompile(`^\!findquote (.*)$`)
|
||
|
hearRandQuote = regexp.MustCompile(`^\!randquote$`)
|
||
|
hearQuote = regexp.MustCompile(`^\!quote (\d+)$`)
|
||
|
)
|
||
|
|
||
|
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) Hears() *regexp.Regexp {
|
||
|
return hearAddQuote
|
||
|
}
|
||
|
|
||
|
func (h *AddQuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
|
||
|
if len(submatches) != 1 || len(submatches[0]) != 2 {
|
||
|
log.Printf("Submatches weird! %+v", submatches)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var rsp string
|
||
|
quote, err := h.QuoteDB.AddQuote(m.Channel, submatches[0][1], m.From)
|
||
|
if err != nil {
|
||
|
log.Printf("%s: AddQuote error: %v", m.Channel, err)
|
||
|
rsp = "Failed to add quote, sorry!"
|
||
|
} else {
|
||
|
rsp = fmt.Sprintf("Quote %d added", quote.QuoteId)
|
||
|
}
|
||
|
|
||
|
w.Send(ctx, m.Reply(rsp))
|
||
|
}
|
||
|
|
||
|
func (h *LastQuoteHandler) Describe() (string, string) {
|
||
|
return "lastquote", "Show the last-added quote"
|
||
|
}
|
||
|
|
||
|
func (h *LastQuoteHandler) Hears() *regexp.Regexp {
|
||
|
return hearLastQuote
|
||
|
}
|
||
|
|
||
|
func (h *LastQuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
|
||
|
quote, err := h.QuoteDB.FindLastQuote(m.Channel)
|
||
|
if err != nil {
|
||
|
log.Printf("%s: Error in LastQuote: %v", m.Channel, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
rsp := fmt.Sprintf("Quote %d by %s: %s", quote.QuoteId, quote.Author, quote.Data)
|
||
|
w.Send(ctx, m.Reply(rsp))
|
||
|
}
|
||
|
|
||
|
func (h *FindQuoteHandler) Describe() (string, string) {
|
||
|
return "findquote", "Search for a quote by substring"
|
||
|
}
|
||
|
|
||
|
func (h *FindQuoteHandler) Hears() *regexp.Regexp {
|
||
|
return hearFindQuote
|
||
|
}
|
||
|
|
||
|
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) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
|
||
|
if len(submatches) != 1 || len(submatches[0]) != 2 {
|
||
|
log.Printf("Submatches weird! %+v", submatches)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
rsp := h.find(m.Channel, submatches[0][1])
|
||
|
w.Send(ctx, m.Reply(rsp))
|
||
|
}
|
||
|
|
||
|
func (h *RandQuoteHandler) Describe() (string, string) {
|
||
|
return "randquote", "Show a random quote"
|
||
|
}
|
||
|
|
||
|
func (h *RandQuoteHandler) Hears() *regexp.Regexp {
|
||
|
return hearRandQuote
|
||
|
}
|
||
|
|
||
|
func (h *RandQuoteHandler) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
|
||
|
quote, err := h.QuoteDB.FindRandomQuote(m.Channel)
|
||
|
if err != nil {
|
||
|
log.Printf("%s: Error in RandQuote: %v", m.Channel, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
rsp := fmt.Sprintf("Quote %d by %s: %s", quote.QuoteId, quote.Author, quote.Data)
|
||
|
w.Send(ctx, m.Reply(rsp))
|
||
|
}
|
||
|
|
||
|
func (h *QuoteHandler) Describe() (string, string) {
|
||
|
return "quote", "show a specific quote, identified by ID"
|
||
|
}
|
||
|
|
||
|
func (h *QuoteHandler) Hears() *regexp.Regexp {
|
||
|
return hearQuote
|
||
|
}
|
||
|
|
||
|
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) Heard(ctx context.Context, w hugot.ResponseWriter, m *hugot.Message, submatches [][]string) {
|
||
|
if len(submatches) != 1 || len(submatches[0]) != 2 {
|
||
|
log.Printf("Submatches weird! %+v", submatches)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
rsp := h.show(m.Channel, submatches[0][1])
|
||
|
w.Send(ctx, m.Reply(rsp))
|
||
|
}
|