lua, socket.http i WykopAPI

Przyszedł czas, żeby w końcu ruszyć się i zrobić coś konstruktywnego. Jakoś przypadkiem do głowy mi wpadła zabawa API Wykopu i napisanie klienta mikrobloga w cli. Co z tego wyjdzie na koniec? Nie wiem, nie jest to ważne, bo głównym celem projektu jest głównie nauka i zdobywanie doświadczenia. Czemu lua? Bo je lubię i chyba nikt nie pisał niczego opartego o API wykopu w lua.

Co jest potrzebne?* luasocket, *lua-cjson i md5 (wszystkie dostępne przez luarocks). I oczywiście klucze do API Wykopu.

Zanim będzie o samej implementacji w lua – możliwe problemy już na samym początku korzystania z API – każde żądanie musi być podpisane. Jak? Jeśli to GET wtedy podpisem jest suma md5 wyliczona ze stringu http://a.wykop.pl/. W przypadku POST na koniec stringu lądują oddzielone przecinkami wartości parametrów. Co ważne – kolejność ma znaczenie – wartości muszą być wg kolejności alfabetycznej kluczy.

Od czego zacząć? Ja stwierdziłem, że skoro ma to być klient mikrobloga od pobierania ostatnich wpisów. Jak to zrobić? Czytając api wykopu wiemy, że requestem jest /stream/appkey/. Dodatkowo można dodać (co w moim przypadku jest, powiedzmy, konieczne) /output/clear co usunie tagi HTML.

function request( action )
  url = apiurl .. action .. "/appkey/" .. key .. "/output/" .. output
  sign = md5.sumhexa( secret .. url )

  return url, sign
end

url, sign = request( "stream" )

W ten sposób mamy wygenerowany adres i podpis uwierzytelniający. Teraz trzeba go tylko wysłać. I tu przydatne są sockety, a dokładniej* socket.http. Prosta w użyciu biblioteka do porozumiewania się z serwerem http. Dodatkowo od razu zastosujemy *cjson, bo raw data jest całkowicie nieczytelna – na szczęście jest ładnie opisane jakie dane są w jsonie zwracane. Co jest fajnego w tej bibliotece? Jest szybka i przekształca jsona na tablicę w lua, więc dalsze wykorzystywanie danych jest banalne.
Nie możemy zapomnieć o ustawieniu odpowiednich nagłówków, bo inaczej nasze połączenie dostanie błąd id6 od serwera wykopu – czyli błędne uwierzytelnienie.

headers = { ["apisign"] = sign }
respond = {}

http.request{ headers = headers,
              url = url,
              sink = ltn12.sink.table( respond )
            }

d = cjson.decode( respond[1] )

W tej chwili mamy już dane kilkunastu wpisów z mikrobloga dostępne w tablicy d i wystarczy je wyświetlić.

s = string.format( "[#%s] %s (%s): %s", d[1]["id"], d[1]["author"], 
                   d[1]["vote_count"], d[1]["body"] )
print( s )

W ten sposób otrzymamy trochę danych o najnowszym wpisie na mikroblogu. Całość kodu wygląda tak:

#!/usr/bin/env lua
local cjson = require "cjson"
local http = require "socket.http"
local md5 = require "md5"
local ltm12 = require "ltn12"

apiurl = "http://a.wykop.pl/"
key = "KluczApiAplikacji"
secret = "KluczSekret"
output = "clear"

function request( action )
  url = apiurl .. action .. "/appkey/" .. key .. "/output/" .. output
  sign = md5.sumhexa( secret .. url )

  return url, sign
end

url, sign = request( "stream" )

headers = { ["apisign"] = sign }
respond = {}

http.request{ headers = headers,
              url = url,
              sink = ltn12.sink.table( respond )
            }

d = cjson.decode( respond[1] )

s = string.format( "[#%s] %s (%s): %s", d[1]["id"], d[1]["author"], 
                   d[1]["vote_count"], d[1]["body"] )
print( s )

Dalsza część za jakiś czas.