santa/utils/websocket.go

153 lines
3.5 KiB
Go

package utils
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"github.com/gorilla/websocket"
amino "github.com/tendermint/go-amino"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpc "github.com/tendermint/tendermint/rpc/lib/types"
abci "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var aminoCdc = amino.NewCodec()
func init() {
log.SetOutput(os.Stdout)
ctypes.RegisterAmino(aminoCdc)
}
// ListenNewBlock listen new block and trigger sendtx
func (app SantaApp) ListenNewBLock(isTest bool) {
triggerInterval, err := strconv.ParseInt(app.TriggerInterval, 10, 64)
if err != nil {
log.Fatal("Trigger interval should be number", err)
}
var scheme string
var host string
if strings.HasPrefix(app.Node, "https") {
scheme = "wss"
host = strings.TrimPrefix(app.Node, "https://")
} else {
scheme = "ws"
host = strings.TrimPrefix(app.Node, "http://")
}
u := url.URL{Scheme: scheme, Host: host, Path: "/websocket"}
log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
bz, err := json.Marshal(JSONRPC{
JSONRPC: "2.0",
Method: "subscribe",
ID: "0",
Params: Query{
Query: "tm.event='NewBlock'",
},
})
c.WriteMessage(websocket.TextMessage, bz)
for {
_, bz, _ := c.ReadMessage()
var rpcResponse rpc.RPCResponse
var resultEvent ctypes.ResultEvent
// var newBlockEvent abci.EventDataNewBlock
err := aminoCdc.UnmarshalJSON(bz, &rpcResponse)
if err != nil {
continue
}
err = aminoCdc.UnmarshalJSON(rpcResponse.Result, &resultEvent)
blockEvent, ok := resultEvent.Data.(abci.EventDataNewBlock)
if !ok {
continue
}
if blockEvent.Block.Height%triggerInterval == 0 {
txHash, err := app.SendTx(blockEvent.Block.ChainID)
if err != nil {
log.Printf("[Fail] to send tx: %s", err.Error())
app.sendFailMessage(err.Error())
continue
}
log.Printf("[Success] Height: %d,\tTxHash: %s\n", blockEvent.Block.Height, txHash)
app.sendSuccessMessage(blockEvent.Block.Height)
}
if isTest {
break
}
}
}
func (app SantaApp) sendFailMessage(msg string) (err error) {
if app.FailWebHookURL != "" && app.FailWebHookDataKey != "" {
notiBody, _ := json.Marshal(map[string]string{
app.FailWebHookDataKey: fmt.Sprintf("[Fail] sending tx: %s", msg),
})
// send notification to slack
_, err = http.Post(app.FailWebHookURL, "application/json", bytes.NewBuffer(notiBody))
if err != nil {
return err
}
}
return nil
}
func (app SantaApp) sendSuccessMessage(height int64) (err error) {
if app.SuccessWebHookURL != "" && app.SuccessWebHookDataKey != "" {
feeCoin, err := sdk.ParseCoin(app.FeeAmount)
if err != nil {
return err
}
amount := sdk.NewDecFromIntWithPrec(feeCoin.Amount, 6).String()
denom := strings.ToUpper(strings.TrimLeft(feeCoin.Denom, "u"))
notiBody, _ := json.Marshal(map[string]string{
app.SuccessWebHookDataKey: fmt.Sprintf("Hohoho! Santa just gave out %s %s to stakeholders in presents in block %d", amount, denom, height),
})
// send notification to slack
_, err = http.Post(app.SuccessWebHookURL, "application/json", bytes.NewBuffer(notiBody))
if err != nil {
return err
}
}
return nil
}
// no-lint
type Query struct {
Query string `json:"query"`
}
// no-lint
type JSONRPC struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
ID string `json:"id"`
Params Query `json:"params"`
}