mirror of https://github.com/velour/catbase.git
bid: connect scores/bids/etc to the chat
This commit is contained in:
parent
dd0f9efeae
commit
286582417b
2
main.go
2
main.go
|
@ -5,6 +5,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/velour/catbase/plugins/cli"
|
"github.com/velour/catbase/plugins/cli"
|
||||||
|
"github.com/velour/catbase/plugins/newsbid"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -124,6 +125,7 @@ func main() {
|
||||||
b.AddPlugin(nerdepedia.New(b))
|
b.AddPlugin(nerdepedia.New(b))
|
||||||
b.AddPlugin(tldr.New(b))
|
b.AddPlugin(tldr.New(b))
|
||||||
b.AddPlugin(stock.New(b))
|
b.AddPlugin(stock.New(b))
|
||||||
|
b.AddPlugin(newsbid.New(b))
|
||||||
b.AddPlugin(cli.New(b))
|
b.AddPlugin(cli.New(b))
|
||||||
// catches anything left, will always return true
|
// catches anything left, will always return true
|
||||||
b.AddPlugin(fact.New(b))
|
b.AddPlugin(fact.New(b))
|
||||||
|
|
|
@ -1,25 +1,109 @@
|
||||||
package newsbid
|
package newsbid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"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/plugins/newsbid/webshit"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NewsBid struct {
|
type NewsBid struct {
|
||||||
bot bot.Bot
|
bot bot.Bot
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
|
ws *webshit.Webshit
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(b bot.Bot) *NewsBid {
|
func New(b bot.Bot) *NewsBid {
|
||||||
|
ws := webshit.New(b.DB())
|
||||||
p := &NewsBid{
|
p := &NewsBid{
|
||||||
bot: b,
|
bot: b,
|
||||||
db: b.DB(),
|
db: b.DB(),
|
||||||
|
ws: ws,
|
||||||
}
|
}
|
||||||
p.bot.Register(p, bot.Message, p.message)
|
p.bot.Register(p, bot.Message, p.message)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *NewsBid) message(conn bot.Connector, k bot.Kind, message msg.Message, args ...interface{}) bool {
|
func (p *NewsBid) message(conn bot.Connector, k bot.Kind, message msg.Message, args ...interface{}) bool {
|
||||||
|
body := strings.ToLower(message.Body)
|
||||||
|
ch := message.Channel
|
||||||
|
if message.Command && body == "balance" {
|
||||||
|
bal := p.ws.GetBalance(message.User.Name)
|
||||||
|
p.bot.Send(conn, bot.Message, ch, fmt.Sprintf("%s, your current balance is %d.",
|
||||||
|
message.User.Name, bal))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if message.Command && body == "bids" {
|
||||||
|
bids, err := p.ws.GetAllBids()
|
||||||
|
if err != nil {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, fmt.Sprintf("Error getting bids: %s", err))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(bids) == 0 {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, "No bids to report.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
sort.Slice(bids, func(i, j int) bool { return bids[i].User < bids[j].User })
|
||||||
|
out := "Bids:\n"
|
||||||
|
for _, b := range bids {
|
||||||
|
out += fmt.Sprintf("%s bid %d on %s\n", b.User, b.Bid, b.Title)
|
||||||
|
}
|
||||||
|
p.bot.Send(conn, bot.Message, ch, out)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if message.Command && body == "scores" {
|
||||||
|
bals, err := p.ws.GetAllBalances()
|
||||||
|
if err != nil {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, fmt.Sprintf("Error getting bids: %s", err))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(bals) == 0 {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, "No balances to report.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
out := "NGate balances:\n"
|
||||||
|
for _, b := range bals {
|
||||||
|
out += fmt.Sprintf("%s has a total score of %d with %d left to bid this session\n", b.User, b.Score, b.Balance)
|
||||||
|
}
|
||||||
|
p.bot.Send(conn, bot.Message, ch, out)
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
if message.Command && strings.HasPrefix(body, "bid") {
|
||||||
|
parts := strings.Fields(body)
|
||||||
|
if len(parts) != 3 {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, "You must bid with an amount and a URL.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
amount, _ := strconv.Atoi(parts[1])
|
||||||
|
url := parts[2]
|
||||||
|
if err := p.ws.Bid(message.User.Name, amount, url); err != nil {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, fmt.Sprintf("Error placing bid: %s", err))
|
||||||
|
} else {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, "Your bid has been placed.")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if message.Command && body == "check ngate" {
|
||||||
|
p.check(conn, ch)
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *NewsBid) check(conn bot.Connector, ch string) {
|
||||||
|
wr, err := p.ws.Check()
|
||||||
|
if err != nil {
|
||||||
|
p.bot.Send(conn, bot.Message, ch, fmt.Sprintf("Error checking ngate: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, res := range wr {
|
||||||
|
msg := fmt.Sprintf("%s: won %d and lost %d for a score of %d",
|
||||||
|
res.User, res.Won, res.Lost, res.Score)
|
||||||
|
p.bot.Send(conn, bot.Message, ch, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -29,12 +29,24 @@ type Bid struct {
|
||||||
Title string
|
Title string
|
||||||
URL string
|
URL string
|
||||||
Bid int
|
Bid int
|
||||||
|
Placed int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b Bid) PlacedParsed() time.Time {
|
||||||
|
return time.Unix(b.Placed, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Balance struct {
|
||||||
|
User string
|
||||||
|
Balance int
|
||||||
|
Score int
|
||||||
}
|
}
|
||||||
|
|
||||||
type WeeklyResult struct {
|
type WeeklyResult struct {
|
||||||
User string
|
User string
|
||||||
Won int
|
Won int
|
||||||
Lost int
|
Lost int
|
||||||
|
Score int
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(db *sqlx.DB) *Webshit {
|
func New(db *sqlx.DB) *Webshit {
|
||||||
|
@ -45,40 +57,36 @@ func New(db *sqlx.DB) *Webshit {
|
||||||
|
|
||||||
// setup will create any necessary SQL tables and populate them with minimal data
|
// setup will create any necessary SQL tables and populate them with minimal data
|
||||||
func (w *Webshit) setup() {
|
func (w *Webshit) setup() {
|
||||||
if _, err := w.db.Exec(`create table if not exists webshit_bids (
|
w.db.MustExec(`create table if not exists webshit_bids (
|
||||||
id integer primary key,
|
id integer primary key autoincrement,
|
||||||
user string,
|
user string,
|
||||||
title string,
|
title string,
|
||||||
url string,
|
url string,
|
||||||
bid integer,
|
bid integer,
|
||||||
created integer
|
placed integer
|
||||||
)`); err != nil {
|
)`)
|
||||||
log.Fatal().Err(err)
|
w.db.MustExec(`create table if not exists webshit_balances (
|
||||||
}
|
|
||||||
if _, err := w.db.Exec(`create table if not exists webshit_balances (
|
|
||||||
user string primary key,
|
user string primary key,
|
||||||
balance int,
|
balance int,
|
||||||
score int
|
score int
|
||||||
)`); err != nil {
|
)`)
|
||||||
log.Fatal().Err(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Webshit) Check() (map[string]WeeklyResult, error) {
|
func (w *Webshit) Check() ([]WeeklyResult, error) {
|
||||||
stories, published, err := w.GetWeekly()
|
stories, published, err := w.GetWeekly()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var bids []Bid
|
var bids []Bid
|
||||||
if err = w.db.Get(&bids, `select * from webshit_bids where created < ?`,
|
if err = w.db.Select(&bids, `select user,title,url,bid from webshit_bids where placed < ?`,
|
||||||
published.Unix()); err != nil {
|
published.Unix()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assuming no bids earlier than the weekly means there hasn't been a new weekly
|
// Assuming no bids earlier than the weekly means there hasn't been a new weekly
|
||||||
if len(bids) == 0 {
|
if len(bids) == 0 {
|
||||||
return nil, nil
|
return nil, fmt.Errorf("there are no bids against the current ngate post")
|
||||||
}
|
}
|
||||||
|
|
||||||
storyMap := map[string]Story{}
|
storyMap := map[string]Story{}
|
||||||
|
@ -94,7 +102,7 @@ func (w *Webshit) Check() (map[string]WeeklyResult, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all those bids
|
// Delete all those bids
|
||||||
if _, err = w.db.Exec(`delete from webshit_bids where created < ?`,
|
if _, err = w.db.Exec(`delete from webshit_bids where placed < ?`,
|
||||||
published.Unix()); err != nil {
|
published.Unix()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -107,10 +115,11 @@ func (w *Webshit) Check() (map[string]WeeklyResult, error) {
|
||||||
return wr, nil
|
return wr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Webshit) checkBids(bids []Bid, storyMap map[string]Story) map[string]WeeklyResult {
|
func (w *Webshit) checkBids(bids []Bid, storyMap map[string]Story) []WeeklyResult {
|
||||||
wr := map[string]WeeklyResult{}
|
wr := map[string]WeeklyResult{}
|
||||||
for _, b := range bids {
|
for _, b := range bids {
|
||||||
win, loss := 0, 0
|
win, loss := 0, 0
|
||||||
|
score := w.GetScore(b.User)
|
||||||
if s, ok := storyMap[b.Title]; ok {
|
if s, ok := storyMap[b.Title]; ok {
|
||||||
log.Info().Interface("story", s).Msg("won bid")
|
log.Info().Interface("story", s).Msg("won bid")
|
||||||
win = b.Bid
|
win = b.Bid
|
||||||
|
@ -123,14 +132,16 @@ func (w *Webshit) checkBids(bids []Bid, storyMap map[string]Story) map[string]We
|
||||||
User: b.User,
|
User: b.User,
|
||||||
Won: win,
|
Won: win,
|
||||||
Lost: loss,
|
Lost: loss,
|
||||||
|
Score: score + win - loss,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res.Won = win
|
res.Won += win
|
||||||
res.Lost = loss
|
res.Lost += loss
|
||||||
|
res.Score += win - loss
|
||||||
wr[b.User] = res
|
wr[b.User] = res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wr
|
return wrMapToSlice(wr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeadlines will return the current possible news headlines for bidding
|
// GetHeadlines will return the current possible news headlines for bidding
|
||||||
|
@ -165,7 +176,7 @@ func (w *Webshit) GetWeekly() ([]Story, *time.Time, error) {
|
||||||
return nil, nil, fmt.Errorf("no webshit weekly found")
|
return nil, nil, fmt.Errorf("no webshit weekly found")
|
||||||
}
|
}
|
||||||
|
|
||||||
published := feed.PublishedParsed
|
published := feed.Items[0].PublishedParsed
|
||||||
|
|
||||||
buf := bytes.NewBufferString(feed.Items[0].Description)
|
buf := bytes.NewBufferString(feed.Items[0].Description)
|
||||||
doc, err := goquery.NewDocumentFromReader(buf)
|
doc, err := goquery.NewDocumentFromReader(buf)
|
||||||
|
@ -197,6 +208,34 @@ func (w *Webshit) GetBalance(user string) int {
|
||||||
return balance
|
return balance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Webshit) GetScore(user string) int {
|
||||||
|
q := `select score from webshit_balances where user=?`
|
||||||
|
var score int
|
||||||
|
err := w.db.Get(&score, q, user)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Webshit) GetAllBids() ([]Bid, error) {
|
||||||
|
var bids []Bid
|
||||||
|
err := w.db.Select(&bids, `select * from webshit_bids`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Webshit) GetAllBalances() ([]Balance, error) {
|
||||||
|
var balances []Balance
|
||||||
|
err := w.db.Select(&balances, `select * from webshit_balances`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return balances, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Bid allows a user to place a bid on a particular story
|
// Bid allows a user to place a bid on a particular story
|
||||||
func (w *Webshit) Bid(user string, amount int, URL string) error {
|
func (w *Webshit) Bid(user string, amount int, URL string) error {
|
||||||
bal := w.GetBalance(user)
|
bal := w.GetBalance(user)
|
||||||
|
@ -209,14 +248,15 @@ func (w *Webshit) Bid(user string, amount int, URL string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := w.db.MustBegin()
|
tx := w.db.MustBegin()
|
||||||
_, err = tx.Exec(`insert into webshit_bids (user,title,url,bid,created) values (?,?,?,?,?)`,
|
_, err = tx.Exec(`insert into webshit_bids (user,title,url,bid,placed) values (?,?,?,?,?)`,
|
||||||
user, story.Title, story.URL, amount, time.Now().Unix())
|
user, story.Title, story.URL, amount, time.Now().Unix())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = tx.Exec(`update webshit_balances set balance=? where user=?`,
|
q := `insert into webshit_balances (user,balance,score) values (?,?,0)
|
||||||
bal-amount, user)
|
on conflict(user) do update set balance=?`
|
||||||
|
_, err = tx.Exec(q, user, bal-amount, bal-amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
|
@ -259,11 +299,11 @@ func (w *Webshit) getStoryByURL(URL string) (Story, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Webshit) updateScores(results map[string]WeeklyResult) error {
|
func (w *Webshit) updateScores(results []WeeklyResult) error {
|
||||||
tx := w.db.MustBegin()
|
tx := w.db.MustBegin()
|
||||||
for _, res := range results {
|
for _, res := range results {
|
||||||
if _, err := tx.Exec(`update webshit_balances set score=score+? where user=?`,
|
if _, err := tx.Exec(`update webshit_balances set score=? where user=?`,
|
||||||
res.Won-res.Lost, res.User); err != nil {
|
res.Score, res.User); err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -271,3 +311,11 @@ func (w *Webshit) updateScores(results map[string]WeeklyResult) error {
|
||||||
err := tx.Commit()
|
err := tx.Commit()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrMapToSlice(wr map[string]WeeklyResult) []WeeklyResult {
|
||||||
|
var out = []WeeklyResult{}
|
||||||
|
for _, r := range wr {
|
||||||
|
out = append(out, r)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@ func make(t *testing.T) *Webshit {
|
||||||
|
|
||||||
func TestWebshit_GetWeekly(t *testing.T) {
|
func TestWebshit_GetWeekly(t *testing.T) {
|
||||||
w := make(t)
|
w := make(t)
|
||||||
weekly, _, err := w.GetWeekly()
|
weekly, pub, err := w.GetWeekly()
|
||||||
|
t.Logf("Pub: %v", pub)
|
||||||
|
assert.NotNil(t, pub)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotEmpty(t, weekly)
|
assert.NotEmpty(t, weekly)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue