![将实时 Stack Exchange 声誉置于文档中](https://linux22.com/image/336563/%E5%B0%86%E5%AE%9E%E6%97%B6%20Stack%20Exchange%20%E5%A3%B0%E8%AA%89%E7%BD%AE%E4%BA%8E%E6%96%87%E6%A1%A3%E4%B8%AD.png)
我希望能够当前的声誉写入文档中,这样,当我重建文档时,显示的声誉就会更新。
例如,我可能会写
My current reputation on {\tt tex.se} is \texrep.
并得到(假设\texrep
已定义)
为此,我希望\texrep
将其定义为\directlua{get_rep('tex')}
,其中get_rep
是在外部Lua脚本中定义的Lua函数(例如fetch.lua
)。
注意:我已经回答了我自己的问题,原因有二:
- 看看我的方法是否可以改进;
- 与他人分享这一“发现”。
更新:我的回答相当长,并且带来了一个不良副作用:阻塞了页面上的所有其他内容!确保检查其他答案低于我的,因为他们改进了我的方法。
答案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 次,因此每次需要时重新下载用户信息不是一个好主意。解决方案是缓存数据,有两种方式:
根据文档构建。例如,我第一次写入时
\texrep
,它会下载数据并将其存储在变量中,以便后续出现时\texrep
使用存储在内存中的值。在硬盘上。例如,
\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
其中USER
和USERID
设置为某些用户的用户名和用户 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 分钟)后更新缓存。