achivements: first draft

This commit is contained in:
Chris Sexton 2020-04-24 16:24:29 -04:00 committed by Chris Sexton
parent 888216647f
commit e91d0bdf95
1 changed files with 177 additions and 42 deletions

View File

@ -2,27 +2,18 @@ package achievements
import ( import (
"fmt" "fmt"
"regexp"
"strings"
"time" "time"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot" "github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg" "github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/config" "github.com/velour/catbase/config"
) )
// I feel dirty about this, but ;shrug
var instance *AchievementsPlugin
type Category int
const (
Daily = iota
Monthly
Yearly
Forever
)
// A plugin to track our misdeeds // A plugin to track our misdeeds
type AchievementsPlugin struct { type AchievementsPlugin struct {
bot bot.Bot bot bot.Bot
@ -31,36 +22,42 @@ type AchievementsPlugin struct {
} }
func New(b bot.Bot) *AchievementsPlugin { func New(b bot.Bot) *AchievementsPlugin {
if instance == nil {
ap := &AchievementsPlugin{ ap := &AchievementsPlugin{
bot: b, bot: b,
cfg: b.Config(), cfg: b.Config(),
db: b.DB(), db: b.DB(),
} }
instance = ap err := ap.mkDB()
ap.mkDB() if err != nil {
log.Fatal().Err(err).Msg("unable to create achievements tables")
}
b.Register(ap, bot.Message, ap.message) b.Register(ap, bot.Message, ap.message)
b.Register(ap, bot.Help, ap.help) b.Register(ap, bot.Help, ap.help)
} return ap
return instance
} }
func (p *AchievementsPlugin) mkDB() error { func (p *AchievementsPlugin) mkDB() error {
q := `create table if not exists achievements ( trophiesTable := `create table if not exists trophies (
emojy string primary key,
description string,
creator string
);`
awardsTable := `create table if not exists awards (
id integer primary key, id integer primary key,
achievement string, emojy string references trophies(emojy) on delete restrict on update cascade,
emojy string, holder string,
image_url string, amount integer default 0,
current_holder string, granted timestamp CURRENT_TIMESTAMP
amount integer,
expires integer
);` );`
tx, err := p.db.Beginx() tx, err := p.db.Beginx()
if err != nil { if err != nil {
return err return err
} }
_, err = tx.Exec(q) if _, err = tx.Exec(trophiesTable); err != nil {
if err != nil { return err
}
if _, err = tx.Exec(awardsTable); err != nil {
return err return err
} }
err = tx.Commit() err = tx.Commit()
@ -70,33 +67,171 @@ func (p *AchievementsPlugin) mkDB() error {
return nil return nil
} }
func (p *AchievementsPlugin) GetAwards(nick string) []Award {
var awards []Award
q := `select * from awards inner join trophies on awards.emojy=trophies.emojy where holder=?`
if err := p.db.Select(&awards, q, nick); err != nil {
log.Error().Err(err).Msg("could not select awards")
}
return awards
}
var grantRegex = regexp.MustCompile(`(?i)grant (?P<emojy>(?::[[:word:][:punct:]]+:\s?)+) to :?(?P<nick>[[:word:]]+):?`)
var createRegex = regexp.MustCompile(`(?i)create trophy (?P<emojy>(?::[[:word:][:punct:]]+:\s?)+) (?P<description>.+)`)
var greatRegex = regexp.MustCompile(`(?i)how great (?:am i|is :?(?P<who>[[:word:]]+))[[:punct:]]*`)
func (p *AchievementsPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *AchievementsPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
nick := message.User.Name
if greatRegex.MatchString(message.Body) {
submatches := greatRegex.FindAllStringSubmatch(message.Body, -1)
who := submatches[0][1]
if who == "" {
who = nick
}
awards := p.GetAwards(who)
if len(awards) == 0 {
m := fmt.Sprintf("%s has no achievements to their name. They really suck.", who)
if who == nick {
m = fmt.Sprintf("You have no achievements to your name. "+
"You are a sad and terrible specimen of the human condition, %s.", who)
}
p.bot.Send(c, bot.Message, message.Channel, m)
} else {
m := fmt.Sprintf("Wow, let's all clap for %s. Look at these awards:", who)
for _, a := range awards {
m += fmt.Sprintf("\n%s - %s", a.Emojy, a.Description)
}
p.bot.Send(c, bot.Message, message.Channel, m)
}
return true
}
if message.Command && grantRegex.MatchString(message.Body) {
submatches := grantRegex.FindAllStringSubmatch(message.Body, -1)
emojy := submatches[0][1]
receiver := submatches[0][2]
trophy, err := p.FindTrophy(emojy)
if err != nil {
log.Error().Err(err).Msg("could not find trophy")
msg := fmt.Sprintf("The %s award doesn't exist.", emojy)
p.bot.Send(c, bot.Message, message.Channel, msg)
return true
}
if nick == trophy.Creator {
a, err := p.Grant(receiver, emojy)
if err != nil {
log.Error().Err(err).Msg("could not award trophy")
}
msg := fmt.Sprintf("Congrats %s. You just got the %s award for %s.",
receiver, emojy, a.Description)
p.bot.Send(c, bot.Message, message.Channel, msg)
}
return true
}
if message.Command && createRegex.MatchString(message.Body) {
submatches := createRegex.FindAllStringSubmatch(message.Body, -1)
emojy := submatches[0][1]
description := submatches[0][2]
t, err := p.Create(emojy, description, nick)
if err != nil {
log.Error().Err(err).Msg("could not create trophy")
if strings.Contains(err.Error(), "exists") {
p.bot.Send(c, bot.Message, message.Channel, err.Error())
return true
}
p.bot.Send(c, bot.Message, message.Channel, "I'm too humble to ever award that trophy")
return true
}
resp := fmt.Sprintf("Okay %s. I have crafted a one-of-a-kind %s trophy to give for %s",
nick, t.Emojy, t.Description)
p.bot.Send(c, bot.Message, message.Channel, resp)
return true
}
return false return false
} }
func (p *AchievementsPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *AchievementsPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
ch := message.Channel ch := message.Channel
me := p.bot.WhoAmI() me := p.bot.WhoAmI()
msg := fmt.Sprintf("%s helps those who help themselves.", me) msg := "The achievements plugins awards trophies."
msg += fmt.Sprintf("\nYou can create a trophy with `%s, create trophy <emojy> <description>`", me)
msg += fmt.Sprintf("\nYou can award a trophy with `%s, grant <emojy> to <nick>`", me)
msg += "\nYou can see others awards with `How great is <nick>`"
msg += "\nYou can see your awards with `How great am I?`"
p.bot.Send(c, bot.Message, ch, msg) p.bot.Send(c, bot.Message, ch, msg)
return true return true
} }
// Award is used by other plugins to register a particular award for a user // Award is used by other plugins to register a particular award for a user
func Grant(nick, thing string, category Category) error { func (p *AchievementsPlugin) Grant(nick, emojy string) (Award, error) {
return nil empty := Award{}
q := `insert into awards (emojy,holder) values (?, ?)`
tx, err := p.db.Beginx()
if err != nil {
return empty, err
}
if _, err := tx.Exec(q, emojy, nick); err != nil {
tx.Rollback()
return empty, err
}
if err := tx.Commit(); err != nil {
return empty, err
}
return p.FindAward(emojy)
}
func (p *AchievementsPlugin) Create(emojy, description, creator string) (Trophy, error) {
t, err := p.FindTrophy(emojy)
if err == nil {
return t, fmt.Errorf("the trophy %s already exists", emojy)
}
q := `insert into trophies (emojy,description,creator) values (?,?,?)`
tx, err := p.db.Beginx()
if err != nil {
return Trophy{}, err
}
_, err = tx.Exec(q, emojy, description, creator)
if err != nil {
tx.Rollback()
return Trophy{}, err
}
err = tx.Commit()
return Trophy{
Emojy: emojy,
Description: description,
Creator: creator,
}, err
}
type Trophy struct {
Emojy string
Description string
Creator string
} }
type Award struct { type Award struct {
ID int Trophy
Achievement string ID int64
Emojy string Holder string
ImageURL string
CurrentHolder string
Amount int Amount int
Expires *time.Time Granted *time.Time
} }
func (a *Award) Save() error { func (a *Award) Save() error {
return nil return nil
} }
func (p *AchievementsPlugin) FindTrophy(emojy string) (Trophy, error) {
q := `select * from trophies where emojy=?`
var t Trophy
err := p.db.Get(&t, q, emojy)
return t, err
}
func (p *AchievementsPlugin) FindAward(emojy string) (Award, error) {
q := `select * from awards inner join trophies on awards.emojy=trophies.emojy where trophies.emojy=?`
var a Award
err := p.db.Get(&a, q, emojy)
return a, err
}