The following script uses RSelenium to post all athletes’ logged activities to FSurge’s Social Wall.

It can be imported in Windows Task scheduler to automatically upload activities on specific timepoints (e.g., every 10 minutes; see this R-bloggers post). This may also be scheduled from R itself (see here).

#clean environment
rm(list=ls())
#garbage collect
gc()
#kill java instances within Rstudio
system("taskkill /im java.exe /f", intern=FALSE, ignore.stdout=FALSE)

#setwd to folder containing script
wd <- "C:/Users/Rob/Documents/GitHub/FinalSurge_experiment" 
setwd(wd)

#load packages
#install.packages("RSelenium")
#install.packages("netstat")
library(RSelenium)
library(rvest)
library(tidyverse)
library(netstat)
library(pingr)

#find a free port
port <- netstat::free_port(random=T)
#ping port for confirmation
#pingr::ping_port("localhost", port)

#set up selenium server and browser
#rsDriver() tries to load latest Chrome driveres; so set to NULL!!!
rD <- rsDriver(browser="firefox", port=port, verbose=F, chromever=NULL)
remDr <- rD[["client"]]
#pingr::ping_port("localhost", port) #check

#load login attributes from `secret`-folder, containing attributes required for login:
#email, password, 
load("./local/accounts.R") # all pre-set accounts

#only take accounts that are assigned a social wall
df <- accounts[which(accounts$socialwall==1),] #social wall participants

#and only take accounts that are occupied
df <- df[which(df$occupied==T),]

#shuffle rows...
df <- df[sample(1:nrow(df)), ]

#no. of athletes n
(n <- nrow(df))

for (i in 1:n) {

  try = 0
  success <- FALSE
  
  while (!success & try<5) {
    
    try=try+1
    
    tryCatch( {
      
      #login
      remDr$navigate("https://log.finalsurge.com/login.cshtml")
      remDr$findElement(using = "id", value = "login_name")$sendKeysToElement(list(df$email[i]))
      remDr$findElement(using = "id", value = "login_password")$sendKeysToElement(list(df$paswoord[i]))
      remDr$findElements("class name", "btn")[[1]]$clickElement()
      
      #navigate to workout report
      remDr$navigate("https://log.finalsurge.com/WorkoutReport.cshtml")
      
      #activities performed in last 5 days can be posted to social wall
      today <- as.Date(format(Sys.Date()))
      mydate <- today-5
      myFSdate <- format(mydate, "%m/%d/%Y") #convert to final surge format
      
      remDr$findElement(using = "id", value = "WorkoutDate")$clearElement()
      remDr$findElement(using = "id", value = "WorkoutDate")$sendKeysToElement(list(myFSdate, key="enter"))
      Sys.sleep(3)
      #get the activities performed
      #remDr$findElement("class", "table")$highlightElement()
      page <- remDr$getPageSource() #get source
      
      ###page[[2]]#this is a test: this will give an error!!
      
      #and put in dataframe
      fs_table <- page[[1]] %>%
        read_html() %>%
        html_nodes('table') %>% 
        .[[1]] %>%
        html_table() %>%
        as.data.frame(.) %>% 
        #remove last row (total)
        .[-nrow(.),]
      
      #exclude activities that cannot be posted on the wall
      #i.e., with no time
      fs_table <- fs_table[fs_table$Werkelijk!="",]
      
      #also exclude warmup and cooldown
      fs_table <- fs_table[fs_table$Activiteit!="Warming-up" & fs_table$Activiteit!="Cooldown",]
      
      if("NA" %in% rownames(fs_table)) {
        fs_table <- fs_table[-nrow(fs_table),]
      }
      
      #the workout report does not contain the full workout description...
      #that is, the name combined with the description.
      #because there is a limit on the number of characters in the workout report....
      #scrape full description by navigating to 'workout details' link
      
      #the description may also contain substring "updated to social wall" if activities
      #were already shared (i add this later on); these activities should of course not be 
      #shared again...
      
      if(nrow(fs_table)>0) { # only if athlete has shareable workouts...
        
        #first create a new variable 'workout description'
        fs_table$description <- NA
        
        #also add a variable indicating whether activity is already posted or not
        fs_table$posted <- 0
        
        #get inner html of workout report
        #remDr$findElement("class", "table")$highlightElement()
        inner <- remDr$findElement("class", "table")$getElementAttribute("innerHTML")
        
        #get urls corresponding to activities in workout report
        #and put in vector
        url <- read_html(inner[[1]]) %>% #read html
          html_nodes("td") %>% #get data cells (td tags)
          .[grepl( "WorkoutDetails", ., fixed = TRUE)] %>% #extract "WorkoutDetails" urls
          str_extract_all(.,"(?<=a href=).+(?=target=)") %>%
          unlist(.) %>%
          substring(.,16) %>%
          substring(.,1,nchar(.)-2)
        
        for (x in 1:nrow(fs_table)) { #for activity x
  
          #get full link
          link <- paste0("https://log.finalsurge.com/WorkoutAdd", url[as.numeric(rownames(fs_table))][x])
          
          #navigate
          remDr$navigate(link)
          Sys.sleep(1)
          
          #scrape workout description
          desc <- remDr$findElement(using = "id", value = "Desc")$getElementAttribute("value")[[1]]
          fs_table$description[x] <- desc
          
          #and name
          name <- remDr$findElement(using = "id", value = "Name")$getElementAttribute("value")[[1]]
          fs_table$Workout[x] <- name
          
          #and notes
          #remDr$findElements("class name", "w-box-header")[[2]]$highlightElement()
          #for some reason, the textbox may already be opened. So, click only if that is not the case
          if(remDr$findElements("class name", "w-box-content")[[2]]$getElementText()[[1]] == "") {
            remDr$findElements("class name", "w-box-header")[[2]]$clickElement() }
          
          note <- remDr$findElement(using = "id", value = "PostDesc")$getElementAttribute("value")[[1]]
          fs_table$Notities[x] <- note
          
          #if the description contains "posted to social wall", posted=1
          fs_table$posted[x] <- ifelse(T %in% grepl("Gepost op de Social Wall!", fs_table$description[x], fixed = TRUE), 1, fs_table$posted[x])
        } 
       
        #remove empty strings with NA
        fs_table$Workout <- ifelse(fs_table$Workout=="", NA, fs_table$Workout)
        fs_table$description <- ifelse(fs_table$description=="", NA, fs_table$description)
        fs_table$Gevoel <- ifelse(fs_table$Gevoel=="", NA, fs_table$Gevoel)
        fs_table$Notities <- ifelse(fs_table$Notities=="", NA, fs_table$Notities)
        
        #if new activities to share...
        share <- which(fs_table$posted==0)
        
        if(length(share)>0) {
          Sys.sleep(1)
          #to the social wall!
          remDr$navigate("https://log.finalsurge.com/SocialWall/")
          Sys.sleep(.5)
          #agree  on "advisory on sharing"
          remDr$findElements("class name", "btn")[[1]]$clickElement()
          
          for (j in unique(share)) {
            remDr$navigate("https://log.finalsurge.com/SocialWall/")
            Sys.sleep(.5)
            remDr$findElement(using = 'class','social-workout')$clickElement()
            Sys.sleep(.5)
            remDr$findElements("class", "ShareWorkout")[[j]]$clickElement()
          
            #before publishing, add a description (workout note), based on `fs_table`/workoutreport
            
            #get activity from fs_table corresponding to this workout
            activity <- fs_table[j,]
            
            if(!is.na(activity$Workout)) {
              remDr$findElement(using="id", value="socialpost")$sendKeysToElement(list(paste0("Activiteit: ", activity$Workout, "\n\n")))
            }
            if(!is.na(activity$description)) {
              remDr$findElement(using="id", value="socialpost")$sendKeysToElement(list(paste0("Beschrijving: ", activity$description, "\n\n")))
            }
            if(!is.na(activity$Notities)) {
              remDr$findElement(using="id", value="socialpost")$sendKeysToElement(list(paste0("Notities: ", activity$Notities)))
            }
            #publish
            remDr$findElements("class name", "btn")[[5]]$clickElement()
            
            Sys.sleep(3)
            
            #now go back to the workout details to add substring "posted to social wall"
            #to description to prevent activity from being shared again!
            remDr$navigate(paste0("https://log.finalsurge.com/WorkoutAdd/",  url[as.numeric(rownames(fs_table))[j]]))
            Sys.sleep(1)
            des <- remDr$findElement("id", "Desc")$getElementText()
            if(!grepl("Gepost op de Social Wall!", des)) {
              remDr$findElement(using="id", value="Desc")$sendKeysToElement(list("\n\nGepost op de Social Wall!"))
              Sys.sleep(.2)
              remDr$findElement(using = "id", value = "saveButton")$clickElement()
            }
    
          }
        }
      }
      #logout
      remDr$findElement(using = 'link text','Uitloggen')$clickElement()
      Sys.sleep(.5)
      
      # and re-log in with new account...)
      remDr$findElement(using = 'link text','Log in account')$clickElement()
      Sys.sleep(1)
      
      #if the script succeeded, we're at the login page again!
      if(remDr$getTitle()[[1]]=="Final Surge - Inloggen") success <- TRUE
    }, 
    error = function(e) { #if an error is caught, logout 
      remDr$findElement(using = 'link text','Uitloggen')$clickElement()
    }) 
  }
  
}

#terminate processes
pid <- rD$server$process$get_pid()#get process id
system(paste0("Taskkill /F /T" ," /PID ", pid))
#pingr::ping_port("localhost", port) #check

rm(list=ls())
gc()
quit()
n
LS0tDQp0aXRsZTogIlNvY2lhbCBXYWxsIFVwZGF0ZXIiDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY3NzOiB0d2Vha3MuY3NzDQogICAgdG9jOiAgZmFsc2UNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCi0tLQ0KDQpgYGB7ciwgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KbGlicmFyeShrbml0cikNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLmN1dG9mZj0xMDApLHRpZHk9VFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsY29tbWVudCA9ICIjPiIsIGNhY2hlPVRSVUUsIGNsYXNzLnNvdXJjZT1jKCJ0ZXN0IiksIGNsYXNzLm91dHB1dD1jKCJ0ZXN0MiIpKQ0Kb3B0aW9ucyh3aWR0aCA9IDEwMCkNCnJnbDo6c2V0dXBLbml0cigpDQoNCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCmBgYA0KDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0Ka2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCd0b3AnLCAncmlnaHQnKSkNCiNrbGlwcHk6OmtsaXBweShjb2xvciA9ICdkYXJrcmVkJykNCiNrbGlwcHk6OmtsaXBweSh0b29sdGlwX21lc3NhZ2UgPSAnQ2xpY2sgdG8gY29weScsIHRvb2x0aXBfc3VjY2VzcyA9ICdEb25lJykNCmBgYA0KDQoNCi0tLSAgDQoNClRoZSBmb2xsb3dpbmcgc2NyaXB0IHVzZXMgYFJTZWxlbml1bWAgdG8gcG9zdCBhbGwgYXRobGV0ZXMnIGxvZ2dlZCBhY3Rpdml0aWVzIHRvIEZTdXJnZSdzIFNvY2lhbCBXYWxsLiANCg0KSXQgY2FuIGJlIGltcG9ydGVkIGluIFdpbmRvd3MgVGFzayBzY2hlZHVsZXIgdG8gYXV0b21hdGljYWxseSB1cGxvYWQgYWN0aXZpdGllcyBvbiBzcGVjaWZpYyB0aW1lcG9pbnRzIChlLmcuLCBldmVyeSAxMCBtaW51dGVzOyBzZWUgdGhpcyBbUi1ibG9nZ2VycyBwb3N0XShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8yMDE4LzEwL2hvdy10by1ydW4tci1mcm9tLXRoZS10YXNrLXNjaGVkdWxlci8jOn46dGV4dD1SdW5uaW5nJTIwUiUyMGZyb20lMjB0aGUlMjBUYXNrJTIwU2NoZWR1bGVyJnRleHQ9UHJlc3NpbmclMjB0aGUlMjB3aW5kb3dzJTIwa2V5JTJDJTIwZm9sbG93ZWQsdGhlJTIwbmFtZSUyMG9mJTIweW91ciUyMHRhc2spKS4gVGhpcyBtYXkgYWxzbyBiZSBzY2hlZHVsZWQgZnJvbSBSIGl0c2VsZiAoc2VlIFtoZXJlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGFza3NjaGVkdWxlUi92aWduZXR0ZXMvdGFza3NjaGVkdWxlUi5odG1sKSkuDQoNCmBgYHtyLCBldmFsPUZ9DQojY2xlYW4gZW52aXJvbm1lbnQNCnJtKGxpc3Q9bHMoKSkNCiNnYXJiYWdlIGNvbGxlY3QNCmdjKCkNCiNraWxsIGphdmEgaW5zdGFuY2VzIHdpdGhpbiBSc3R1ZGlvDQpzeXN0ZW0oInRhc2traWxsIC9pbSBqYXZhLmV4ZSAvZiIsIGludGVybj1GQUxTRSwgaWdub3JlLnN0ZG91dD1GQUxTRSkNCg0KI3NldHdkIHRvIGZvbGRlciBjb250YWluaW5nIHNjcmlwdA0Kd2QgPC0gIkM6L1VzZXJzL1JvYi9Eb2N1bWVudHMvR2l0SHViL0ZpbmFsU3VyZ2VfZXhwZXJpbWVudCIgDQpzZXR3ZCh3ZCkNCg0KI2xvYWQgcGFja2FnZXMNCiNpbnN0YWxsLnBhY2thZ2VzKCJSU2VsZW5pdW0iKQ0KI2luc3RhbGwucGFja2FnZXMoIm5ldHN0YXQiKQ0KbGlicmFyeShSU2VsZW5pdW0pDQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KG5ldHN0YXQpDQpsaWJyYXJ5KHBpbmdyKQ0KDQojZmluZCBhIGZyZWUgcG9ydA0KcG9ydCA8LSBuZXRzdGF0OjpmcmVlX3BvcnQocmFuZG9tPVQpDQojcGluZyBwb3J0IGZvciBjb25maXJtYXRpb24NCiNwaW5ncjo6cGluZ19wb3J0KCJsb2NhbGhvc3QiLCBwb3J0KQ0KDQojc2V0IHVwIHNlbGVuaXVtIHNlcnZlciBhbmQgYnJvd3Nlcg0KI3JzRHJpdmVyKCkgdHJpZXMgdG8gbG9hZCBsYXRlc3QgQ2hyb21lIGRyaXZlcmVzOyBzbyBzZXQgdG8gTlVMTCEhIQ0KckQgPC0gcnNEcml2ZXIoYnJvd3Nlcj0iZmlyZWZveCIsIHBvcnQ9cG9ydCwgdmVyYm9zZT1GLCBjaHJvbWV2ZXI9TlVMTCkNCnJlbURyIDwtIHJEW1siY2xpZW50Il1dDQojcGluZ3I6OnBpbmdfcG9ydCgibG9jYWxob3N0IiwgcG9ydCkgI2NoZWNrDQoNCiNsb2FkIGxvZ2luIGF0dHJpYnV0ZXMgZnJvbSBgc2VjcmV0YC1mb2xkZXIsIGNvbnRhaW5pbmcgYXR0cmlidXRlcyByZXF1aXJlZCBmb3IgbG9naW46DQojZW1haWwsIHBhc3N3b3JkLCANCmxvYWQoIi4vbG9jYWwvYWNjb3VudHMuUiIpICMgYWxsIHByZS1zZXQgYWNjb3VudHMNCg0KI29ubHkgdGFrZSBhY2NvdW50cyB0aGF0IGFyZSBhc3NpZ25lZCBhIHNvY2lhbCB3YWxsDQpkZiA8LSBhY2NvdW50c1t3aGljaChhY2NvdW50cyRzb2NpYWx3YWxsPT0xKSxdICNzb2NpYWwgd2FsbCBwYXJ0aWNpcGFudHMNCg0KI2FuZCBvbmx5IHRha2UgYWNjb3VudHMgdGhhdCBhcmUgb2NjdXBpZWQNCmRmIDwtIGRmW3doaWNoKGRmJG9jY3VwaWVkPT1UKSxdDQoNCiNzaHVmZmxlIHJvd3MuLi4NCmRmIDwtIGRmW3NhbXBsZSgxOm5yb3coZGYpKSwgXQ0KDQojbm8uIG9mIGF0aGxldGVzIG4NCihuIDwtIG5yb3coZGYpKQ0KDQpmb3IgKGkgaW4gMTpuKSB7DQoNCiAgdHJ5ID0gMA0KICBzdWNjZXNzIDwtIEZBTFNFDQogIA0KICB3aGlsZSAoIXN1Y2Nlc3MgJiB0cnk8NSkgew0KICAgIA0KICAgIHRyeT10cnkrMQ0KICAgIA0KICAgIHRyeUNhdGNoKCB7DQogICAgICANCiAgICAgICNsb2dpbg0KICAgICAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL2xvZ2luLmNzaHRtbCIpDQogICAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gImxvZ2luX25hbWUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGRmJGVtYWlsW2ldKSkNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAibG9naW5fcGFzc3dvcmQiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KGRmJHBhc3dvb3JkW2ldKSkNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50cygiY2xhc3MgbmFtZSIsICJidG4iKVtbMV1dJGNsaWNrRWxlbWVudCgpDQogICAgICANCiAgICAgICNuYXZpZ2F0ZSB0byB3b3Jrb3V0IHJlcG9ydA0KICAgICAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1dvcmtvdXRSZXBvcnQuY3NodG1sIikNCiAgICAgIA0KICAgICAgI2FjdGl2aXRpZXMgcGVyZm9ybWVkIGluIGxhc3QgNSBkYXlzIGNhbiBiZSBwb3N0ZWQgdG8gc29jaWFsIHdhbGwNCiAgICAgIHRvZGF5IDwtIGFzLkRhdGUoZm9ybWF0KFN5cy5EYXRlKCkpKQ0KICAgICAgbXlkYXRlIDwtIHRvZGF5LTUNCiAgICAgIG15RlNkYXRlIDwtIGZvcm1hdChteWRhdGUsICIlbS8lZC8lWSIpICNjb252ZXJ0IHRvIGZpbmFsIHN1cmdlIGZvcm1hdA0KICAgICAgDQogICAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZyA9ICJpZCIsIHZhbHVlID0gIldvcmtvdXREYXRlIikkY2xlYXJFbGVtZW50KCkNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gImlkIiwgdmFsdWUgPSAiV29ya291dERhdGUiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KG15RlNkYXRlLCBrZXk9ImVudGVyIikpDQogICAgICBTeXMuc2xlZXAoMykNCiAgICAgICNnZXQgdGhlIGFjdGl2aXRpZXMgcGVyZm9ybWVkDQogICAgICAjcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIiwgInRhYmxlIikkaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgICBwYWdlIDwtIHJlbURyJGdldFBhZ2VTb3VyY2UoKSAjZ2V0IHNvdXJjZQ0KICAgICAgDQogICAgICAjIyNwYWdlW1syXV0jdGhpcyBpcyBhIHRlc3Q6IHRoaXMgd2lsbCBnaXZlIGFuIGVycm9yISENCiAgICAgIA0KICAgICAgI2FuZCBwdXQgaW4gZGF0YWZyYW1lDQogICAgICBmc190YWJsZSA8LSBwYWdlW1sxXV0gJT4lDQogICAgICAgIHJlYWRfaHRtbCgpICU+JQ0KICAgICAgICBodG1sX25vZGVzKCd0YWJsZScpICU+JSANCiAgICAgICAgLltbMV1dICU+JQ0KICAgICAgICBodG1sX3RhYmxlKCkgJT4lDQogICAgICAgIGFzLmRhdGEuZnJhbWUoLikgJT4lIA0KICAgICAgICAjcmVtb3ZlIGxhc3Qgcm93ICh0b3RhbCkNCiAgICAgICAgLlstbnJvdyguKSxdDQogICAgICANCiAgICAgICNleGNsdWRlIGFjdGl2aXRpZXMgdGhhdCBjYW5ub3QgYmUgcG9zdGVkIG9uIHRoZSB3YWxsDQogICAgICAjaS5lLiwgd2l0aCBubyB0aW1lDQogICAgICBmc190YWJsZSA8LSBmc190YWJsZVtmc190YWJsZSRXZXJrZWxpamshPSIiLF0NCiAgICAgIA0KICAgICAgI2Fsc28gZXhjbHVkZSB3YXJtdXAgYW5kIGNvb2xkb3duDQogICAgICBmc190YWJsZSA8LSBmc190YWJsZVtmc190YWJsZSRBY3Rpdml0ZWl0IT0iV2FybWluZy11cCIgJiBmc190YWJsZSRBY3Rpdml0ZWl0IT0iQ29vbGRvd24iLF0NCiAgICAgIA0KICAgICAgaWYoIk5BIiAlaW4lIHJvd25hbWVzKGZzX3RhYmxlKSkgew0KICAgICAgICBmc190YWJsZSA8LSBmc190YWJsZVstbnJvdyhmc190YWJsZSksXQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjdGhlIHdvcmtvdXQgcmVwb3J0IGRvZXMgbm90IGNvbnRhaW4gdGhlIGZ1bGwgd29ya291dCBkZXNjcmlwdGlvbi4uLg0KICAgICAgI3RoYXQgaXMsIHRoZSBuYW1lIGNvbWJpbmVkIHdpdGggdGhlIGRlc2NyaXB0aW9uLg0KICAgICAgI2JlY2F1c2UgdGhlcmUgaXMgYSBsaW1pdCBvbiB0aGUgbnVtYmVyIG9mIGNoYXJhY3RlcnMgaW4gdGhlIHdvcmtvdXQgcmVwb3J0Li4uLg0KICAgICAgI3NjcmFwZSBmdWxsIGRlc2NyaXB0aW9uIGJ5IG5hdmlnYXRpbmcgdG8gJ3dvcmtvdXQgZGV0YWlscycgbGluaw0KICAgICAgDQogICAgICAjdGhlIGRlc2NyaXB0aW9uIG1heSBhbHNvIGNvbnRhaW4gc3Vic3RyaW5nICJ1cGRhdGVkIHRvIHNvY2lhbCB3YWxsIiBpZiBhY3Rpdml0aWVzDQogICAgICAjd2VyZSBhbHJlYWR5IHNoYXJlZCAoaSBhZGQgdGhpcyBsYXRlciBvbik7IHRoZXNlIGFjdGl2aXRpZXMgc2hvdWxkIG9mIGNvdXJzZSBub3QgYmUgDQogICAgICAjc2hhcmVkIGFnYWluLi4uDQogICAgICANCiAgICAgIGlmKG5yb3coZnNfdGFibGUpPjApIHsgIyBvbmx5IGlmIGF0aGxldGUgaGFzIHNoYXJlYWJsZSB3b3Jrb3V0cy4uLg0KICAgICAgICANCiAgICAgICAgI2ZpcnN0IGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSAnd29ya291dCBkZXNjcmlwdGlvbicNCiAgICAgICAgZnNfdGFibGUkZGVzY3JpcHRpb24gPC0gTkENCiAgICAgICAgDQogICAgICAgICNhbHNvIGFkZCBhIHZhcmlhYmxlIGluZGljYXRpbmcgd2hldGhlciBhY3Rpdml0eSBpcyBhbHJlYWR5IHBvc3RlZCBvciBub3QNCiAgICAgICAgZnNfdGFibGUkcG9zdGVkIDwtIDANCiAgICAgICAgDQogICAgICAgICNnZXQgaW5uZXIgaHRtbCBvZiB3b3Jrb3V0IHJlcG9ydA0KICAgICAgICAjcmVtRHIkZmluZEVsZW1lbnQoImNsYXNzIiwgInRhYmxlIikkaGlnaGxpZ2h0RWxlbWVudCgpDQogICAgICAgIGlubmVyIDwtIHJlbURyJGZpbmRFbGVtZW50KCJjbGFzcyIsICJ0YWJsZSIpJGdldEVsZW1lbnRBdHRyaWJ1dGUoImlubmVySFRNTCIpDQogICAgICAgIA0KICAgICAgICAjZ2V0IHVybHMgY29ycmVzcG9uZGluZyB0byBhY3Rpdml0aWVzIGluIHdvcmtvdXQgcmVwb3J0DQogICAgICAgICNhbmQgcHV0IGluIHZlY3Rvcg0KICAgICAgICB1cmwgPC0gcmVhZF9odG1sKGlubmVyW1sxXV0pICU+JSAjcmVhZCBodG1sDQogICAgICAgICAgaHRtbF9ub2RlcygidGQiKSAlPiUgI2dldCBkYXRhIGNlbGxzICh0ZCB0YWdzKQ0KICAgICAgICAgIC5bZ3JlcGwoICJXb3Jrb3V0RGV0YWlscyIsIC4sIGZpeGVkID0gVFJVRSldICU+JSAjZXh0cmFjdCAiV29ya291dERldGFpbHMiIHVybHMNCiAgICAgICAgICBzdHJfZXh0cmFjdF9hbGwoLiwiKD88PWEgaHJlZj0pLisoPz10YXJnZXQ9KSIpICU+JQ0KICAgICAgICAgIHVubGlzdCguKSAlPiUNCiAgICAgICAgICBzdWJzdHJpbmcoLiwxNikgJT4lDQogICAgICAgICAgc3Vic3RyaW5nKC4sMSxuY2hhciguKS0yKQ0KICAgICAgICANCiAgICAgICAgZm9yICh4IGluIDE6bnJvdyhmc190YWJsZSkpIHsgI2ZvciBhY3Rpdml0eSB4DQogIA0KICAgICAgICAgICNnZXQgZnVsbCBsaW5rDQogICAgICAgICAgbGluayA8LSBwYXN0ZTAoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1dvcmtvdXRBZGQiLCB1cmxbYXMubnVtZXJpYyhyb3duYW1lcyhmc190YWJsZSkpXVt4XSkNCiAgICAgICAgICANCiAgICAgICAgICAjbmF2aWdhdGUNCiAgICAgICAgICByZW1EciRuYXZpZ2F0ZShsaW5rKQ0KICAgICAgICAgIFN5cy5zbGVlcCgxKQ0KICAgICAgICAgIA0KICAgICAgICAgICNzY3JhcGUgd29ya291dCBkZXNjcmlwdGlvbg0KICAgICAgICAgIGRlc2MgPC0gcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJEZXNjIikkZ2V0RWxlbWVudEF0dHJpYnV0ZSgidmFsdWUiKVtbMV1dDQogICAgICAgICAgZnNfdGFibGUkZGVzY3JpcHRpb25beF0gPC0gZGVzYw0KICAgICAgICAgIA0KICAgICAgICAgICNhbmQgbmFtZQ0KICAgICAgICAgIG5hbWUgPC0gcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJOYW1lIikkZ2V0RWxlbWVudEF0dHJpYnV0ZSgidmFsdWUiKVtbMV1dDQogICAgICAgICAgZnNfdGFibGUkV29ya291dFt4XSA8LSBuYW1lDQogICAgICAgICAgDQogICAgICAgICAgI2FuZCBub3Rlcw0KICAgICAgICAgICNyZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAidy1ib3gtaGVhZGVyIilbWzJdXSRoaWdobGlnaHRFbGVtZW50KCkNCiAgICAgICAgICAjZm9yIHNvbWUgcmVhc29uLCB0aGUgdGV4dGJveCBtYXkgYWxyZWFkeSBiZSBvcGVuZWQuIFNvLCBjbGljayBvbmx5IGlmIHRoYXQgaXMgbm90IHRoZSBjYXNlDQogICAgICAgICAgaWYocmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInctYm94LWNvbnRlbnQiKVtbMl1dJGdldEVsZW1lbnRUZXh0KClbWzFdXSA9PSAiIikgew0KICAgICAgICAgICAgcmVtRHIkZmluZEVsZW1lbnRzKCJjbGFzcyBuYW1lIiwgInctYm94LWhlYWRlciIpW1syXV0kY2xpY2tFbGVtZW50KCkgfQ0KICAgICAgICAgIA0KICAgICAgICAgIG5vdGUgPC0gcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJQb3N0RGVzYyIpJGdldEVsZW1lbnRBdHRyaWJ1dGUoInZhbHVlIilbWzFdXQ0KICAgICAgICAgIGZzX3RhYmxlJE5vdGl0aWVzW3hdIDwtIG5vdGUNCiAgICAgICAgICANCiAgICAgICAgICAjaWYgdGhlIGRlc2NyaXB0aW9uIGNvbnRhaW5zICJwb3N0ZWQgdG8gc29jaWFsIHdhbGwiLCBwb3N0ZWQ9MQ0KICAgICAgICAgIGZzX3RhYmxlJHBvc3RlZFt4XSA8LSBpZmVsc2UoVCAlaW4lIGdyZXBsKCJHZXBvc3Qgb3AgZGUgU29jaWFsIFdhbGwhIiwgZnNfdGFibGUkZGVzY3JpcHRpb25beF0sIGZpeGVkID0gVFJVRSksIDEsIGZzX3RhYmxlJHBvc3RlZFt4XSkNCiAgICAgICAgfSANCiAgICAgICANCiAgICAgICAgI3JlbW92ZSBlbXB0eSBzdHJpbmdzIHdpdGggTkENCiAgICAgICAgZnNfdGFibGUkV29ya291dCA8LSBpZmVsc2UoZnNfdGFibGUkV29ya291dD09IiIsIE5BLCBmc190YWJsZSRXb3Jrb3V0KQ0KICAgICAgICBmc190YWJsZSRkZXNjcmlwdGlvbiA8LSBpZmVsc2UoZnNfdGFibGUkZGVzY3JpcHRpb249PSIiLCBOQSwgZnNfdGFibGUkZGVzY3JpcHRpb24pDQogICAgICAgIGZzX3RhYmxlJEdldm9lbCA8LSBpZmVsc2UoZnNfdGFibGUkR2V2b2VsPT0iIiwgTkEsIGZzX3RhYmxlJEdldm9lbCkNCiAgICAgICAgZnNfdGFibGUkTm90aXRpZXMgPC0gaWZlbHNlKGZzX3RhYmxlJE5vdGl0aWVzPT0iIiwgTkEsIGZzX3RhYmxlJE5vdGl0aWVzKQ0KICAgICAgICANCiAgICAgICAgI2lmIG5ldyBhY3Rpdml0aWVzIHRvIHNoYXJlLi4uDQogICAgICAgIHNoYXJlIDwtIHdoaWNoKGZzX3RhYmxlJHBvc3RlZD09MCkNCiAgICAgICAgDQogICAgICAgIGlmKGxlbmd0aChzaGFyZSk+MCkgew0KICAgICAgICAgIFN5cy5zbGVlcCgxKQ0KICAgICAgICAgICN0byB0aGUgc29jaWFsIHdhbGwhDQogICAgICAgICAgcmVtRHIkbmF2aWdhdGUoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1NvY2lhbFdhbGwvIikNCiAgICAgICAgICBTeXMuc2xlZXAoLjUpDQogICAgICAgICAgI2FncmVlICBvbiAiYWR2aXNvcnkgb24gc2hhcmluZyINCiAgICAgICAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiYnRuIilbWzFdXSRjbGlja0VsZW1lbnQoKQ0KICAgICAgICAgIA0KICAgICAgICAgIGZvciAoaiBpbiB1bmlxdWUoc2hhcmUpKSB7DQogICAgICAgICAgICByZW1EciRuYXZpZ2F0ZSgiaHR0cHM6Ly9sb2cuZmluYWxzdXJnZS5jb20vU29jaWFsV2FsbC8iKQ0KICAgICAgICAgICAgU3lzLnNsZWVwKC41KQ0KICAgICAgICAgICAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnY2xhc3MnLCdzb2NpYWwtd29ya291dCcpJGNsaWNrRWxlbWVudCgpDQogICAgICAgICAgICBTeXMuc2xlZXAoLjUpDQogICAgICAgICAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIiwgIlNoYXJlV29ya291dCIpW1tqXV0kY2xpY2tFbGVtZW50KCkNCiAgICAgICAgICANCiAgICAgICAgICAgICNiZWZvcmUgcHVibGlzaGluZywgYWRkIGEgZGVzY3JpcHRpb24gKHdvcmtvdXQgbm90ZSksIGJhc2VkIG9uIGBmc190YWJsZWAvd29ya291dHJlcG9ydA0KICAgICAgICAgICAgDQogICAgICAgICAgICAjZ2V0IGFjdGl2aXR5IGZyb20gZnNfdGFibGUgY29ycmVzcG9uZGluZyB0byB0aGlzIHdvcmtvdXQNCiAgICAgICAgICAgIGFjdGl2aXR5IDwtIGZzX3RhYmxlW2osXQ0KICAgICAgICAgICAgDQogICAgICAgICAgICBpZighaXMubmEoYWN0aXZpdHkkV29ya291dCkpIHsNCiAgICAgICAgICAgICAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmc9ImlkIiwgdmFsdWU9InNvY2lhbHBvc3QiKSRzZW5kS2V5c1RvRWxlbWVudChsaXN0KHBhc3RlMCgiQWN0aXZpdGVpdDogIiwgYWN0aXZpdHkkV29ya291dCwgIlxuXG4iKSkpDQogICAgICAgICAgICB9DQogICAgICAgICAgICBpZighaXMubmEoYWN0aXZpdHkkZGVzY3JpcHRpb24pKSB7DQogICAgICAgICAgICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nPSJpZCIsIHZhbHVlPSJzb2NpYWxwb3N0Iikkc2VuZEtleXNUb0VsZW1lbnQobGlzdChwYXN0ZTAoIkJlc2NocmlqdmluZzogIiwgYWN0aXZpdHkkZGVzY3JpcHRpb24sICJcblxuIikpKQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgaWYoIWlzLm5hKGFjdGl2aXR5JE5vdGl0aWVzKSkgew0KICAgICAgICAgICAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZz0iaWQiLCB2YWx1ZT0ic29jaWFscG9zdCIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QocGFzdGUwKCJOb3RpdGllczogIiwgYWN0aXZpdHkkTm90aXRpZXMpKSkNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgICNwdWJsaXNoDQogICAgICAgICAgICByZW1EciRmaW5kRWxlbWVudHMoImNsYXNzIG5hbWUiLCAiYnRuIilbWzVdXSRjbGlja0VsZW1lbnQoKQ0KICAgICAgICAgICAgDQogICAgICAgICAgICBTeXMuc2xlZXAoMykNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgI25vdyBnbyBiYWNrIHRvIHRoZSB3b3Jrb3V0IGRldGFpbHMgdG8gYWRkIHN1YnN0cmluZyAicG9zdGVkIHRvIHNvY2lhbCB3YWxsIg0KICAgICAgICAgICAgI3RvIGRlc2NyaXB0aW9uIHRvIHByZXZlbnQgYWN0aXZpdHkgZnJvbSBiZWluZyBzaGFyZWQgYWdhaW4hDQogICAgICAgICAgICByZW1EciRuYXZpZ2F0ZShwYXN0ZTAoImh0dHBzOi8vbG9nLmZpbmFsc3VyZ2UuY29tL1dvcmtvdXRBZGQvIiwgIHVybFthcy5udW1lcmljKHJvd25hbWVzKGZzX3RhYmxlKSlbal1dKSkNCiAgICAgICAgICAgIFN5cy5zbGVlcCgxKQ0KICAgICAgICAgICAgZGVzIDwtIHJlbURyJGZpbmRFbGVtZW50KCJpZCIsICJEZXNjIikkZ2V0RWxlbWVudFRleHQoKQ0KICAgICAgICAgICAgaWYoIWdyZXBsKCJHZXBvc3Qgb3AgZGUgU29jaWFsIFdhbGwhIiwgZGVzKSkgew0KICAgICAgICAgICAgICByZW1EciRmaW5kRWxlbWVudCh1c2luZz0iaWQiLCB2YWx1ZT0iRGVzYyIpJHNlbmRLZXlzVG9FbGVtZW50KGxpc3QoIlxuXG5HZXBvc3Qgb3AgZGUgU29jaWFsIFdhbGwhIikpDQogICAgICAgICAgICAgIFN5cy5zbGVlcCguMikNCiAgICAgICAgICAgICAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAiaWQiLCB2YWx1ZSA9ICJzYXZlQnV0dG9uIikkY2xpY2tFbGVtZW50KCkNCiAgICAgICAgICAgIH0NCiAgICANCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0NCiAgICAgICNsb2dvdXQNCiAgICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ1VpdGxvZ2dlbicpJGNsaWNrRWxlbWVudCgpDQogICAgICBTeXMuc2xlZXAoLjUpDQogICAgICANCiAgICAgICMgYW5kIHJlLWxvZyBpbiB3aXRoIG5ldyBhY2NvdW50Li4uKQ0KICAgICAgcmVtRHIkZmluZEVsZW1lbnQodXNpbmcgPSAnbGluayB0ZXh0JywnTG9nIGluIGFjY291bnQnKSRjbGlja0VsZW1lbnQoKQ0KICAgICAgU3lzLnNsZWVwKDEpDQogICAgICANCiAgICAgICNpZiB0aGUgc2NyaXB0IHN1Y2NlZWRlZCwgd2UncmUgYXQgdGhlIGxvZ2luIHBhZ2UgYWdhaW4hDQogICAgICBpZihyZW1EciRnZXRUaXRsZSgpW1sxXV09PSJGaW5hbCBTdXJnZSAtIElubG9nZ2VuIikgc3VjY2VzcyA8LSBUUlVFDQogICAgfSwgDQogICAgZXJyb3IgPSBmdW5jdGlvbihlKSB7ICNpZiBhbiBlcnJvciBpcyBjYXVnaHQsIGxvZ291dCANCiAgICAgIHJlbURyJGZpbmRFbGVtZW50KHVzaW5nID0gJ2xpbmsgdGV4dCcsJ1VpdGxvZ2dlbicpJGNsaWNrRWxlbWVudCgpDQogICAgfSkgDQogIH0NCiAgDQp9DQoNCiN0ZXJtaW5hdGUgcHJvY2Vzc2VzDQpwaWQgPC0gckQkc2VydmVyJHByb2Nlc3MkZ2V0X3BpZCgpI2dldCBwcm9jZXNzIGlkDQpzeXN0ZW0ocGFzdGUwKCJUYXNra2lsbCAvRiAvVCIgLCIgL1BJRCAiLCBwaWQpKQ0KI3BpbmdyOjpwaW5nX3BvcnQoImxvY2FsaG9zdCIsIHBvcnQpICNjaGVjaw0KDQpybShsaXN0PWxzKCkpDQpnYygpDQpxdWl0KCkNCm4NCmBgYA0K


Copyright © 2023 Rob Franken