This post gives more technical detail for the recent talk that I gave at Bring-a-Hack NYC. In it, I describe a system that copies posts from Ghost Party's Instagram automatically to the Ghost Party Website at ghostparty.today.

This roughly works like so:

Authorization with IndieAuth

One missing step in that rough outline is the bit where we tell OwnYourGram *how* to post to our site via Micropub, as well as making sure that OwnYourGram can prove that we have given it permission to post there. Many Micropub clients support IndieAuth as a way to do this permission-granting step (known as authorization). With a typical personal website, I'd delegate this process to indieauth.com, which lets you offload the step of "proving" that you own the website where you're trying to post by instead letting you log into an existing social network account.

ghostparty.today is a shared website, so I thought a simpler way to approach this would be to create a custom authorization endpoint that accepts a password. I could give this password to other GHOST PARTY folks and they can post whatever they like.

Some nice folks in the IndieWeb community had already created an authorization endpoint that works this way, called selfauth. I created a pretty simplistic port of selfauth from PHP to JavaScript using NodeJS on the awesome Glitch social code hosting platform. It needs some more robust error checking, and some micropub clients seem to have trouble with it, but it works with OwnYourGram and Quill, so it's Good Enough for now!

Interface displaying text 'You are attempting to login as https://ghostparty.today/ with client https://quill.p3k.io/' along with a password prompt.
Enter the password to get authorized!

If you're interested, you can find the source for this endpoint here: https://glitch.com/edit/#!/befitting-price

Micropub Endpoint

Finally, there's the Micropub endpoint itself. I based mine off of this Glitch micropub project, created by Adam Bachman at IndieWebCamp Baltimore this year. Specfically, I took the bits that verify the access token. From there, it was a matter of stumbling through various bits of handling a Micropub post, pulling in extra libraries as I needed them. I slowly added support for saving uploaded files to our Neocities site via the Neocities API, then for generating an HTML snippet for the new post, and finally for adding the post to the site's main page.

The process for adding the generated post content to the site itself is extremely retro. The Micropub endpoint actually downloads the HTML from https://ghostparty.today/index.html. Inside there is a comment that looks like:

<!-- NEW POSTS BELOW -->

So the new contents for index.html is everything before the comment, the comment, the new content, and then everything after the comment. Once that's constructed, the endpoint uploads index.html back to Neocities with the new contents.

You can find the source for the Micropub endpoint here: https://glitch.com/edit/#!/prism-dirt

It should be possible to fork the project on Glitch and configure it to work with your own Neocities site! You'll need to use cURL with the Neocities API to generate an authorization token to configure the app, rather than saving username and password info in the project!

Making ghostparty.today "IndieWeb-Readable"

The GHOST PARTY site follows an extremely old-school publishing model where each post only exists as a small section of HTML on a single long page. This complicates things a bit because interacting with posts on the IndieWeb typically requires three things:

Microformats2 parsers like X-Ray do a great job at parsing HTML page into meaningful data, but the programs that use the parsed data usually stop at the first thing they find. So, if I ask X-Ray to parse https://ghostparty.today/, this is what it sees:

{
  "data":{
    "type":"card",
    "name":"GHOST PARTY",
    "url":"https://ghostparty.today/",
    "photo":"https://ghostparty.neocities.org/ghost-party-logo.png"
  },
  "url":"https://ghostparty.today/",
  "code":200
}

This is great in that it contains the "identity" information that we want the page to have. However, that page also has a feed! How can I get at that? By telling X-Ray to only look at a fragment of the page! Here's how X-Ray sees https://ghostparty.today/#feed

{
  "data":{
    "type":"feed",
    "items":[
      {
        "type":"entry",
        "published":"2018-03-07T16:44:16+00:00",
        "url":"https://ghostparty.today/#2018-03-07-181020",
        "photo":["https://ghostparty.today/uploads/2018-03-07/181020-ig0LN1JG.jpg"],
        "content":{"text":"snow day selfie #snowday #snowfie #ghoststories"} 
      },
      {
        "type":"entry",
        ...
      },
      ...
    ],
    "url":"https://ghostparty.today/#feed",
    "code":200,
    "info":{"found_fragment":true}
  }
}

Data! Nice! So if we want to follow ghostparty.today with an indie reader, we use https://ghostparty.today/#feed as the URL.

Looking at the content of the feed, you'll notice that the individual items have a url property which also has a fragment ID in the URL. This allows IndieWeb sites to interact with a particular post, even though it's one of many on the same page. For example, I was able to create an RSVP post to the most recent Ghost Party show, and (by looking just at the content in the fragment at https://ghostparty.today/#show-2018-02-14) my site was able to pick up the machine-readable details about the event to display them on my own site.

The microformats markup for the h-card and h-feed are built into the index.html file itself and don't change very often. However, for each new micropub post we need to convert those properties into a chunk of HTML for the index.html page that also contains the microformats h-entry markup. To do this, I made this Handlebars template:

<div class="h-entry" id="{{ mp-slug }}">
  <a class="u-author" href="/"></a>
  <a class="u-url" href="#{{ mp-slug }}">{{#if name}}<h2 class="p-name">{{ name }}</h2>{{/if}}</a>
  <time class="dt-published" datetime="{{ published }}"></time>
  {{#if video}}
    {{#each video}}
      <video controls loop class="u-video" src="{{ . }}"></video>
    {{/each}}
  {{ else }}
    {{#each photo}}
      <img class="u-photo" src="{{ . }}" />
    {{/each}}
    {{#each audio}}
      <audio class="u-audio" src="{{ . }}"></audio>
    {{/each}}
  {{/if}}
  <div class="e-content">{{ content }}</div>
</div>
<img src="ghosts.gif" />

Not so bad!

Resources

Here's a collection of some of the other libraries and docs I used to pull this thing together:


Likes

Aaron Parecki
Aaron Parecki

Mentions

Eli Mellen Eli Mellen at said:

This is SUCH a great post. I've noodled with a similar idea for a while, but haven't gotten it up and running yet — trying to figure out if there is a way to build a service, or even local electron app, that would let folks post to a neocities site without needing another web host or server.