If you just want the code, and not the explanation, look here.
The trick is to use the Stack Exchange API to download the information on a user. For example, to get my own user information (regarding tex.se), I would go to http://api.stackexchange.com/2.2/users?inname=digitalis_&site=tex
. For a different user, or a different site, you would change the respective fields.
Let's put this into a function, 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
where cache_name
is
cache_name = function (user, site)
return ('cache/' .. user .. '_' .. site)
end
Because the API limits you to 300 requests per day, it's a bad idea to re-download the user information every time you need it. The solution is to cache the data, in two ways:
Per document build. E.g., the first time I write \texrep
, it downloads the data and stores it in a variable, so that subsequent occurrences of \texrep
use the value stored in memory.
On the hard drive. E.g., calling \texrep
the first time downloads the data and saves it to a file on the hard drive (say, digitalis_tex
), and subsequent document builds read the value from this file.
curl_user
already deals with (1) by saving the data in the cache/
directory, under I file with the name <user>_<site>
.
A consequence of (2) is that there will need to be a variable which determines whether Lua should re-download or not. We will set it to true
for now, as well as creating the table which caches data 'per build'.
UPDATING = true
cache = {}
A function is needed to manage this 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
where extract_user
is defined as
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
chooses whether to update the saved cache on the hard drive, and uses some pattern matching magic to filter out users which are not wanted. This is needed because of how the API request works: it actually gives you the data of all users whose names have a particular string in them. To differentiate from other users, you need the user's ID (which you can find by looking at the url when you visit your profile on http://stackexchange.com).
You can see how the cache starts to evolve as a kind of tree:
cache
└── digitalis
├── tex
│ └── _raw
└── stackoverflow
└── _raw
All that is now desired is a general function to acquire a value, given:
Here is such a function:
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
It initialises the cache if parts of it are not defined, and returns the desired value.
To get the effect in the question, one may write
get_rep = function (site)
return get_field('reputation', USER, USERID, site)
end
where USER
and USERID
are set to some user's username and user ID.
USER = 'digitalis'
USERID = 5477562
The entire .tex file would then be
\directlua{dofile('fetch.lua')}
\def\texrep{\directlua{get_rep('tex')}}
My current reputation on {\tt tex.se} is \texrep.
\bye
Note that the functions defined here are general enough to be used for more than just getting the reputation of the user: e.g.,
- the number of badges,
- the change in reputation in the last year/quarter/month,
- the url to the profile image,
- etc.
are all possible.
{\tt foo}
,{\bf foo}
,{\it foo}
etc. commands are 20+ years deprecated.{\ttfamily foo}
,{\bfseries foo}
,{\itshape foo}
or\texttt{foo}
,\textbf{foo}
,\textit{foo}
are strictly encouraged. Self-answering is also explicitly acceptable on Stackexchange, although admittedly some people (and some stacks) don't like it, but you certainly don't have to justify yourself, I thought this was cool :)\tt
because I was using Plain (Lua)TeX, to make the question as general as possible (so it would also apply to ConTeXt etc.): but you are right!