You are reading content from Scuttlebutt
User has not chosen to be hosted publicly
@Rabble %Xn0an7P8A8ZlswUCLofiFjc0BJP9c97LeTgCwgD38UM=.sha256
Voted A cute feature of Project Moon Hermit is its debug mode. If you pass a `-d`
@Soapy mcSoap %7oK66ZSJ+eDZ1vDxaNluK5xyl9fsguYqf1xjxVFgdfw=.sha256
Re: %9snVZ3RMf

A cute feature of Project Moon Hermit is its debug mode. If you pass a -d to the moonhermit binary invocation, it will output some information about all the muxrpc calls its making.

To make the video above, I just changed the shebang/hashpling line at the top to include -d.

Now, you can see every muxrpc call.

@enkiv2 %AwXffzCtTQWvh/xTVAUm/JpRFGswHmAB/mn3cm5EhWI=.sha256
Voted # Find all pubs you follow Today I decided to tackle a task that is quite
@Soapy mcSoap %5VvX2Td+MsaA2uYu6beDOxGWZuc/7fP90bTmaSib4kc=.sha256
Re: %9snVZ3RMf

This script could be patched to be an interactive script prompting the user if they want to unfollow each pub. Or it could simply unfollow all of them (crude, I know) and hope for the best.

There is no other tool to do quick SSB scripts. The only comparable alternative is firing NodeJS, but that will require some boilerplate code to load keys, start a client connected to the running server, etc... moon hermit is a binary requiring no boilerplate. Lua is an easier language to learn than JS.

If you want to help Project Moon Hermit development, please join my ko-fi.


PS: Some of the Lua scripts included in the repository use third-party Lua libraries that can be installed using luarocks.

PPS: At the moment Moon Hermit relies on some local Lua files. I'll find a way to embed them in the binary at some point in the future, right now you need to keep them next to the binary.

@Soapy mcSoap %9snVZ3RMf1YLEnOkRPl50lMcLD+8zruyUxSFSb9/WcM=.sha256

Find all pubs you follow

Today I decided to tackle a task that is quite difficult to do in most SSB clients. Figuring out which pubs you're following.

During the development of this script, I found some bugs in moonhermit.c which have been fixed and pushed to the repo. Basically moonhermit had a hardcoded request id, which meant that multiple requests on the same script were getting borked. Now it is a random number.

First, a small video of the running script:

Neat, right? 😃

The script needs to do a ton of muxrpc calls.

  • Figure out the name of the feed you've choosen (either you or some feed passed as an argument to the script).
  • Find out all pub announcement messages in the feed.
  • Compute a relationship map by fetching all contact messages for the feed.
  • Find which feeds the chosen feed follows that have sent pub announcement messages.
  • Get the name for each of those pub feeds.

Remember that project moon hermit has a extremely strong bottleneck because it does the JSON encoding and decoding on the Lua side using pure Lua string functions. It is really slow (I should have used a C JSON library).

Also, remember that project moon hermit doesn't stream. The code may look like pull-streams, but that is for developer convenience, it actually fetches all the data before passing it through the pipeline. It means that something like ssb:createUserStream() will actually fetch and encode all the database before sending it further through the script.

It takes about 20 seconds for it to go through my feed and find all information it needs. That doesn't mean that all feeds that it identifies as pubs are actual pubs, apparently some people have send pub announcement messages from their own feeds...

Anyway, here is the script, it is a bit convoluted but not too much.

#!./moonhermit

--[[
This script does the following:

* Chooses a feed by either inspecting the first argument passed to it
or by finding out who the running user is.
* Gets the name for that feed.
* Finds all follows for that feed.
* Gets all pub annoucements
* Finds intersections between follows and pub announcements.
* Gets the name for all the feeds in the intersection.

Lots of muxrpc calls... 
]]

require "pl"


--[[ Figure out if the user passed a feed or if we need to check the running
user feed is
]]

local key = arg[1]

if key == nil then
    print "Finding who you are..."
    local whoami, err = ssb:whoami()
    if err then
        print("error:" .. err)
        exit(1)
    end
    key = whoami.id
else
    print(string.format("Finding pubs for feed: %s", key))
end


--[[
Helper functions to compute relationship table and pub announcement set.
]]

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

function justAbout(m)
    local c1 = lookup(m, "value", "content", "type") == "about"
    local c2 = lookup(m, "value", "author") == lookup(m, "value", "content", "about")

    return c1 and c2
end

function getName(feed)
    local s, err = ssb:createUserStream({
        id = feed
    })

    if err then
        return nil, err 
    end

    local a = stream(s).filter(justAbout).reverse().toarray()
    local name = lookup(a[1], "value", "content", "name")
    if name == nil then
        -- first message was not setting a name, look further...
        for i, v in pairs(a) do
            if lookup(v, "value", "content", "name") ~= nil then
                name = lookup(v, "value", "content", "name")
                break
            end
        end
    end
    return name, nil, a
end

local contacts = {}

function addToMap(m)
    local user = m.value.author
    local target = m.value.content.contact 
    local action = m.value.content.following and 1 or -1
    contacts[target] = (contacts[target] or 0) + action
end

local keyname = getName(key)


--[[ Get all pub messages, compute relationship graph ]]

print "Finding all pub announcement messages..."
local pubMsgs, err2 = ssb:messagesByType({
    type = "pub"
})

print("Looking up " .. keyname .. " history...")
local msgs, err1 = ssb:createHistoryStream({
    id = key
})

if (not err1) and (not err2) then
    -- compute contacts table
    stream(msgs).filter(justContacts).foreach(addToMap)

    -- rework pubMsgs table into a Set
    local allPubs = Set{}

    for i, v in pairs(pubMsgs) do 
        local k = lookup(v, "value", "content", "address", "key")
        --local k = lookup(v, "value", "author")
        allPubs = allPubs+Set{k}
    end

    -- output some cool information
    local contactMsgs = stream(msgs).filter(justContacts).toarray()
    print(string.format("\n%d contact msgs", #contactMsgs ))
    print(string.format("%d pub msgs", #pubMsgs ))
    print(string.format("%d unique pub ids\n", #allPubs ))

    -- find pubs in contacts table
    for feed, v in pairs(contacts) do
        if v == 1 then
            if allPubs[feed] then
                local name, _ = getName(feed)
                if name == nil then
                    name = "unknown name (maybe out of hops)"
                end
                print(string.format("%s follow pub %s known as %s", keyname, feed, name))
            end
        end
    end
else
    print(err1)
    print(err2)
end
User has not chosen to be hosted publicly
User has not chosen to be hosted publicly
User has not chosen to be hosted publicly
@cel %firv8WOwh59V2XdMMQilchPC0QgGRSuOOUiFCkuhMrU=.sha256
Voted In theory, I can adapt this script and export my whole post history to my b

Show whole feed
Join Scuttlebutt now