将实时 Stack Exchange 声誉置于文档中

将实时 Stack Exchange 声誉置于文档中

我希望能够当前的声誉写入文档中,这样,当我重建文档时,显示的声誉就会更新。

例如,我可能会写

My current reputation on {\tt tex.se} is \texrep.

并得到(假设\texrep已定义)

我目前在 tex.se 上的声誉是 43。

为此,我希望\texrep将其定义为\directlua{get_rep('tex')},其中get_rep是在外部Lua脚本中定义的Lua函数(例如fetch.lua)。


注意:我已经回答了我自己的问题,原因有二:

  1. 看看我的方法是否可以改进;
  2. 与他人分享这一“发现”。

更新:我的回答相当长,并且带来了一个不良副作用:阻塞了页面上的所有其他内容!确保检查其他答案低于我的,因为他们改进了我的方法。

答案1

如果你只想要代码,而不是解释,请查看这里


诀窍是使用堆栈交换 API下载用户信息。例如,要获取我自己的用户信息(关于 tex.se),我会转到http://api.stackexchange.com/2.2/users?inname=digitalis_&site=tex。对于不同的用户或不同的网站,您需要更改相应的字段。

让我们把它放入一个函数中curl_user

curl_user = function (user, site)
   os.execute('mkdir -p cache')
   print('\nGetting ' .. user .. ' from ' .. site .. '...\n')
   os.execute('curl "http://api.stackexchange.com/2.2/users?inname='
      .. user .. '&site=' .. site
      .. '" > ' .. cache_name(user, site) .. '.gz')
   os.execute('gunzip -f ' .. cache_name(user, site) .. '.gz')
end

哪里cache_name

cache_name = function (user, site)
   return ('cache/' .. user .. '_' .. site)
end

由于 API 限制您每天最多可请求 300 次,因此每次需要时重新下载用户信息不是一个好主意。解决方案是缓存数据,有两种方式:

  1. 根据文档构建。例如,我第一次写入时\texrep,它会下载数据并将其存储在变量中,以便后续出现时\texrep使用存储在内存中的值。

  2. 在硬盘上。例如,\texrep第一次调用会下载数据并将其保存到硬盘上的文件中(例如digitalis_tex),后续的文档构建会从该文件中读取值。

curl_user已经通过将数据保存在cache/目录中来处理(1),在名为的 I 文件下<user>_<site>

(2) 的结果是,需要一个变量来确定 Lua 是否应该重新下载。我们true现在将其设置为,并创建“每个构建”缓存数据的表。

UPDATING = true
cache = {}

需要一个函数来管理这个缓存:

get_user = function (user, userid, site)
   if cache[user] == nil then
      cache[user] = {}
   end

   if cache[user][site] == nil then
      cache[user][site] = {}
      cache[user][site]['_raw'] = extract_user(user, userid, site)
   end

   return cache[user][site]['_raw']
end

其中extract_user定义为

extract_user = function (user, userid, site)
   if UPDATING then
      curl_user(user, site)
   end

   io.input(cache_name(user, site))
   raw = io.read('*all')
   me = string.match(raw, userid .. '.*')
   return me
end

extract_user选择是否更新硬盘上保存的缓存,并使用一些模式匹配魔法来过滤掉不想要的用户。这是因为 API 请求的工作方式:它实际上为您提供了所有名称中包含特定字符串的用户的数据。为了与其他用户区分开来,您需要用户的 ID(您可以在访问个人资料时查看 URL 来找到它http://stackexchange.com)。

您可以看到缓存如何开始演变成一种树:

cache
 └── digitalis
      ├── tex
      │    └── _raw
      └── stackoverflow
           └── _raw

现在所需要的是一个获取值的一般函数,给定:

  • 一片田野,
  • 用户,
  • 网站。

这是一个这样的函数:

get_field = function (field, user, userid, site)
   if not (cache[user]
          and cache[user][site]
          and cache[user][site][field]) then
      me = get_user(user, userid, site)
      pure = string.match(me, '"' .. field .. '":[^,}]+')
      cache[user][site][field] = string.match(pure, '[^:]+$')
   end
   return cache[user][site][field]
end

如果缓存的某些部分未定义,它将初始化缓存并返回所需的值。


为了得到问题的效果,可以写

get_rep = function (site)
   return get_field('reputation', USER, USERID, site)
end

其中USERUSERID设置为某些用户的用户名和用户 ID。

USER = 'digitalis'
USERID = 5477562

整个 .tex 文件将是

\directlua{dofile('fetch.lua')}
\def\texrep{\directlua{get_rep('tex')}}
My current reputation on {\tt tex.se} is \texrep.
\bye

请注意,此处定义的函数足够通用,不仅仅可以用于获取用户的声誉:例如,

  • 徽章数量,
  • 过去一年/一季度/一月的声誉变化,
  • 个人资料图片的 URL,
  • ETC。

都是可能的。

答案2

以下是该命令的 ConTeXt 实现。我使用了一个稍微不同的 API 调用获得声誉。

ConTeXt 有一个内置机制来下载远程内容。此内容缓存在 目录中texmf-cache。因此,想法是简单地下载内容、解压缩、从 json 转换为 lua 表,然后访问键 的值reputation。LuaTeX 附带gzip库,而 ConTeXt 有一个 JSON 解析器;因此根本不需要使用外部程序。

\usemodule[json]
\startluacode
  local get_user_reputation = function(user)
    local url           = string.format("http://api.stackexchange.com//2.2/users/%s?order=desc&sort=reputation&site=tex", user)
    local specification = resolvers.splitmethod(url)
    local filename      = resolvers.finders['http'](specification) or ""

    local gz = gzip.open(filename, "rb")
    local str = ""
    if gz then
        str = gz:read("*all")
        gz:close()
    end
    local js  = utilities.json.tolua(str)
    local rep = js["items"][1]["reputation"] or "0"
    context(rep)
  end

  interfaces.definecommand {
      name = "getrep",
      arguments = {
        { "option", "number" }, 
      },
      macro = get_user_reputation,
  }

\stopluacode

\starttext

My current reputation on \type{tex.se} is \getrep[323].

\stoptext

由于 JSON 对象返回了大量信息,因此您也可以提取其他键。例如,在我的情况下,这是我获得的信息:

t={
 ["has_more"]=false,
 ["items"]={
  {
   ["accept_rate"]=88,
   ["account_id"]=65687,
   ["badge_counts"]={
    ["bronze"]=188,
    ["gold"]=2,
    ["silver"]=82,
   },
   ["creation_date"]=1280861336,
   ["display_name"]="Aditya",
   ["is_employee"]=false,
   ["last_access_date"]=1466999559,
   ["last_modified_date"]=1464661963,
   ["link"]="http://tex.stackexchange.com/users/323/aditya",
   ["profile_image"]="https://www.gravatar.com/avatar/9734cf7ba55eda3a1707cd61a452bdce?s=128&d=identicon&r=PG&f=1",
   ["reputation"]=44190,
   ["reputation_change_day"]=10,
   ["reputation_change_month"]=1060,
   ["reputation_change_quarter"]=1612,
   ["reputation_change_week"]=50,
   ["reputation_change_year"]=3347,
   ["user_id"]=323,
   ["user_type"]="registered",
   ["website_url"]="http://randomdeterminism.wordpress.com/",
  },
 },
 ["quota_max"]=300,
 ["quota_remaining"]=285,
}

默认情况下,如果缓存超过 1 天,ConTeXt 将再次获取数据。如果您想要更快地更新(请注意 stackexchange API 中有速率限制),请添加:

\enabledirectives[schemes.threshold=1200]

这将在 1200 秒(即 20 分钟)后更新缓存。

相关内容