pull/38/head
Miguel Mota 5 years ago
parent e457a89755
commit 0eb3daed6f

2
.gitignore vendored

@ -29,6 +29,8 @@ bfg.jar
build
dist
bin
main
!main.go
# flatpak
.flatpak-builder

@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.4] - 2019-04-21
### Changed
- CoinMarketCap legacy V2 API to Pro V1 API
### Added
- Config option to use CoinMarketCap Pro V1 API KEY
## [1.1.3] - 2019-02-25
### Fixed
- Vendor dependencies
## [1.1.2] - 2018-12-30
### Fixed
- Paginate CoinMarketCap V1 API responses due to their backward-incompatible update

@ -10,6 +10,9 @@ deps:
debug:
DEBUG=1 go run main.go
build:
@go build main.go
# http://macappstore.org/upx
build/mac: clean/mac
env GOARCH=amd64 go build -ldflags "-s -w" -o bin/macos/cointop && upx bin/macos/cointop

@ -354,7 +354,7 @@ You can then configure the actions you want for each key:
```toml
currency = "USD"
defaultView = "default"
defaultView = ""
[shortcuts]
"$" = "last_page"
@ -422,6 +422,13 @@ defaultView = "default"
t = "sort_column_total_supply"
u = "sort_column_last_updated"
v = "sort_column_24h_volume"
[favorites]
[portfolio]
[coinmarketcap]
pro_api_key = ""
```
You may specify a different config file to use by using the `-config` flag:
@ -519,7 +526,14 @@ Frequently asked questions:
- Q: How do I add my CoinMarketCap Pro API Key?
- A: Export the environment variable `CMC_PRO_API_KEY` containing the API key.
- A: Add the API key in the cointop config file:
```toml
[coinmarketcap]
pro_api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
```
Alternatively, you can export the environment variable `CMC_PRO_API_KEY` containing the API key.
- Q: I installed cointop without errors but the command is not found.

@ -3,7 +3,6 @@ package cointop
import (
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"sync"
@ -16,6 +15,7 @@ import (
"github.com/miguelmota/cointop/cointop/common/filecache"
"github.com/miguelmota/cointop/cointop/common/table"
"github.com/patrickmn/go-cache"
log "github.com/sirupsen/logrus"
)
// TODO: clean up and optimize codebase
@ -82,6 +82,8 @@ type Cointop struct {
inputview *gocui.View
inputviewname string
defaultView string
apiKeys *apiKeys
limiter <-chan time.Time
}
// PortfolioEntry is portfolio entry
@ -100,6 +102,11 @@ type Config struct {
ConfigFilepath string
}
// apiKeys is api keys structure
type apiKeys struct {
cmc string
}
var defaultConfigPath = "~/.cointop/config"
// NewCointop initializes cointop
@ -117,18 +124,19 @@ func NewCointop(config *Config) *Cointop {
}
ct := &Cointop{
api: api.NewCMC(),
refreshticker: time.NewTicker(1 * time.Minute),
sortby: "rank",
page: 0,
perpage: 100,
forcerefresh: make(chan bool),
maxtablewidth: 175,
actionsmap: actionsMap(),
shortcutkeys: defaultShortcuts(),
allcoinsslugmap: make(map[string]*coin),
allcoins: []*coin{},
refreshticker: time.NewTicker(1 * time.Minute),
sortby: "rank",
page: 0,
perpage: 100,
forcerefresh: make(chan bool),
maxtablewidth: 175,
actionsmap: actionsMap(),
shortcutkeys: defaultShortcuts(),
// DEPRECATED: favorites by 'symbol' is deprecated because of collisions. Kept for backward compatibility.
favoritesbysymbol: map[string]bool{},
favorites: map[string]bool{},
favoritesbysymbol: make(map[string]bool),
favorites: make(map[string]bool),
cache: cache.New(1*time.Minute, 2*time.Minute),
debug: debug,
configFilepath: configFilepath,
@ -189,21 +197,36 @@ func NewCointop(config *Config) *Cointop {
},
portfolioupdatemenuviewname: "portfolioupdatemenu",
inputviewname: "input",
apiKeys: new(apiKeys),
limiter: time.Tick(2 * time.Second),
}
err := ct.setupConfig()
if err != nil {
log.Fatal(err)
}
allcoinsslugmap := map[string]types.Coin{}
ct.api = api.NewCMC(ct.apiKeys.cmc)
coinscachekey := "allcoinsslugmap"
filecache.Get(coinscachekey, &allcoinsslugmap)
ct.cache.Set(coinscachekey, allcoinsslugmap, 10*time.Second)
filecache.Get(coinscachekey, &ct.allcoinsslugmap)
for k := range ct.allcoinsslugmap {
ct.allcoins = append(ct.allcoins, ct.allcoinsslugmap[k])
}
if len(ct.allcoins) > 1 {
max := len(ct.allcoins)
if max > 100 {
max = 100
}
ct.sort(ct.sortby, ct.sortdesc, ct.allcoins, false)
ct.coins = ct.allcoins[0:max]
}
// DEPRECATED: favorites by 'symbol' is deprecated because of collisions. Kept for backward compatibility.
// Here we're doing a lookup based on symbol and setting the favorite to the coin name instead of coin symbol.
for i := range allcoinsslugmap {
coin := allcoinsslugmap[i]
for i := range ct.allcoinsslugmap {
coin := ct.allcoinsslugmap[i]
for k := range ct.favoritesbysymbol {
if coin.Symbol == k {
ct.favorites[coin.Name] = true

@ -5,8 +5,8 @@ import (
)
// NewCMC new CoinMarketCap API
func NewCMC() Interface {
return cmc.New()
func NewCMC(apiKey string) Interface {
return cmc.New(apiKey)
}
// NewCC new CryptoCompare API

@ -1,12 +1,10 @@
package coinmarketcap
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
"sync"
"time"
apitypes "github.com/miguelmota/cointop/cointop/common/api/types"
@ -20,9 +18,12 @@ type Service struct {
}
// New new service
func New() *Service {
func New(apiKey string) *Service {
if apiKey == "" {
apiKey = os.Getenv("CMC_PRO_API_KEY")
}
client := cmc.NewClient(&cmc.Config{
ProAPIKey: os.Getenv("CMC_PRO_API_KEY"),
ProAPIKey: apiKey,
})
return &Service{
client: client,
@ -31,15 +32,18 @@ func New() *Service {
// Ping ping API
func (s *Service) Ping() error {
info, err := s.client.Cryptocurrency.Info(&cmc.InfoOptions{
Symbol: "BTC",
})
if err != nil {
return errors.New("failed to ping")
}
if info == nil {
return errors.New("failed to ping")
}
// TODO: notify in statusbar of failed ping (instead of fatal to make it work offline)
/*
info, err := s.client.Cryptocurrency.Info(&cmc.InfoOptions{
Symbol: "BTC",
})
if err != nil {
return errors.New("failed to ping")
}
if info == nil {
return errors.New("failed to ping")
}
*/
return nil
}
@ -81,33 +85,26 @@ func (s *Service) getLimitedCoinData(convert string, offset int) (map[string]api
}
// GetAllCoinData gets all coin data. Need to paginate through all pages
func (s *Service) GetAllCoinData(convert string) (chan map[string]apitypes.Coin, error) {
var wg sync.WaitGroup
ch := make(chan map[string]apitypes.Coin)
func (s *Service) GetAllCoinData(convert string, ch chan map[string]apitypes.Coin) error {
go func() {
var mutex sync.Mutex
maxPages := 15
maxPages := 10
defer close(ch)
for i := 0; i < maxPages; i++ {
time.Sleep(time.Duration(i) * time.Second)
wg.Add(1)
go func(j int) {
defer wg.Done()
coins, err := s.getLimitedCoinData(convert, j)
if err != nil {
return
}
mutex.Lock()
defer mutex.Unlock()
ret := make(map[string]apitypes.Coin)
for k, v := range coins {
ret[k] = v
}
ch <- ret
}(i)
if i > 0 {
time.Sleep(1 * time.Second)
}
coins, err := s.getLimitedCoinData(convert, i)
if err != nil {
return
}
ret := make(map[string]apitypes.Coin)
for k, v := range coins {
ret[k] = v
}
ch <- ret
}
wg.Wait()
}()
return ch, nil
return nil
}
// GetCoinGraphData gets coin graph data

@ -7,7 +7,7 @@ import (
// Interface interface
type Interface interface {
Ping() error
GetAllCoinData(convert string) (chan map[string]types.Coin, error)
GetAllCoinData(convert string, ch chan map[string]types.Coin) error
GetCoinGraphData(coin string, start int64, end int64) (types.CoinGraph, error)
GetGlobalMarketGraphData(start int64, end int64) (types.MarketGraph, error)
GetGlobalMarketData(convert string) (types.GlobalMarketData, error)

@ -12,11 +12,12 @@ import (
var fileperm = os.FileMode(0644)
type config struct {
Shortcuts map[string]interface{} `toml:"shortcuts"`
Favorites map[string][]interface{} `toml:"favorites"`
Portfolio map[string]interface{} `toml:"portfolio"`
Currency interface{} `toml:"currency"`
DefaultView interface{} `toml:"defaultView"`
Shortcuts map[string]interface{} `toml:"shortcuts"`
Favorites map[string][]interface{} `toml:"favorites"`
Portfolio map[string]interface{} `toml:"portfolio"`
Currency interface{} `toml:"currency"`
DefaultView interface{} `toml:"defaultView"`
CoinMarketCap map[string]interface{} `toml:"coinmarketcap"`
}
func (ct *Cointop) setupConfig() error {
@ -48,6 +49,10 @@ func (ct *Cointop) setupConfig() error {
if err != nil {
return err
}
err = ct.loadAPIKeysFromConfig()
if err != nil {
return err
}
return nil
}
@ -162,13 +167,17 @@ func (ct *Cointop) configToToml() ([]byte, error) {
var currencyIfc interface{} = ct.currencyconversion
var defaultViewIfc interface{} = ct.defaultView
cmcIfc := map[string]interface{}{
"pro_api_key": ct.apiKeys.cmc,
}
var inputs = &config{
Shortcuts: shortcutsIfcs,
Favorites: favoritesIfcs,
Portfolio: portfolioIfc,
Currency: currencyIfc,
DefaultView: defaultViewIfc,
Shortcuts: shortcutsIfcs,
Favorites: favoritesIfcs,
Portfolio: portfolioIfc,
Currency: currencyIfc,
DefaultView: defaultViewIfc,
CoinMarketCap: cmcIfc,
}
var b bytes.Buffer
@ -223,6 +232,16 @@ func (ct *Cointop) loadDefaultViewFromConfig() error {
return nil
}
func (ct *Cointop) loadAPIKeysFromConfig() error {
for key, value := range ct.config.CoinMarketCap {
k := strings.TrimSpace(strings.ToLower(key))
if k == "pro_api_key" {
ct.apiKeys.cmc = value.(string)
}
}
return nil
}
func (ct *Cointop) loadFavoritesFromConfig() error {
for k, arr := range ct.config.Favorites {
// DEPRECATED: favorites by 'symbol' is deprecated because of collisions. Kept for backward compatibility.

@ -1,6 +1,8 @@
package cointop
import "log"
import (
log "github.com/sirupsen/logrus"
)
func (ct *Cointop) debuglog(s string) {
if ct.debug {

@ -14,13 +14,13 @@ func (ct *Cointop) toggleFavorite() error {
ct.favorites[coin.Name] = true
coin.Favorite = true
}
ct.updateTable()
go ct.updateTable()
return nil
}
func (ct *Cointop) toggleShowFavorites() error {
ct.portfoliovisible = false
ct.filterByFavorites = !ct.filterByFavorites
ct.updateTable()
go ct.updateTable()
return nil
}

@ -71,15 +71,13 @@ func (ct *Cointop) layout(g *gocui.Gui) error {
ct.tableview.Highlight = true
ct.tableview.SelBgColor = gocui.ColorCyan
ct.tableview.SelFgColor = gocui.ColorBlack
_, found := ct.cache.Get("allcoinsslugmap")
if found {
ct.cache.Delete("allcoinsslugmap")
}
go func() {
ct.updateCoins()
ct.updateTable()
_, found := ct.cache.Get("allcoinsslugmap")
if found {
ct.cache.Delete("allcoinsslugmap")
ct.updateCoins()
ct.updateTable()
}
}()
}

@ -2,17 +2,21 @@ package cointop
import (
"sync"
"time"
types "github.com/miguelmota/cointop/cointop/common/api/types"
"github.com/miguelmota/cointop/cointop/common/filecache"
)
var coinslock sync.Mutex
var updatecoinsmux sync.Mutex
func (ct *Cointop) updateCoins() error {
coinslock.Lock()
defer coinslock.Unlock()
cachekey := "allcoinsslugmap"
var err error
var allcoinsslugmap map[string]types.Coin
cached, found := ct.cache.Get(cachekey)
if found {
@ -24,37 +28,31 @@ func (ct *Cointop) updateCoins() error {
// cache miss
if allcoinsslugmap == nil {
ct.debuglog("cache miss")
ch, err := ct.api.GetAllCoinData(ct.currencyconversion)
ch := make(chan map[string]types.Coin)
err = ct.api.GetAllCoinData(ct.currencyconversion, ch)
if err != nil {
return err
}
for {
coins, ok := <-ch
if !ok {
break
for coins := range ch {
allcoinsslugmap := make(map[string]types.Coin)
for _, c := range coins {
allcoinsslugmap[c.Name] = c
}
ct.updateCoinsMap(coins, true)
ct.updateTable()
go ct.processCoinsMap(allcoinsslugmap)
ct.cache.Set(cachekey, ct.allcoinsslugmap, 10*time.Second)
filecache.Set(cachekey, ct.allcoinsslugmap, 24*time.Hour)
}
/*
ct.cache.Set(cachekey, allcoinsslugmap, 10*time.Second)
go func() {
filecache.Set(cachekey, allcoinsslugmap, 24*time.Hour)
}()
*/
} else {
ct.updateCoinsMap(allcoinsslugmap, false)
ct.processCoinsMap(allcoinsslugmap)
}
return nil
}
func (ct *Cointop) updateCoinsMap(allcoinsslugmap map[string]types.Coin, b bool) {
if len(ct.allcoinsslugmap) == 0 {
ct.allcoinsslugmap = map[string]*coin{}
}
func (ct *Cointop) processCoinsMap(allcoinsslugmap map[string]types.Coin) {
updatecoinsmux.Lock()
defer updatecoinsmux.Unlock()
for k, v := range allcoinsslugmap {
last := ct.allcoinsslugmap[k]
ct.allcoinsslugmap[k] = &coin{
@ -75,17 +73,15 @@ func (ct *Cointop) updateCoinsMap(allcoinsslugmap map[string]types.Coin, b bool)
if last != nil {
ct.allcoinsslugmap[k].Favorite = last.Favorite
}
if b {
ct.allcoins = append(ct.allcoins, ct.allcoinsslugmap[k])
}
}
//if len(ct.allcoins) == 0 {
if b {
//ct.sort(ct.sortby, ct.sortdesc, ct.allcoins)
}
if !b {
if len(ct.allcoins) < len(ct.allcoinsslugmap) {
list := []*coin{}
for k := range allcoinsslugmap {
coin := ct.allcoinsslugmap[k]
list = append(list, coin)
}
ct.allcoins = append(ct.allcoins, list...)
} else {
// update list in place without changing order
for i := range ct.allcoinsslugmap {
cm := ct.allcoinsslugmap[i]
@ -111,4 +107,10 @@ func (ct *Cointop) updateCoinsMap(allcoinsslugmap map[string]types.Coin, b bool)
}
}
}
ct.updateTable()
}
func (ct *Cointop) getListCount() int {
return len(ct.allCoins())
}

@ -78,7 +78,7 @@ func (ct *Cointop) updateMarketbar() error {
} else {
market, err = ct.api.GetGlobalMarketData(ct.currencyconversion)
if err != nil {
return err
filecache.Get(cachekey, &market)
}
ct.cache.Set(cachekey, market, 10*time.Second)

@ -9,19 +9,19 @@ func (ct *Cointop) currentDisplayPage() int {
}
func (ct *Cointop) totalPages() int {
return (ct.getListCount() / ct.perpage) + 1
return ct.getListCount() / ct.perpage
}
func (ct *Cointop) totalPerPage() int {
return ct.perpage
func (ct *Cointop) totalPagesDisplay() int {
return ct.totalPages() + 1
}
func (ct *Cointop) getListCount() int {
return len(ct.allCoins())
func (ct *Cointop) totalPerPage() int {
return ct.perpage
}
func (ct *Cointop) setPage(page int) int {
if (page*ct.perpage) <= ct.getListCount() && page >= 0 {
if (page*ct.perpage) < ct.getListCount() && page >= 0 {
ct.page = page
}
return ct.page

@ -14,14 +14,14 @@ import (
func (ct *Cointop) togglePortfolio() error {
ct.filterByFavorites = false
ct.portfoliovisible = !ct.portfoliovisible
ct.updateTable()
go ct.updateTable()
return nil
}
func (ct *Cointop) toggleShowPortfolio() error {
ct.filterByFavorites = false
ct.portfoliovisible = true
ct.updateTable()
go ct.updateTable()
return nil
}

@ -6,7 +6,10 @@ import (
)
func (ct *Cointop) refresh() error {
ct.forcerefresh <- true
go func() {
<-ct.limiter
ct.forcerefresh <- true
}()
return nil
}
@ -16,8 +19,10 @@ func (ct *Cointop) refreshAll() error {
ct.setRefreshStatus()
ct.cache.Delete("allcoinsslugmap")
ct.cache.Delete("market")
ct.updateCoins()
ct.updateTable()
go func() {
ct.updateCoins()
ct.updateTable()
}()
go ct.updateChart()
return nil
}

@ -2,11 +2,22 @@ package cointop
import (
"sort"
"sync"
"github.com/jroimartin/gocui"
)
func (ct *Cointop) sort(sortby string, desc bool, list []*coin) {
var sortlock sync.Mutex
func (ct *Cointop) sort(sortby string, desc bool, list []*coin, renderHeaders bool) {
sortlock.Lock()
defer sortlock.Unlock()
if list == nil {
return
}
if len(list) < 2 {
return
}
ct.sortby = sortby
ct.sortdesc = desc
sort.Slice(list[:], func(i, j int) bool {
@ -54,7 +65,10 @@ func (ct *Cointop) sort(sortby string, desc bool, list []*coin) {
return a.Rank < b.Rank
}
})
ct.updateHeaders()
if renderHeaders {
ct.updateHeaders()
}
}
func (ct *Cointop) sortToggle(sortby string, desc bool) error {
@ -62,7 +76,7 @@ func (ct *Cointop) sortToggle(sortby string, desc bool) error {
desc = !ct.sortdesc
}
ct.sort(sortby, desc, ct.coins)
ct.sort(sortby, desc, ct.coins, true)
ct.updateTable()
return nil
}
@ -84,14 +98,12 @@ func (ct *Cointop) getSortColIndex() int {
func (ct *Cointop) sortAsc() error {
ct.sortdesc = false
ct.sort(ct.sortby, ct.sortdesc, ct.coins)
ct.updateTable()
return nil
}
func (ct *Cointop) sortDesc() error {
ct.sortdesc = true
ct.sort(ct.sortby, ct.sortdesc, ct.coins)
ct.updateTable()
return nil
}
@ -104,7 +116,7 @@ func (ct *Cointop) sortPrevCol() error {
k = 0
}
nextsortby = ct.tablecolumnorder[k]
ct.sort(nextsortby, ct.sortdesc, ct.coins)
ct.sort(nextsortby, ct.sortdesc, ct.coins, true)
ct.updateTable()
return nil
}
@ -118,7 +130,7 @@ func (ct *Cointop) sortNextCol() error {
k = l - 1
}
nextsortby = ct.tablecolumnorder[k]
ct.sort(nextsortby, ct.sortdesc, ct.coins)
ct.sort(nextsortby, ct.sortdesc, ct.coins, true)
ct.updateTable()
return nil
}

@ -8,7 +8,7 @@ import (
func (ct *Cointop) updateStatusbar(s string) {
currpage := ct.currentDisplayPage()
totalpages := ct.totalPages()
totalpages := ct.totalPagesDisplay()
var quitText string
var favoritesText string
var portfolioText string

@ -136,7 +136,7 @@ func (ct *Cointop) refreshTable() error {
namecolor(pad.Right(fmt.Sprintf("%.22s", name), 21, " ")),
color.White(pad.Right(fmt.Sprintf("%.6s", coin.Symbol), symbolpadding, " ")),
colorprice(fmt.Sprintf("%12s", humanize.Commaf(coin.Price))),
color.White(fmt.Sprintf("%17s", humanize.Commaf(coin.MarketCap))),
color.White(fmt.Sprintf("%18s", humanize.Commaf(coin.MarketCap))),
color.White(fmt.Sprintf("%15s", humanize.Commaf(coin.Volume24H))),
color1h(fmt.Sprintf("%8.2f%%", coin.PercentChange1H)),
color24h(fmt.Sprintf("%8.2f%%", coin.PercentChange24H)),
@ -158,7 +158,9 @@ func (ct *Cointop) refreshTable() error {
ct.update(func() {
ct.tableview.Clear()
ct.table.Format().Fprint(ct.tableview)
ct.rowChanged()
go ct.rowChanged()
go ct.updateHeaders()
go ct.updateMarketbar()
go ct.updateChart()
})
@ -183,16 +185,14 @@ func (ct *Cointop) updateTable() error {
}
}
ct.coins = sliced
ct.sort(ct.sortby, ct.sortdesc, ct.coins)
ct.refreshTable()
go ct.refreshTable()
return nil
}
if ct.portfoliovisible {
sliced = ct.getPortfolioSlice()
ct.coins = sliced
ct.sort(ct.sortby, ct.sortdesc, ct.coins)
ct.refreshTable()
go ct.refreshTable()
return nil
}
@ -223,8 +223,9 @@ func (ct *Cointop) updateTable() error {
sliced = allcoins[start:end]
}
ct.coins = sliced
ct.sort(ct.sortby, ct.sortdesc, ct.coins)
ct.refreshTable()
ct.sort(ct.sortby, ct.sortdesc, ct.coins, true)
go ct.refreshTable()
return nil
}

@ -2,10 +2,15 @@ package cointop
import (
"github.com/jroimartin/gocui"
log "github.com/sirupsen/logrus"
)
// update update view
func (ct *Cointop) update(f func()) {
if ct.g == nil {
log.Fatal("gocui is not initialized")
}
ct.g.Update(func(g *gocui.Gui) error {
f()
return nil

@ -1,7 +1,7 @@
package cointop
// TODO: make dynamic based on git tag
const version = "1.1.3"
const version = "1.1.4"
func (ct *Cointop) version() string {
return version

@ -3,6 +3,7 @@ module github.com/miguelmota/cointop
require (
github.com/BurntSushi/toml v0.3.1
github.com/anaskhan96/soup v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.1
github.com/fatih/color v1.7.0
github.com/gizak/termui v2.3.0+incompatible
github.com/jroimartin/gocui v0.4.0
@ -15,6 +16,7 @@ require (
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/sirupsen/logrus v1.4.1
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a // indirect
golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2 // indirect
golang.org/x/sys v0.0.0-20190416152802-12500544f89f // indirect

@ -11,6 +11,7 @@ github.com/gizak/termui v2.3.0+incompatible h1:S8wJoNumYfc/rR5UezUM4HsPEo3RJh0LK
github.com/gizak/termui v2.3.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA=
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/maruel/panicparse v1.1.1 h1:k62YPcEoLncEEpjMt92GtG5ugb8WL/510Ys3/h5IkRc=
github.com/maruel/panicparse v1.1.1/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI=
github.com/maruel/panicparse v1.1.2-0.20180806203336-f20d4c4d746f h1:mtX2D0ta3lWxCvv276VVIH6mMYzm4jhSfYP70ZJSOQU=
@ -36,6 +37,11 @@ github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyh
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/net v0.0.0-20180215212450-dc948dff8834/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -43,6 +49,7 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2 h1:iC0Y6EDq+rhnAePxGvJs2kzUAYcwESqdcGRPzEUfzTU=
golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=

Loading…
Cancel
Save