2023-04-21 08:02:00 +00:00
package main
import (
2023-04-24 19:01:46 +00:00
"context"
2023-04-21 08:02:00 +00:00
"flag"
"fmt"
2024-04-07 19:36:27 +00:00
"git.gammaspectra.live/P2Pool/consensus/v3/types"
"git.gammaspectra.live/P2Pool/consensus/v3/utils"
"git.gammaspectra.live/P2Pool/observer-cmd-utils/index"
cmdutils "git.gammaspectra.live/P2Pool/observer-cmd-utils/utils"
2023-04-21 08:02:00 +00:00
hbot "github.com/whyrusleeping/hellabot"
log2 "gopkg.in/inconshreveable/log15.v2"
"gopkg.in/sorcix/irc.v2"
"io"
"log"
"net/http"
2023-04-21 13:38:59 +00:00
"net/url"
2023-04-24 19:01:46 +00:00
"nhooyr.io/websocket"
2023-04-21 08:02:00 +00:00
"os"
2023-07-22 15:35:45 +00:00
"slices"
2023-04-21 08:02:00 +00:00
"strings"
"sync"
"time"
)
type foundBlocks [ ] * index . FoundBlock
func ( s foundBlocks ) Get ( mainId types . Hash ) * index . FoundBlock {
if i := slices . IndexFunc ( s , func ( block * index . FoundBlock ) bool {
return block . MainBlock . Id == mainId
} ) ; i != - 1 {
return s [ i ]
}
return nil
}
func ( s foundBlocks ) GetPrevious ( b * index . FoundBlock ) * index . FoundBlock {
var prev * index . FoundBlock
for _ , block := range s {
if prev == nil {
if block . MainBlock . Height < b . MainBlock . Height {
prev = block
}
} else if block . MainBlock . Height < b . MainBlock . Height && block . MainBlock . Height > prev . MainBlock . Height {
prev = block
}
}
return prev
}
const FormatColorGreen = "\x0303"
const FormatColorRed = "\x0304"
const FormatColorOrange = "\x0307"
const FormatColorYellow = "\x0308"
const FormatColorLightGreen = "\x0309"
const FormatBold = "\x02"
const FormatItalic = "\x1D"
const FormatUnderline = "\x1F"
const FormatReset = "\x0F"
func main ( ) {
ircHost := flag . String ( "irc-host" , "irc.libera.chat" , "" )
ircPort := flag . Uint ( "irc-port" , 6697 , "" )
ircSsl := flag . Bool ( "irc-ssl" , true , "" )
botNickName := flag . String ( "bot-nick" , "" , "" )
botUserName := flag . String ( "bot-user" , "" , "" )
2023-04-21 13:38:59 +00:00
pleromaHost := flag . String ( "pleroma-host" , "" , "" )
2023-04-21 08:02:00 +00:00
botPassword := os . Getenv ( "BOT_PASSWORD" )
2023-04-21 13:38:59 +00:00
pleromaCookie := os . Getenv ( "PLEROMA_COOKIE" )
2023-04-21 08:02:00 +00:00
2023-04-25 06:34:17 +00:00
dbPath := flag . String ( "db" , "" , "Path of database" )
2023-04-21 08:02:00 +00:00
botChannels := flag . String ( "channels" , "" , "A list of #CHANNEL,NAME,API_ENDPOINT separated by; for example: #p2pool-main,Main,https://p2pool.observer;#p2pool-mini,Mini,https://mini.p2pool.observer" )
flag . Parse ( )
2023-04-25 06:34:17 +00:00
db , err := NewDB ( * dbPath )
if err != nil {
log . Panic ( err )
2023-04-21 13:38:59 +00:00
}
doPleromaPost := func ( e * channelEntry , message string ) {
if pleromaCookie != "" {
uri , _ := url . Parse ( * pleromaHost + "/api/v1/statuses" )
r := & http . Request {
Method : "POST" ,
URL : uri ,
Header : http . Header {
"content-type" : { "application/x-www-form-urlencoded" } ,
} ,
}
formValues := make ( url . Values )
formValues . Set ( "status" , message )
formValues . Set ( "visibility" , "public" )
formValues . Set ( "content_type" , "text/plain" )
r . Body = io . NopCloser ( strings . NewReader ( formValues . Encode ( ) ) )
r . AddCookie ( & http . Cookie {
Name : "__Host-pleroma_key" ,
Value : pleromaCookie ,
} )
if response , err := http . DefaultClient . Do ( r ) ; err != nil {
log . Print ( err )
} else {
defer response . Body . Close ( )
log . Printf ( "[Pleroma] Posted status %s" , message )
2023-04-21 08:02:00 +00:00
}
}
}
var channelEntries [ ] * channelEntry
for _ , e := range strings . Split ( * botChannels , ";" ) {
e = strings . TrimSpace ( e )
if e == "" {
continue
}
split := strings . Split ( e , "," )
if len ( split ) != 3 {
log . Panicf ( "invalid entry %s" , e )
continue
}
2023-04-25 06:34:17 +00:00
log . Printf ( "Creating entry for %s" , split [ 2 ] )
entry := & channelEntry {
2023-04-26 04:18:17 +00:00
ApiEndpoint : split [ 2 ] ,
Channel : split [ 0 ] ,
Name : split [ 1 ] ,
Window : make ( map [ types . Hash ] * index . SideBlock ) ,
2023-04-25 06:34:17 +00:00
}
//websocket needs to come after
2023-04-26 04:18:17 +00:00
entry . getConsensus ( )
entry . getPreviousBlocks ( )
entry . getPreviousWindow ( )
2023-04-25 06:34:17 +00:00
entry . openWebSocket ( )
channelEntries = append ( channelEntries , entry )
2023-04-21 08:02:00 +00:00
}
2023-04-24 19:01:46 +00:00
var onlineUsersLock sync . RWMutex
var onlineUsers [ ] string
var beforeJoiningChannels sync . Once
2023-04-21 08:02:00 +00:00
if bot , err := hbot . NewBot ( fmt . Sprintf ( "%s:%d" , * ircHost , * ircPort ) , * botNickName , func ( bot * hbot . Bot ) {
2023-04-25 06:34:17 +00:00
bot . ThrottleDelay = time . Millisecond * 100
2023-04-21 08:02:00 +00:00
bot . Nick = * botNickName
bot . Realname = * botUserName
bot . SSL = * ircSsl
bot . Password = botPassword
bot . PingTimeout = time . Hour * 24 * 365 //one year, fix issue with hbot timeouting
if bot . Password != "" {
bot . SASL = true
}
//need to empty old list
bot . Channels = bot . Channels [ : 0 ]
for _ , e := range channelEntries {
bot . Channels = append ( bot . Channels , e . Channel )
}
//unique channels
slices . Sort ( bot . Channels )
bot . Channels = slices . Compact ( bot . Channels )
2023-04-21 13:38:59 +00:00
2023-04-24 19:01:46 +00:00
// Trigger commands before joining channels
bot . AddTrigger ( hbot . Trigger {
Condition : func ( bot * hbot . Bot , m * hbot . Message ) bool {
return m . Command == irc . RPL_WELCOME || m . Command == irc . RPL_ENDOFMOTD // 001 or 372
} ,
Action : func ( bot * hbot . Bot , m * hbot . Message ) bool {
beforeJoiningChannels . Do ( func ( ) {
2023-04-21 13:38:59 +00:00
2023-04-24 19:01:46 +00:00
} )
return false
} ,
} )
2023-04-21 13:38:59 +00:00
2023-04-22 09:21:25 +00:00
// Track channel joins to have users nicks as connected
2023-04-21 08:02:00 +00:00
bot . AddTrigger ( hbot . Trigger {
Condition : func ( bot * hbot . Bot , message * hbot . Message ) bool {
return message . Command == irc . RPL_NAMREPLY || message . Command == irc . RPL_ENDOFNAMES
} ,
Action : func ( bot * hbot . Bot , message * hbot . Message ) bool {
if message . Command == irc . RPL_NAMREPLY {
if len ( message . Params ) < 4 {
return false
}
channelName := message . Params [ 2 ]
if slices . ContainsFunc ( channelEntries , func ( entry * channelEntry ) bool {
return entry . Channel == channelName
} ) {
onlineUsersLock . Lock ( )
defer onlineUsersLock . Unlock ( )
2023-04-25 06:34:17 +00:00
for _ , nick := range strings . Split ( strings . ToLower ( message . Params [ 3 ] ) , " " ) {
2023-04-21 08:02:00 +00:00
if ! slices . Contains ( onlineUsers , nick ) {
onlineUsers = append ( onlineUsers , strings . TrimLeft ( nick , "@~%+" ) )
}
}
}
}
return false
} ,
} )
2023-04-22 09:21:25 +00:00
//Tracks nick changes
bot . AddTrigger ( hbot . Trigger {
Condition : func ( bot * hbot . Bot , message * hbot . Message ) bool {
return message . Command == irc . NICK
} ,
Action : func ( bot * hbot . Bot , message * hbot . Message ) bool {
if message . Command == irc . NICK {
if len ( message . Params ) != 1 {
return false
}
2023-04-25 06:34:17 +00:00
from := strings . ToLower ( message . From )
to := strings . ToLower ( message . To )
2023-04-22 09:21:25 +00:00
onlineUsersLock . Lock ( )
defer onlineUsersLock . Unlock ( )
if i := slices . Index ( onlineUsers , from ) ; i != - 1 {
onlineUsers [ i ] = to
} else {
log . Printf ( "Could not find old user %s -> %s" , from , to )
}
}
return false
} ,
} )
//Tracks JOIN/LEAVE
bot . AddTrigger ( hbot . Trigger {
Condition : func ( bot * hbot . Bot , message * hbot . Message ) bool {
return message . Command == irc . JOIN || message . Command == irc . QUIT || message . Command == irc . PART
} ,
Action : func ( bot * hbot . Bot , message * hbot . Message ) bool {
if message . Command == irc . JOIN {
if len ( message . Params ) != 1 {
return false
}
2023-04-25 06:34:17 +00:00
nick := strings . ToLower ( message . From )
2023-04-22 09:21:25 +00:00
channelName := message . To
onlineUsersLock . Lock ( )
defer onlineUsersLock . Unlock ( )
var channel * channelEntry
for _ , c := range channelEntries {
if c . Channel == channelName {
channel = c
break
}
}
if channel == nil {
//not our channels
return false
}
if i := slices . Index ( onlineUsers , nick ) ; i == - 1 {
onlineUsers = append ( onlineUsers , nick )
}
} else if message . Command == irc . QUIT {
2023-04-25 06:34:17 +00:00
nick := strings . ToLower ( message . From )
2023-04-22 09:21:25 +00:00
onlineUsersLock . Lock ( )
defer onlineUsersLock . Unlock ( )
if i := slices . Index ( onlineUsers , nick ) ; i != - 1 {
onlineUsers = slices . Delete ( onlineUsers , i , i + 1 )
} else {
log . Printf ( "Could not find user who quit %s" , nick )
}
}
return false
} ,
} )
2023-04-24 19:01:46 +00:00
} ) ; err != nil {
log . Panic ( err )
} else {
logHandler := log2 . LvlFilterHandler ( log2 . LvlDebug , log2 . StdoutHandler )
bot . Logger . SetHandler ( logHandler )
// see about irc.ERR_NICKNAMEINUSE or irc.ERR_NICKCOLLISION to recover nick
2023-04-25 06:34:17 +00:00
payoutFound := func ( e * channelEntry , b * index . FoundBlock , o * index . MainCoinbaseOutput , sub * Subscription ) {
bot . Msg ( sub . Nick , fmt . Sprintf (
2023-04-28 18:54:19 +00:00
"%sPAYOUT%s on %s: %s%s%s XMR%s to %s%s%s :: Main height %s%d%s, Side height %d :: %s :: Verify payout %s" ,
2023-04-25 06:34:17 +00:00
FormatColorLightGreen + FormatBold , FormatReset , e . Name ,
FormatColorOrange , FormatBold , utils . XMRUnits ( o . Value ) , FormatReset ,
2024-04-07 19:36:27 +00:00
FormatItalic , cmdutils . Shorten ( string ( o . MinerAddress . ToBase58 ( ) ) , 10 ) , FormatReset ,
2023-04-25 06:34:17 +00:00
FormatColorRed , b . MainBlock . Height , FormatReset ,
b . SideHeight ,
2023-04-28 18:54:19 +00:00
GetShareLink ( e . ApiEndpoint , b . SideHeight , b . MainBlock . Id ) ,
GetPayoutLink ( e . ApiEndpoint , b . SideHeight , o . Index , e . Consensus ) ,
2023-04-25 06:34:17 +00:00
) )
}
shareFound := func ( e * channelEntry , tip * index . SideBlock , sub * Subscription ) {
shareCount := 0
uncleCount := 0
var yourWeight types . Difficulty
var totalWeight types . Difficulty
2023-04-26 04:29:05 +00:00
e . IterateWindow ( func ( b * index . SideBlock , weight types . Difficulty ) {
2023-04-25 06:34:17 +00:00
totalWeight = totalWeight . Add ( weight )
if tip . Miner == b . Miner {
if b . IsUncle ( ) {
uncleCount ++
} else {
shareCount ++
}
yourWeight = yourWeight . Add ( weight )
}
2023-04-26 04:29:05 +00:00
} )
2023-04-25 06:34:17 +00:00
shareRatio := float64 ( yourWeight . Lo ) / float64 ( totalWeight . Lo )
if shareRatio > notificationPoolShare { //disable spammy notifications
return
}
if tip . UncleOf != types . ZeroHash {
bot . Msg ( sub . Nick , fmt . Sprintf (
2023-04-28 18:54:19 +00:00
"%sUNCLE SHARE FOUND%s on %s: Side height %s%d%s, Parent Side height %s%d%s :: %s :: Accounted for %d%% of value :: Template Id %s%s%s :: Window shares %d (+%d uncles) ~%.03f%% :: Miner Address %s%s" ,
2023-04-24 19:01:46 +00:00
FormatColorLightGreen + FormatBold , FormatReset , e . Name ,
2023-04-25 06:34:17 +00:00
FormatColorRed , tip . SideHeight , FormatReset ,
FormatColorRed , tip . EffectiveHeight , FormatReset ,
2023-04-28 18:54:19 +00:00
GetShareLink ( e . ApiEndpoint , tip . SideHeight , tip . MainId ) ,
2023-04-24 19:01:46 +00:00
100 - e . Consensus . UnclePenalty ,
2024-04-07 19:36:27 +00:00
FormatItalic , cmdutils . Shorten ( tip . TemplateId . String ( ) , 8 ) , FormatReset ,
2023-04-25 06:34:17 +00:00
shareCount , uncleCount , shareRatio * 100 ,
2024-04-07 19:36:27 +00:00
FormatItalic , cmdutils . Shorten ( string ( tip . MinerAddress . ToBase58 ( ) ) , 10 ) ,
2023-04-24 19:01:46 +00:00
) )
} else {
uncleText := ""
2023-04-25 06:34:17 +00:00
if len ( tip . Uncles ) > 0 {
uncleText = fmt . Sprintf ( ":: Includes %d uncle(s) for extra %d%% of their value " , len ( tip . Uncles ) , e . Consensus . UnclePenalty )
2023-04-24 19:01:46 +00:00
}
2023-04-25 06:34:17 +00:00
bot . Msg ( sub . Nick , fmt . Sprintf (
2023-04-28 18:54:19 +00:00
"%sSHARE FOUND%s on %s: Side height %s%d%s :: %s %s:: Template Id %s%s%s :: Window shares %d (+%d uncles) ~%.03f%% :: Miner Address %s%s" ,
2023-04-24 19:01:46 +00:00
FormatColorLightGreen + FormatBold , FormatReset , e . Name ,
2023-04-25 06:34:17 +00:00
FormatColorRed , tip . SideHeight , FormatReset ,
2023-04-28 18:54:19 +00:00
GetShareLink ( e . ApiEndpoint , tip . SideHeight , tip . MainId ) ,
2023-04-24 19:01:46 +00:00
uncleText ,
2024-04-07 19:36:27 +00:00
FormatItalic , cmdutils . Shorten ( tip . TemplateId . String ( ) , 8 ) , FormatReset ,
2023-04-25 06:34:17 +00:00
shareCount , uncleCount , shareRatio * 100 ,
2024-04-07 19:36:27 +00:00
FormatItalic , cmdutils . Shorten ( string ( tip . MinerAddress . ToBase58 ( ) ) , 10 ) ,
2023-04-24 19:01:46 +00:00
) )
}
}
blockFound := func ( e * channelEntry , b * index . FoundBlock , previous * index . FoundBlock ) {
effort := float64 ( 0 )
if previous != nil {
2023-07-22 15:35:45 +00:00
effort = float64 ( b . CumulativeDifficulty . Sub ( previous . CumulativeDifficulty ) . Mul64 ( 100 ) . Lo ) / float64 ( b . MainBlock . Difficulty )
2023-04-24 19:01:46 +00:00
}
2023-04-26 08:10:06 +00:00
2023-04-24 19:01:46 +00:00
bot . Msg ( e . Channel , fmt . Sprintf (
2023-04-28 18:54:19 +00:00
"%sBLOCK FOUND%s on %s: Main height %s%d%s, Side height %d :: %s :: Effort %s%.02f%%%s :: %s%d miners paid%s, total %s%s%s XMR%s :: Id %s%s" ,
2023-04-24 19:01:46 +00:00
FormatColorLightGreen + FormatBold , FormatReset , e . Name ,
FormatColorRed , b . MainBlock . Height , FormatReset ,
b . SideHeight ,
2023-04-28 18:54:19 +00:00
GetShareLink ( e . ApiEndpoint , b . SideHeight , b . MainBlock . Id ) ,
2023-04-26 08:10:06 +00:00
EffortColor ( effort ) , effort , FormatReset ,
2023-04-24 19:01:46 +00:00
FormatColorOrange , b . WindowOutputs , FormatReset ,
FormatColorOrange , FormatBold , utils . XMRUnits ( b . MainBlock . Reward ) , FormatReset ,
2024-04-07 19:36:27 +00:00
FormatItalic , cmdutils . Shorten ( b . MainBlock . Id . String ( ) , 8 ) ,
2023-04-24 19:01:46 +00:00
) )
doPleromaPost ( e , fmt . Sprintf (
2023-04-28 18:54:19 +00:00
"BLOCK FOUND on %s, Main height %d, Side height %d :: %s :: Effort %.02f%% :: %d miners paid, total %s XMR :: Id %s" ,
2023-04-24 19:01:46 +00:00
e . Name ,
b . MainBlock . Height ,
b . SideHeight ,
2023-04-28 18:54:19 +00:00
GetShareLink ( e . ApiEndpoint , b . SideHeight , b . MainBlock . Id ) ,
2023-04-24 19:01:46 +00:00
effort ,
b . WindowOutputs ,
utils . XMRUnits ( b . MainBlock . Reward ) ,
b . MainBlock . Id ,
) )
}
blockOrphaned := func ( e * channelEntry , b * index . SideBlock ) {
bot . Msg ( e . Channel , fmt . Sprintf (
"%sBLOCK ORPHANED%s on %s: Main height %s%d%s, Side height %d :: Side Template Id %s%s%s :: Id %s%s" ,
FormatColorLightGreen + FormatBold , FormatReset , e . Name ,
FormatColorRed , b . MainHeight , FormatReset ,
b . SideHeight ,
FormatItalic , b . TemplateId , FormatReset ,
FormatItalic , b . MainId ,
) )
doPleromaPost ( e , fmt . Sprintf (
"BLOCK ORPHANED on %s, Main height %d, Side height %d :: Side Template Id %s :: Id %s" ,
e . Name ,
b . MainHeight ,
b . SideHeight ,
b . TemplateId ,
b . MainId ,
) )
}
2023-04-22 09:21:25 +00:00
2023-04-21 08:02:00 +00:00
//Private message
bot . AddTrigger ( hbot . Trigger {
Condition : func ( bot * hbot . Bot , message * hbot . Message ) bool {
trimMessage := strings . TrimSpace ( message . Content )
if len ( trimMessage ) <= 0 || trimMessage [ 0 ] != '.' {
return false
}
2023-04-25 06:34:17 +00:00
if message . To == bot . Nick {
return true
}
for _ , e := range channelEntries {
if message . To == e . Channel {
return true
}
}
return false
2023-04-21 08:02:00 +00:00
} ,
Action : func ( bot * hbot . Bot , message * hbot . Message ) bool {
2023-04-25 06:34:17 +00:00
replyTo := message . To
if replyTo == bot . Nick || replyTo [ 0 ] != '#' {
replyTo = message . Name
}
for _ , c := range commands {
if matches := c . Match . FindStringSubmatch ( message . Content ) ; len ( matches ) > 0 {
if c . Handle ( db , channelEntries , bot , message , replyTo , matches ... ) {
return true
}
}
}
bot . Msg ( replyTo , "Command not recognized" )
2023-04-21 08:02:00 +00:00
return true
} ,
} )
2023-04-24 19:01:46 +00:00
_ = shareFound
2023-04-21 08:02:00 +00:00
for _ , e := range channelEntries {
if e . ApiEndpoint == "" {
continue
}
2023-04-26 17:11:12 +00:00
go func ( e * channelEntry ) {
2023-04-26 08:10:06 +00:00
for range time . NewTicker ( time . Second * 30 ) . C {
ws := e . Ws . Load ( )
if ws != nil {
func ( ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Second * 15 )
defer cancel ( )
if err := ws . Ping ( ctx ) ; err != nil {
2023-04-26 17:11:12 +00:00
log . Printf ( "[WS] WebSocket for %s disconnected on ping: %s" , e . ApiEndpoint , err )
2023-04-26 08:10:06 +00:00
ws . Close ( websocket . StatusGoingAway , "error on ping " + err . Error ( ) )
e . Ws . Store ( nil )
}
} ( )
2023-04-21 08:02:00 +00:00
}
2023-04-24 19:01:46 +00:00
}
2023-04-26 17:11:12 +00:00
} ( e )
2023-04-26 08:10:06 +00:00
go func ( e * channelEntry ) {
2023-04-21 08:02:00 +00:00
2023-04-24 19:01:46 +00:00
for {
2023-04-26 08:10:06 +00:00
ws := e . Ws . Load ( )
if ws == nil {
log . Printf ( "[WS] WebSocket for %s is disconnected, retrying" , e . ApiEndpoint )
for ; e . Ws . Load ( ) == nil ; e . openWebSocket ( ) {
time . Sleep ( time . Second * 30 )
log . Printf ( "[WS] WebSocket for %s is disconnected, retrying again" , e . ApiEndpoint )
}
ws = e . Ws . Load ( )
}
2023-04-25 06:34:17 +00:00
2023-04-26 17:11:12 +00:00
if event , err := e . readEvent ( ) ; event == nil || err != nil {
log . Printf ( "[WS] WebSocket for %s disconnected: %s" , e . ApiEndpoint , err )
2023-04-26 08:10:06 +00:00
ws . Close ( websocket . StatusGoingAway , "error on reading event" )
e . Ws . Store ( nil )
2023-04-26 17:11:12 +00:00
continue
2023-04-24 19:01:46 +00:00
} else {
2023-04-25 06:34:17 +00:00
func ( ) {
e . ChainLock . Lock ( )
defer e . ChainLock . Unlock ( )
switch event . Type {
2024-04-07 19:36:27 +00:00
case cmdutils . JSONEventSideBlock :
2023-04-26 01:32:41 +00:00
log . Printf ( "[SideBlock] On %s, id %s, height %d" , e . Name , event . SideBlock . TemplateId , event . SideBlock . SideHeight )
2023-04-25 06:34:17 +00:00
b := event . SideBlock
e . Window [ b . TemplateId ] = b
if ! b . IsUncle ( ) {
e . Tip = b
}
onlineUsersLock . RLock ( )
defer onlineUsersLock . RUnlock ( )
for _ , sub := range db . GetByAddress ( b . MinerAddress ) {
if slices . Contains ( onlineUsers , strings . ToLower ( sub . Nick ) ) {
//do not send notification if user is not online
shareFound ( e , b , sub )
} else {
log . Printf ( "Could not send notification to %s - %s: not online" , sub . Nick , sub . Address . ToBase58 ( ) )
}
}
e . pruneBlocks ( )
2024-04-07 19:36:27 +00:00
case cmdutils . JSONEventFoundBlock :
2023-04-26 01:32:41 +00:00
log . Printf ( "[FoundBlock] On %s, id %s, height %d" , e . Name , event . FoundBlock . MainBlock . Id , event . FoundBlock . MainBlock . Height )
2023-04-25 06:34:17 +00:00
e . PreviousBlocks = append ( e . PreviousBlocks , event . FoundBlock )
blockFound ( e , event . FoundBlock , e . PreviousBlocks . GetPrevious ( event . FoundBlock ) )
onlineUsersLock . RLock ( )
defer onlineUsersLock . RUnlock ( )
for _ , o := range event . MainCoinbaseOutputs {
for _ , sub := range db . GetByAddress ( o . MinerAddress ) {
if slices . Contains ( onlineUsers , strings . ToLower ( sub . Nick ) ) {
//do not send notification if user is not online
payoutFound ( e , event . FoundBlock , & o , sub )
} else {
log . Printf ( "Could not send notification to %s - %s: not online" , sub . Nick , sub . Address . ToBase58 ( ) )
}
}
}
if len ( e . PreviousBlocks ) > numberOfBlockHistoryToKeep {
//delete oldest block
e . PreviousBlocks = slices . Delete ( e . PreviousBlocks , 0 , 1 )
}
2024-04-07 19:36:27 +00:00
case cmdutils . JSONEventOrphanedBlock :
2023-04-26 01:32:41 +00:00
log . Printf ( "[OrphanedBlock] On %s, id %s, height %d, main id %s, main height %d" , e . Name , event . SideBlock . TemplateId , event . SideBlock . SideHeight , event . SideBlock . MainId , event . SideBlock . MainHeight )
2023-04-25 06:34:17 +00:00
if i := slices . IndexFunc ( e . PreviousBlocks , func ( block * index . FoundBlock ) bool {
return event . SideBlock . MainId == block . MainBlock . Id
} ) ; i != - 1 {
//only notify if we reported it previously
slices . Delete ( e . PreviousBlocks , i , i + 1 )
blockOrphaned ( e , event . SideBlock )
}
2023-04-26 01:19:02 +00:00
default :
log . Printf ( "unknown event %s" , event . Type )
2023-04-26 08:10:06 +00:00
ws . Close ( websocket . StatusGoingAway , "unknown event" )
e . Ws . Store ( nil )
2023-04-26 01:19:02 +00:00
break
2023-04-24 19:01:46 +00:00
}
2023-04-25 06:34:17 +00:00
} ( )
2023-04-21 08:02:00 +00:00
}
}
} ( e )
}
bot . Run ( )
}
}