diff --git a/capi/capi.go b/capi/capi.go index d2f4936..eaf37d5 100644 --- a/capi/capi.go +++ b/capi/capi.go @@ -61,17 +61,18 @@ func GetMinerState() ( func NextChat() ( username *C.char, message *C.char, + id int64, timestamp int64) { nc := chat.NextChatReceived() if nc == nil { - return C.CString(""), C.CString(""), 0 + return C.CString(""), C.CString(""), 0, 0 } - return C.CString(nc.Username), C.CString(nc.Message), nc.Timestamp + return C.CString(nc.Username), C.CString(nc.Message), nc.ID, nc.Timestamp } //export SendChat -func SendChat(message *C.char) { - chat.SendChat(C.GoString(message)) +func SendChat(message *C.char) int64 { + return chat.SendChat(C.GoString(message)) } //export IncreaseThreads diff --git a/capi/niceapi.h b/capi/niceapi.h index 7376d7b..6b76cd8 100644 --- a/capi/niceapi.h +++ b/capi/niceapi.h @@ -179,26 +179,28 @@ typedef struct next_chat_response { // NOTE: you must free() each const char* const char* username; // username of the user who sent the chat (ascii) const char* message; // the chat message (unicode) + int64_t id; // sent by the client to uniquely identify this chat (w.r.t username) int64_t timestamp; // unix timestamp of when the chat was received by chat server } next_chat_response; -// Return the next available chat message. If there are no chat messages left to return, -// the chat response will have empty username/message +// Return the next available chat message. If there are no chat messages left to return, the chat +// response will have 0 for id and timestamp and empty username/message. next_chat_response next_chat() { struct NextChat_return r = NextChat(); next_chat_response response; response.username = r.r0; response.message = r.r1; - response.timestamp = (int64_t)r.r2; + response.id = (int64_t)r.r2; + response.timestamp = (int64_t)r.r3; return response; } -// Queue a chat message for sending. Returns a code indicating if successful (0) or not. Right now -// only success is returned. Message might not be sent immediately, e.g. miner may wait to send it -// with the next mined share. +// Queue a chat message for sending. Message might not be sent immediately, e.g. miner may wait to +// send it with the next mined share. Return value, if positive, is the unique id generated by +// this client sent with the chat. Negative return value indicates error (though right now this +// should never happen). int send_chat(const char *message) { - SendChat((char*)message); - return 0; + return SendChat((char*)message); } // Increase the number of threads by 1. This may fail. get_miner_state will diff --git a/miner.go b/miner.go index 0e3db6f..e2c3d49 100644 --- a/miner.go +++ b/miner.go @@ -16,7 +16,9 @@ import ( "github.com/cryptonote-social/csminer/stratum/client" ) -var () +var ( + chatsSent map[int64]struct{} +) const ( // Valid machine state changes @@ -47,6 +49,7 @@ type MinerConfig struct { } func Mine(c *MinerConfig) error { + chatsSent = map[int64]struct{}{} imResp := minerlib.InitMiner(&minerlib.InitMinerArgs{ Threads: c.Threads, ExcludeHourStart: c.ExcludeHrStart, @@ -145,7 +148,9 @@ func Mine(c *MinerConfig) error { } if strings.HasPrefix(b, "c ") { chatMsg := b[2:] - chat.SendChat(chatMsg) + id := chat.SendChat(chatMsg) + chatsSent[id] = struct{}{} + crylog.Info("\n\nCHAT MESSAGE QUEUED TO SEND:\n[", c.Username, "] (", time.Now().Truncate(time.Second), ")\n", chatMsg, "\n") } } @@ -230,7 +235,12 @@ func printStatsPeriodically() { <-time.After(3 * time.Second) //printStats(true) // print full stats only if actively mining for c := chat.NextChatReceived(); c != nil; c = chat.NextChatReceived() { - crylog.Info("\n\nCHAT MESSAGE RECEIVED:\n[", c.Username, "] ", c.Message, "\n") + _, ok := chatsSent[c.ID] + if !ok { + crylog.Info("\n\nCHAT MESSAGE RECEIVED:\n[", c.Username, "] (", time.Unix(c.Timestamp, 0), ")\n", c.Message, "\n") + } else { + crylog.Info("Chat sent:", c.Message) + } } } } diff --git a/minerlib/chat/chat.go b/minerlib/chat/chat.go index e8fd6b7..58c46ac 100644 --- a/minerlib/chat/chat.go +++ b/minerlib/chat/chat.go @@ -4,6 +4,9 @@ import ( "github.com/cryptonote-social/csminer/crylog" "github.com/cryptonote-social/csminer/stratum/client" + "crypto/rand" + "encoding/binary" + "math" "sync" ) @@ -16,26 +19,40 @@ var ( receivedQueue []*client.ChatResult chatReceivedIndex int - nextToken int + nextToken int64 + + randID int64 ) -func SendChat(chat string) { +func init() { + err := binary.Read(rand.Reader, binary.LittleEndian, &randID) + if err != nil { + crylog.Fatal("Init error for randID:", err) + } + // get rid of negative sign just for aesthetics + randID &= math.MaxInt64 +} + +// Queue a chat for sending, returning the id token of the chat +func SendChat(chat string) int64 { mutex.Lock() defer mutex.Unlock() chatQueue = append(chatQueue, chat) crylog.Info("Chat queued for sending:", chat) + return int64(len(chatQueue)-1) ^ randID } // GetChatToSend returns the next queued chat message that needs to be delivered. The function -// will return the same result until ChatSent is called. It will return (nil, -1) if there are no +// will return the same result until ChatSent is called. It will return ("", -1) if there are no // chats to send at this time. -func GetChatToSend() (chat string, id int) { +func GetChatToSend() (chat string, id int64) { mutex.Lock() defer mutex.Unlock() if chatToSendIndex >= len(chatQueue) { return "", -1 } - return chatQueue[chatToSendIndex], chatToSendIndex + crylog.Info("ID:", int64(chatToSendIndex)^randID, randID) + return chatQueue[chatToSendIndex], int64(chatToSendIndex) ^ randID } func HasChatsToSend() bool { @@ -44,16 +61,15 @@ func HasChatsToSend() bool { return chatToSendIndex < len(chatQueue) } -func ChatSent(id int) { +func ChatSent(id int64) { mutex.Lock() defer mutex.Unlock() - if id == chatToSendIndex { - crylog.Info("Chat message delivered:", chatQueue[id]) + if id == int64(chatToSendIndex)^randID { chatToSendIndex++ } } -func ChatsReceived(chats []client.ChatResult, chatToken int, fetchedToken int) { +func ChatsReceived(chats []client.ChatResult, chatToken int64, fetchedToken int64) { if len(chats) != 0 { crylog.Info("Chats received:", chats) } @@ -86,7 +102,7 @@ func NextChatReceived() *client.ChatResult { return nil } -func NextToken() int { +func NextToken() int64 { mutex.Lock() defer mutex.Unlock() return nextToken diff --git a/minerlib/minerlib.go b/minerlib/minerlib.go index 26dbe29..75c43ec 100644 --- a/minerlib/minerlib.go +++ b/minerlib/minerlib.go @@ -617,7 +617,7 @@ func printStats(isMining bool) { func GetChats() { nt := chat.NextToken() - crylog.Info("Getting chats:", nt) + //crylog.Info("Getting chats:", nt) resp, err := cl.GetChats(nt) if err != nil { crylog.Error("Failed to retrieve chats:", nt, err) diff --git a/stratum/client/client.go b/stratum/client/client.go index 60eeba4..68a34c1 100644 --- a/stratum/client/client.go +++ b/stratum/client/client.go @@ -37,7 +37,7 @@ type Job struct { PoolWallet string `json:"pool_wallet"` ExtraNonce string `json:"extra_nonce"` - ChatToken int `json:"chat_token"` // custom field + ChatToken int64 `json:"chat_token"` // custom field } type RXJob struct { @@ -84,12 +84,13 @@ type SubmitWorkResult struct { type ChatResult struct { Username string // user sending the chat Message string // the chat message + ID int64 // ID sent by client to uniquely identify this chat Timestamp int64 // unix time in seconds when the chat message was sent } type GetChatsResult struct { Chats []ChatResult - NextToken int + NextToken int64 } type Response struct { @@ -102,7 +103,7 @@ type Response struct { Error interface{} `json:"error"` - ChatToken int `json:"chat_token"` // custom field + ChatToken int64 `json:"chat_token"` // custom field } type loginResponse struct { @@ -122,7 +123,7 @@ type loginResponse struct { Message string `json:"message"` } `json:"warning"` - ChatToken int `json:"chat_token"` // custom field + ChatToken int64 `json:"chat_token"` // custom field } type Client struct { @@ -288,7 +289,7 @@ func (cl *Client) submitRequest(submitRequest interface{}, expectedResponseID ui return response, nil } -func (cl *Client) GetChats(chatToken int) (*Response, error) { +func (cl *Client) GetChats(chatToken int64) (*Response, error) { chatRequest := &struct { ID uint64 `json:"id"` Method string `json:"method"` @@ -297,7 +298,7 @@ func (cl *Client) GetChats(chatToken int) (*Response, error) { ID: GET_CHATS_JSON_ID, Method: "get_chats", Params: &struct { - ChatToken int `json:"chat_token"` + ChatToken int64 `json:"chat_token"` }{chatToken}, } @@ -305,7 +306,7 @@ func (cl *Client) GetChats(chatToken int) (*Response, error) { } // if error is returned then client will be closed and put in not-alive state -func (cl *Client) SubmitWork(nonce string, jobid string, chat string, chatID int) (*Response, error) { +func (cl *Client) SubmitWork(nonce string, jobid string, chat string, chatID int64) (*Response, error) { submitRequest := &struct { ID uint64 `json:"id"` Method string `json:"method"` @@ -320,7 +321,7 @@ func (cl *Client) SubmitWork(nonce string, jobid string, chat string, chatID int Result string `json:"result"` Chat string `json:"chat"` - ChatID int `json:"chat_id"` + ChatID int64 `json:"chat_id"` }{"696969", jobid, nonce, "", chat, chatID}, } return cl.submitRequest(submitRequest, SUBMIT_WORK_JSON_ID)