all repos — paprika @ ed37b272f5791d139f3031bd35c8b3c41592f356

go rewrite of taigabot

plugins/stocks.go (view raw)

  1package plugins
  2
  3import (
  4	"encoding/json"
  5	"errors"
  6	"fmt"
  7	"net/http"
  8	"strings"
  9	"time"
 10
 11	"git.icyphox.sh/paprika/config"
 12	"github.com/dustin/go-humanize"
 13	"gopkg.in/irc.v3"
 14)
 15
 16func init() {
 17	Register(Stocks{})
 18}
 19
 20type Stocks struct{}
 21
 22var (
 23	stockClient = &http.Client{
 24		Timeout: 10 * time.Second,
 25	}
 26	api_endpoint = "https://cloud.iexapis.com/v1"
 27	NoIEXApi     = errors.New("No IEX API key")
 28)
 29
 30func (Stocks) Triggers() []string {
 31	return []string{".stock", ".stonk"}
 32}
 33
 34type tickerData struct {
 35	Quote struct {
 36		Symbol        string  `json:"symbol"`
 37		Current       float64 `json:"latestPrice"`
 38		High          float64 `json:"high,omitempty"`
 39		Low           float64 `json:"low,omitempty"`
 40		ChangePercent float64 `json:"changePercent"`
 41	} `json:"quote"`
 42	Stats struct {
 43		Company   string  `json:"companyName"`
 44		Change1y  float64 `json:"year1ChangePercent"`
 45		Change6M  float64 `json:"month6ChangePercent"`
 46		Change30d float64 `json:"day30ChangePercent"`
 47		Change5d  float64 `json:"day5ChangePercent"`
 48	} `json:"stats"`
 49}
 50
 51func formatMoneyNum(field string, value float64, percent bool) string {
 52	if percent {
 53		v := humanize.CommafWithDigits(value*100+0.00000000001, 2)
 54		if value < 0 {
 55			return fmt.Sprintf("%s: \x0304%s%%\x03 ", field, v)
 56		} else {
 57			return fmt.Sprintf("%s: \x0303%s%%\x03 ", field, v)
 58		}
 59	} else {
 60		v := humanize.CommafWithDigits(value+0.00000000001, 2)
 61		return fmt.Sprintf("%s: $%s ", field, v)
 62	}
 63}
 64
 65func getStock(symbol, apiKey string) (string, error) {
 66	req, err := http.NewRequest("GET", api_endpoint+"/stock/market/batch", nil)
 67	if err != nil {
 68		return "[Stocks] Request construction error.", err
 69	}
 70	req.Header.Add("User-Agent", "github.com/icyphox/paprika")
 71	q := req.URL.Query()
 72	q.Add("token", apiKey)
 73	q.Add("symbols", symbol)
 74	q.Add("types", "quote,stats")
 75	req.URL.RawQuery = q.Encode()
 76
 77	res, err := stockClient.Do(req)
 78	if err != nil {
 79		return "[Stocks] API Client Error", err
 80	}
 81	defer res.Body.Close()
 82
 83	if res.StatusCode == 404 {
 84		return fmt.Sprintf("[Stocks] Could not get quote for \x02%s\x02", symbol), nil
 85	}
 86
 87	var resData map[string]tickerData
 88	err = json.NewDecoder(res.Body).Decode(&resData)
 89	if err != nil {
 90		return "[Stock] API response malformed", err
 91	}
 92
 93	quote := resData[symbol].Quote
 94	stats := resData[symbol].Stats
 95	var outRes strings.Builder
 96	outRes.WriteString(fmt.Sprintf("\x02%s (%s)\x02 - ", stats.Company, quote.Symbol))
 97	outRes.WriteString(formatMoneyNum("Current", quote.Current, false))
 98	if quote.High != 0.0 {
 99		outRes.WriteString(formatMoneyNum("High", quote.High, false))
100		outRes.WriteString(formatMoneyNum("Low", quote.Low, false))
101	}
102	outRes.WriteString(formatMoneyNum("24h", quote.ChangePercent, true))
103	outRes.WriteString(formatMoneyNum("5d", stats.Change5d, true))
104	outRes.WriteString(formatMoneyNum("6M", stats.Change6M, true))
105	outRes.WriteString(formatMoneyNum("1y", stats.Change1y, true))
106
107	return outRes.String(), nil
108}
109
110func (Stocks) Execute(m *irc.Message) (string, error) {
111	parsed := strings.SplitN(m.Trailing(), " ", 3)
112	if len(parsed) != 2 {
113		return fmt.Sprintf("Usage: %s <Ticker>", parsed[0]), nil
114	}
115	sym := strings.ToUpper(parsed[1])
116
117	if apiKey, ok := config.C.ApiKeys["iex"]; ok {
118		return getStock(sym, apiKey)
119	}
120
121	return "", NoIEXApi
122}