You are reading content from Scuttlebutt
@SoapDog (Macbook Air M1) %+CGAZUQ0mGPgnMkVhJIoN6T2wrQxg27X69gaQ7YFcSU=.sha256

Made some quality of life changes to ✨ Project Moon Hermit ✨ today. Just for procrastination sake and because I miss Lua.

With the recent changes, one can use moonhermit binary as an interpreter for shell scripting. It is a very handy way to create scripts that interact with SSB. Let me show you an example called getmsg.lua:

#!./moonhermit

-- Requires "supernova" which can be installed from Luarocks
-- https://luarocks.org/modules/gbaptista/supernova

local utils = require "utils"
local supernova = require "supernova"

local msg, error = ssb:get({id = arg[1]})

if (not error) then
    local date = os.date("%A, %B %d %Y at %I:%M:%S %p\n", msg.timestamp//1000)
    print(supernova.italic.yellow("From: ") .. supernova.underline.color(msg.author, "#e317e0"))
    print(supernova.italic.yellow("Date: ") .. supernova.underline.color(date, "#e317e0"))

    print(supernova.gradient(
      utils.textwrap(msg.content.text),
      { '#FF0000', '#FFFF00', '#00FF00', '#0FF0FE', '#233CFE' }
    ))
else
    print(error)
end

It outputs this:

screenshot

The magic part is the high-level SSB library that makes retrieving a message from the running SSB server as easy as:

local msg, error = ssb:get({id = arg[1]})

Lua can return multiple values from a function (a feature I miss in other languages), so that call will either give you an error in the error variable or a table representing the message in the msg variable. Quite neat.

I've changed the moonhermit interpreter to export a global table arg that holds the arguments passed to the script. It is similar to the built-in one from Lua standard interpreter but without the negative indices.

The rest of the code is just having fun with supernova colours.

@SoapDog (Macbook Air M1) %YfXU8AHfPjWnX2h4njHXMKiF3Ed/KRcQ0MI/UesSo6o=.sha256

moonhermit is quite nimble. It is statically linked to Libsodium and Lua and weights only 664k on my Mac. It might not be as fast as NodeJS based stuff, but it is smol and it is fast enough.

The main challenge I see at the moment is that muxrpc calls which generate streams are collected before the data is sent back into the Lua script. This is slower than just pull-streaming. You can still create pull-stream like code, but the stream will be fully computed before being sent through the pipeline.

An example of streams:

-- counts contact messages.

function justContacts(m)
    return lookup(m, "value", "content", "type") == "contact"
end

function displayContactMsg(m)
    local user = m.value.author
    local target = m.value.content.contact 
    local action = m.value.content.following and "followed" or "unfollowed"
    print(string.format("%s %s %s", user, action, target))
end

local msgs, err = ssb:createHistoryStream({
    id = arg[1],
    limit = 50000
})

if (not err) then
    stream(msgs).filter(justContacts).foreach(displayContactMsg)

    local contactMsgs = stream(msgs).filter(justContacts).toarray()
    print(string.format("%d contact msgs", #contactMsgs ))
else
    print(err)
end

That example is a bit naive as it runs through the stream twice: once to print the messages, and then again to count them. It is straightforward code, good enough for throwaway scripts.

This script runs in about 1 second on my machine:

172 contact msgs
./moonhermit test4.lua "@qv10rF4IsmxRZb7g5ekJ33EakYBpdrmV/vtP1ij5BS4=.ed25519  0.77s user 0.06s system 72% cpu 1.140 total

not bad for something that is looping all messages in my feed twice.

@Hendrik Peter %SaIKpr9OHKMFfK8xOb3+QE1LQIbeoDxlMxdZ181uqjU=.sha256

Those rainbow colours :O, how'd you do that?
overall format reminds me of git log! but that's probably good!

Join Scuttlebutt now