最近朋友推荐了一个公式识别软件 SimpleTex,功能类似 Mathpix,但是免费。因为这个软件没有 Linux 版本,所以我想调用它的 API 开发一个 R 包(或许 RStudio 插件更好),目前代码卡在了鉴权这一步,特此发帖求助各位 R 友,谢谢先!

前期尝试

SimpleTex 开放平台 API 文档:https://simpletex.cn/api_doc。我按照 API 文档给的 APP ID、密钥、随机字符和时间戳生成的签名和文档里给出的结果是能对上的:

SIMPLETEX_APP_ID = "19X4f10YM1Va894nvFl89ikY"
SIMPLETEX_APP_SECRET = "fu4Wfmna4153DFN12ctBsPqgVI3vvGGK"
random_str = "mSkYSY28N4WkvidB"
timestamp = "1675550577"
use_batch = "True"
sign = glue::glue(
  "app-id={SIMPLETEX_APP_ID}&",
  "random-str={random_str}&",
  "timestamp={timestamp}&",  
  "use_batch={use_batch}&",
  "secret={SIMPLETEX_APP_SECRET}"
)
sign = digest::digest(sign, algo = "md5", serialize = FALSE)
print(sign)
#> [1] "5f271e1deccd95d467c7dd430ca2c8b1"

我的代码

# 加载 R 包
library(digest)
library(httr)

# APP ID 和 APP SECRET
SIMPLETEX_APP_ID = "XXXX"
SIMPLETEX_APP_SECRET = "XXXX"

# 生成签名信息
chars = c(letters, LETTERS, 0:9)
random_str = paste(
  sample(chars, 16, replace = TRUE),
  collapse = ""
)
timestamp = as.character(as.integer(Sys.time()))
use_batch = "True"
sign = glue::glue(
  "app-id={SIMPLETEX_APP_ID}&",
  "random-str={random_str}&",
  "timestamp={timestamp}&",  
  "use_batch={use_batch}&",
  "secret={SIMPLETEX_APP_SECRET}"
)
sign = digest(sign, algo = "md5", serialize = FALSE)

# 生成鉴权信息
header = c(
  timestamp = timestamp,
  `random-str` = random_str,
  `app-id` = SIMPLETEX_APP_ID,
  sign = sign
)

# API 地址
url = "https://server.simpletex.cn/api/latex_ocr_turbo"

# 调用 API 返回信息
response = POST(
  url = url,
  add_headers(header),
  body = list(file = upload_file("path/to/image")),
  encode = "multipart"
)

print(response)

报错信息

Response [https://server.simpletex.cn/api/latex_ocr_turbo]
  Date: 2024-06-23 19:30
  Status: 401
  Content-Type: application/json
  Size: 97 B

Status: 401表示鉴权失败。

系统环境

xfun::session_info(c("httr", "digest", "glue"))
#> R version 4.2.3 (2023-03-15 ucrt)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 19045)
#> 
#> Locale:
#>   LC_COLLATE=Chinese (Simplified)_China.utf8 
#>   LC_CTYPE=Chinese (Simplified)_China.utf8   
#>   LC_MONETARY=Chinese (Simplified)_China.utf8
#>   LC_NUMERIC=C                               
#>   LC_TIME=Chinese (Simplified)_China.utf8    
#> 
#> Package version:
#>   askpass_1.2.0   curl_5.2.1      digest_0.6.35   glue_1.7.0     
#>   graphics_4.2.3  grDevices_4.2.3 httr_1.4.7      jsonlite_1.8.8 
#>   methods_4.2.3   mime_0.12       openssl_2.2.0   R6_2.5.1       
#>   stats_4.2.3     sys_3.4.2       tools_4.2.3     utils_4.2.3

chuxinyuan 看你 README 中写了如此长一段代码:

if (!file.exists("~/.Renviron")){
  file.create("~/.Renviron")
} 

file_path = "~/.Renviron"
file = file(file_path, open = "a")

comment = "# ID and SECRET of SimpleTex"
writeLines(comment, file)

code_lines = c(
  "SIMPLETEX_APP_ID = \"Your SimpleTex API ID\"",
  "SIMPLETEX_APP_SECRET = \"Your SimpleTex API SECRET\""
)

for (code in code_lines) {
  writeLines(code, file)
}

close(file)

其实就是一句话的事情:

cat(
  '\n# ID and SECRET of SimpleTex',
  'SIMPLETEX_APP_ID = "Your SimpleTex API ID"',
  'SIMPLETEX_APP_SECRET = "Your SimpleTex API SECRET"',
  file = '~/.Renviron', sep = '\n', append = TRUE
)