rwebsockethttrhttpuv

Error in Websocket connectivity between R and Discord


I am developing an R code that uses a web-socket to send messages as they arrive from Discord to R (not to be confuse with having R querying Discord constantly for new messages, using httr::GET).

Rather than having R asking Discord every certain amount of time for new messages (so you know, Discord does not allows this; Discord has a limit of how many times you can request messages), the websocket allows for Discord to send messages to R only when the arrive.

In R there are two libraries (websocket and httpuv) that do this, but the code I developed while working, closes the connection after some random amount of time from a few minutes to hours (I think Python has a safety reconnecting code, yet to be implemented in R, but not sure). The errors have different names like: "Unhandled promise error: invalid state", etc.

I wonder what am I getting wrong in this code, am I missing a line of code to re-start connections upon the connection is closed or crashed?

library(websocket)
library(jsonlite)
library(async)


Heartbeat =10 #container for heartbite

DiscordSignals <- function(DiscordToken){

  #payloads or messages that R has to send to Discord.
  Rpayload = list(
    op= 2,
    d= list(
      token= DiscordToken,
      intents= 513,
      properties=list(
        os= "windows",
        browser= "firefox",
        device= "firefox")
    )
  )

  KeepAlive = list(op= 1, d= "null" )


  Jsonpayload   = toJSON(Rpayload, auto_unbox = TRUE, pretty = TRUE)
  JsonKeepAlive = toJSON(KeepAlive, auto_unbox = TRUE, pretty = TRUE)

  ws <<- WebSocket$new("wss://gateway.discord.gg/?v=9&encording=json", errorLogChannels ="warn")

  ws$onOpen(function(event) {ws$send(Jsonpayload)}) #Handshake

  ws$onMessage(function(event) {
    d <- event$data
    json = fromJSON(d)
    Alert= json$d$content
    OP<<-as.numeric(json$op) #Type of message sent from Discord

    #https://discord.com/developers/docs/topics/gateway#resuming
    #Discord may request additional heartbeats from your app by sending a Heartbeat (opcode OP1) event. Upon receiving the event, .. immediately send back .. Heartbeat ...

    #reset heartbeat rate to whatever asked by Discord. Heartbite rate requested from Discord is sent in OPs 1 or 10.
    if (OP  %in% c(1,10)){Heartbeat <<-round(abs(((json$d$heartbeat_interval )/1000)-runif(1)-3),0)} 

    print (Alert)
  })

  #send heartbeat every given interval
  async({
    p=1
    while (p==1){
      await(delay(Heartbeat)) 
      ws$send(JsonKeepAlive)
  
    }
  })

}


DiscordSignals (BotToken)

Discord.py has an extensive set of functions about this connectivity, but I could not make sense of it.


Solution

  • I found a solution to this...probably not the most elegant, but still a solution, but please feel free to post any alternative solution as a legacy.

    My solution is as this: it turns out that the breakdown of the connection between Discord and R can be caused by either of them, and can be for diverse reasons.

    So my solution is to create an asynchronicity task checking that the connection is open, if not reset the connection. I have had this connection running for 24hours and is still on, while 4 times could have crashed in that time interval.

    DiscordSignals(BotToken) #Start connection
    
    #Check in the background for the connection, if close open again.
    async({
      q=1
      while (q==1){
        await(delay(5)) 
        Socket=ws$readyState()[1] #If opened, value should be 1
        if (Socket!=1){
           print("Restart"); 
           ws$close(); 
           DiscordSignals(BotToken);
           await(delay(40))} #wait some time for next check, to prevent connecting too many times
      }
    })