Press "Enter" to skip to content

Instance-qualified Mastodon URLs | InfoWorld

In Lists and People in Mastodon, I showed how I added a list column to the following tab of the Mastodon browser that I am building. That was a step in the direction of easier and more powerful list management. It allows me to see if the people I’m following are assigned to lists and to consider who should be on a list (or perhaps a different list).

Today, when I started to use that new opportunity seriously, I discovered a new challenge. To assign someone to a list or change the assignment of a list, I clicked the link in the account_url column to open that person’s profile in the Mastodon web application. That was fine for the accounts on my home server, mastodon.social. An account URL like https://mastodon.social/@burningbird from Shelley Powers takes me to Shelley’s profile on my home server where the list manager is available.

But if I’m following someone somewhere else, like Ward Cunningham on https://mastodon.radio/@k9ox, the account URL takes me to Ward’s profile on that server where the list manager isn’t available. To assign Ward to a list, I had to grab his account URL, paste it into the search box on my home server’s web app, and then click the resulting link: https://mastodon.social/ @k9ox@mastodon.radio.

That aged very quickly, so I adjusted the following tab to show the last type of url that I will call an instance qualified url.

Steampipe provides a few ways to make that adjustment. As a dashboard user, you can use Postgres’ regular expression functions to perform the transformation on the SQL query that drives the view. But you’d rather not have to. It’s much better if the plugin does that for you, so the SQL can only refer to a column named instance_qualified_url.

I chose the latter approach. As the author of a Steampipe plugin, you want to make life as easy as possible for users of the plugin. When you’re the author of both the plugin and the dashboard, as I am in this case, you can enjoy a nice virtuous cycle. As the dashboard evolves, it discovers ways to improve the plugin, which leads to more dashboard usage, which suggests more opportunities to improve the plugin. I have really enjoyed the co-evolution of these two components!

Adding a new column to a Steampipe table

To make the change, I extended the structure that defines the columns of the tables mapped from the Mastodon account API. A Steampipe plugin defines columns using a list structure like this.

...,
{
	Name:        "url",
	Type:        proto.ColumnType_STRING,
	Description: "URL for the account.",
},
...,

That struct says: “When the name of a top-level field in the API response is urltell Steampipe to make a database column with that name and with type Postgres text.”

You can also transform values ​​into API responses to synthesize new columns that don’t appear in API responses. Here is the structure I added for this case.

...,
{	
	Name:        "instance_qualified_account_url",
	Type:        proto.ColumnType_STRING,
	Description: "Account URL prefixed with my instance.",
	Transform:   transform.FromValue().Transform(instanceQualifiedAccountUrl),
},
...

That one says: “Send the API response to the transform function instanceQualifiedAccountUrland use its result as the column value.

Here is the function.

func instanceQualifiedAccountUrl(ctx context.Context, input *transform.TransformData) (interface{}, error) {
	url := input.Value.(*mastodon.Status).Account.URL
	qualifiedUrl := qualifiedUrl(ctx, url)
	return qualifiedUrl, nil
}

Delegate the actual work to another function.

func qualifiedUrl(ctx context.Context, url string) string {
	plugin.Logger(ctx).Debug("instanceQualifiedUrl", "server", homeServer, "url", url)
	re := regexp.MustCompile(`https://([^/]+)/@(.+)`)
	matches := re.FindStringSubmatch(url)
	if len(matches) == 0 {
		return url
	}
	person := matches[1]
	server := matches[2]
	qualifiedUrl := fmt.Sprintf("%s/@%s@%s", homeServer, server, person)
	plugin.Logger(ctx).Debug("instanceQualifiedUrl", "qualifiedUrl", qualifiedUrl)
	schemelessHomeServer := strings.ReplaceAll(homeServer, "https://", "")
	qualifiedUrl = strings.ReplaceAll(qualifiedUrl, "@"+schemelessHomeServer, "")
	plugin.Logger(ctx).Debug("qualifiedUrl", "qualifiedUrl", qualifiedUrl)
	return qualifiedUrl
}

Because? Two different sets of column definitions require the same transformation. instanceQualifiedAccountUrl work with the answers Account API. But account URLs also appear in the Status API that drives timeline views. Those use a different transform function, instanceQualifiedStatusUrlto do the same transformation for a different API response.

Also Read:  Mojo language marries Python and MLIR for AI development

Account URL to Status URL

He instanceQualifiedAccountUrl column solved the original problem. I was able to take off my plugin author hat, put on my dashboard author hat, and refer to account URLs as instance-qualified URLs in all tabs that display them. Any such link now leads to a profile that I see through the lens of mastodon.social and that allows me to use the web app’s list manager directly, without the cumbersome copy/paste/find procedure.

However, my happy dance did not last long. Newly sensitized to that copy/paste/find friction, I realized it was still happening when I try to respond to items that appear in a timeline view. Here is a recent example: https://techpolicy.social/@mnot/109610641523489182.

That is the URL displayed on the dashboard. When I click, I land on Mark’s server and can see the item, but if I try to reply, I’m met with the dreaded copy/paste/find operation.

No problem! I will use a similar transformation! Not so fast. I can form a URL like https://mastodon.social/@mnot@techpolicy.social/109610641523489182 but it doesn’t go anywhere.

If I copy/paste/find, I land on a similar but different URL: https://mastodon.social/@mnot@techpolicy.social/109610641692667630. It has the same structure but a different beep ID. This URL is also the one that appears in the web app’s launch timeline, so I can reply directly from that view.

I’m out of my league here, so I’ll end with a plea for help. It makes sense for a home server to assign its own ID to an item fetched from an external server and for the web app to use that ID. But I don’t see a way to acquire that id directly from the API. I suspect it’s possible to acquire it via search, but doing it for every item on a timeline will quickly deplete the tight budget for API requests (only 300 every five minutes).

So Lazy Mastodon, am I stuck here or is there a way to transform external state urls into instance relative state urls?

Update: Solved!

After chatting with Jari Pennanen, I took another look and realized that the required ID was available in the API response after all, I just wasn’t using it (facepalm). And, in fact, there are two types of ID: one for original toots and one for boosts. The columns for both cases are added here and the setting for the dashboard to use them here.

Here is the result.

Instance Qualified URL IDG

Thanks for being my rubber ducky, Jari! The instance-qualified reblog and toot urls make this dashboard much more useful.

These series:

  1. Autonomy, pack size, friction, fanout and speed
  2. Create a Mastodon panel with Steampipe
  3. Navigating the fediverse
  4. A Bloomberg terminal for Mastodon
  5. Create your own Mastodon UX
  6. Lists and people on Mastodon
  7. How many people on my Mastodon feed also tweeted today?
  8. Qualified Mastodon URLs per instance
  9. Mastodon Ratio Charts
  10. Working with Mastodon lists
  11. Images considered harmful (sometimes)
  12. Mapping the broader fediverse

Copyright © 2023 IDG Communications, Inc.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *