[Space-Lua] Open-Graph widget

To prepare myself for the transition to lua and practice some syntax along the way (and maybe out of boredom), I recently decided to implement a simple link-preview widget using the Open Graph protocol properties found in many websites.

My code consists of two functions:
og.fetch, wich extracts tags and their properties from a website
and og.card, wich uses the data returned by og.fetch to display an info card in plain html.

Some examples:


${og.card("https://xkcd.com/1272/", 300)}


${og.card("https://medium.com/@TeddyYeung/what-is-open-graph-f3abadd7d1f9", 300)}


${og.card("https://www.youtube.com/watch?v=0FBxnKxxMhU", 300)}

This is my first time writing lua and I’m sure there’s lots of room for improvement, but at least it’s self-contained and works (so far xD).

space-lua:

og = {}

function og.fetch(url) 
  local function extract_prop(header, prop_name)
    local prop_start, prop_end = string.find(header, 'og:'..prop_name..'" content="[^"]+"/?>')
    local prop = string.sub(header, prop_start, prop_end)
    local tag_closer_length = 0
    if string.find(prop, "/>") == nil then
      -- <tag prop="value">
      tag_closer_length = 2
    else
      -- <tag prop="value"/>
      tag_closer_length = 3
    end
    return string.sub(prop, 15 + string.len(prop_name), prop_end - prop_start - tag_closer_length)
  end
  
  local response = http.request(url)
  if response.ok then
    local body = response.body
    local head_start, _ = string.find(body, "<head>")
    local head_end, _ = string.find(body, "</head>")
    local header = string.sub(body, head_start, head_end)

    local title = extract_prop(header, "title")
    local url = extract_prop(header, "url")
    local image = extract_prop(header, "image")
    -- description and site_name are optional properties, might return nil/""
    local description = extract_prop(header, "description")
    local sitename = extract_prop(header, "site_name")
    
    return {title = title, url = url, image = image, description = description, sitename = sitename}
  else
    return "Error: got status code "..response.status
  end
end

function og.card(url, width)
  local og_data = og.fetch(url)
  return {
    html = "<div style='background-color:rgba(0,0,0,0.15);padding:10px;border-radius:5px;display:flex;flex-direction:column;align-items:space-evenly;width:"..width.."px;'><span class='sb-sub'>"..og_data.sitename.."</span><a href = '"..og_data.url.."'><b>"..og_data.title.."</b></a>"..og_data.description.."<img src='"..og_data.image.."' style='margin-top:10px;'/></div>"
  }
end

Open for any feedback! :smiley:

7 Likes

Nice, thanks for sharing. It’s good to see what people are doing with this. Part of the Lua (and SilverBullet) fun is that it’s perfectly fine to hack things so that they work for you, without overthinking too much about how to “properly” engineer. It’s ok, chill :laughing:

That said, I am wondering whether parsing HTML is going to be a common use case. If so, there’s probably ways (already) that can expose some of (browser) JavaScript native DOM handling and querying to Space Lua as well to make this nicer.

1 Like

i like how you did the styling, feels very natural

1 Like

I’ll keep that in mind ;D

HTML parsing

My first thought was to just use the DOMParser class with a call to js.new, but I ended up implementing it in 100% Lua just to see how much more work that would be.
Looking at the code now, I feel like the effort isn’t immensly higher than using native DOM parsing (though that probably depends on the application), but having a lua api available would certainly improve parsing stability & scalability a lot :+1:

Edit: just realized, that using browser-native apis with js.new is not really possible (please correct me if I’m wrong), but using something like dom-parser with js.import would probably work as well.

In v2 you actually can:

local boldNode = js.new(js.window.DOMParser).parseFromString("<b>Bold</b>", "text/html")
1 Like