# jam-my-stack 🥞 > A set of simple IndieWeb Jamstack publishing syndication tools Published at https://www.npmjs.com/package/jam-my-stack [![npm version](https://badge.fury.io/js/jam-my-stack.svg)](https://badge.fury.io/js/jam-my-stack) These simple scripts **enrich your Jamstack-site** by adding/manipulating/whatever (meta)data, such as extra posts, indexing, and so forth. A primary example of these tools in action is my own site https://brainbaking.com - inspect how it's used at https://git.brainbaking.com/wgroeneveld/brainbaking **Are you looking for a way to receive webmentions?** See https://git.brainbaking.com/wgroeneveld/go-jamming ! ## The tools Usage: 1. `yarn add jam-my-stack` 2. `const { mastodon, goodreads } = require('jam-my-stack')` ### 1. Mastodon #### 1.1 `parseFeed` An async function that parses your Fediverse-compatible feed (Mastodon/Pleroma/...) and converts entries to `.md` Markdown files for your Jamstack to enjoy. Usage example: ```js await mastodon.parseFeed({ notesdir: `${__dirname}/content/notes`, url: "https://chat.brainbaking.com/users/wouter/feed", utcOffset: 60, titleCount: 50, titlePrefix: "Note: " }) ``` Options and their default values: - `utcOffset`: `60` (= GMT+1, that's where I am!) (in **minutes**, see [day.js docs](https://day.js.org/docs/en/manipulate/utc-offset) - `titleCount`: `50`. Will add "..." and trim if title length bigger. - `titlePrefix`: `""`. Will add before title (e.g. "Note: ") - `ignoreReplies`: `false`. If true, will not process `in-reply-to` items. Note that this **does not** delete the notes dir with every call. It simply checks if there isn't already a file with the same name (based on the publication date), and adds one if not. Example feed entry: ```xml http://activitystrea.ms/schema/1.0/note http://activitystrea.ms/schema/1.0/post https://chat.brainbaking.com/objects/77a3ecfb-47e1-4d7a-a24a-8b779d80a8ac I pulled the Google plug and installed LineageOS: https://brainbaking.com/post/2021/03/getting-ri... I pulled the Google plug and installed LineageOS: <a href="https://brainbaking.com/post/2021/03/getting-rid-of-tracking-using-lineageos/" rel="ugc">https://brainbaking.com/post/2021/03/getting-rid-of-tracking-using-lineageos/</a> Very impressed so far! Also rely on my own CalDAV server to replace GCalendar. Any others here running <a class="hashtag" data-tag="lineageos" href="https://chat.brainbaking.com/tag/lineageos" rel="tag ugc">#lineageos</a> for privacy reasons? 2021-03-01T19:03:35.273023Z 2021-03-01T19:03:35.273023Z https://chat.brainbaking.com/contexts/ff9aa62e-3357-41ad-951d-15f6ad506424 ``` This generates the file `01h20m03s35.md` (it assumes UTC times in the feed and adjusts according to specified `utcOffset`, such as GMT+1 in this example), with contents: ```md --- source: "https://chat.brainbaking.com/objects/77a3ecfb-47e1-4d7a-a24a-8b779d80a8ac" title: "I pulled the Google plug and installed LineageOS: https://brainbaking.com/post/2021/03/getting-ri..." date: "2021-03-01T19:03:35" --- I pulled the Google plug and installed LineageOS: https://brainbaking.com/post/2021/03/getting-rid-of-tracking-using-lineageos/ Very impressed so far! Also rely on my own CalDAV server to replace GCalendar. Any others here running #lineageos for privacy reasons? ``` See implementation for more details and features. **Also parsers**: - `` image types (see `render-enclosures.ejs`) [ejs template](https://ejs.co/), that is appended to the Markdown file if any are found. Styling is up to you... - `... @https://blah.com/blie hi there` - this is a **in-reply-to** toot which adds `context` frontmatter, so your html renderer can use the correct IndieWeb classes. This should also enable webmention sending since you mention the URL. If you "at" a valid Mastodon user, it will automatically do this. ### 2. Goodreads #### 2.1 `createWidget` An async function that reads and modifies Goodreads JS widget embed code, converting low-res book covers to hi-res ones if possible. This omits possible Goodread cookies and cross-domain mishaps. Usage example: ```js const widget = await goodreads.createWidget("https://www.goodreads.com/review/grid_widget/5451893.Wouter's%20bookshelf:%20read?cover_size=medium&hide_link=&hide_title=&num_books=12&order=d&shelf=read&sort=date_added&widget_id=1496758344") await fsp.writeFile(`${__dirname}/static/js/goodreads.js`, widget, 'utf-8') ``` ### 3. Lunr As of version `1.0.30`, Lunr functionality was removed in favor of https://pagefind.app/ With Pagefind, there's no need to integrate it into jam-my-stack, greatly simplifying things _and_ reducing the index file size. ### 4. Howlongtobeat #### 4.1 `howlong` Adds https://howlongtobeat.com/ game length (`MainGame`) and an ID to your front matter (keys `howlongtobeat_id` and `howlongtobeat_hrs`), provided you first added a property called `game_name`. (This gets substituted). It also downloads a thumbnail of the cover image as `cover.jpg` in **the same relative directory as the source article** if you provided the dir as an option. The downloaded thumbnail is automatically optimized for the web using `mogrify` (this will emit a warning if you do not have ImageMagick installed locally). So, Frontmatter like this: ``` --- title: Diablo 3 my Review game_name: Diablo 3 --- ``` Gets subsituted by something like this: ``` --- title: Diablo 3 my Review howlongtobeat_id: 62129 howlongtobeat_hrs: 20.5 --- ``` In your Hugo template, add a link to `https://howlongtobeat.com/game?id={howlongtobeat_id}` and you're all set! Usage example: ```js await howlong({ postDir: `${__dirname}/content`, downloadDir: `${__dirname}/static`) ``` It will print out games and metadata it found. Suppose the above `diablo-3.md` lives in `content/games/switch/diablo-3`, then a `cover.jpg` will be automatically downloaded in `static/games/switch/diablo-3/` and that directory will be created if not yet existing. Working example: https://jefklakscodex.com/games/diablo-3/ (on the left side). Check out the Hugo template to use the properties at https://git.brainbaking.com/wgroeneveld/jefklakscodex. ### 5. Webmentions In cooperation with https://git.brainbaking.com/wgroeneveld/go-jamming #### 5.1 `getWebmentions` Calls the get webmention endpoint, sorts by date, adds metadata such as relative date (`relativeTarget`, property), and returns data. Could be written in a `data` folder for Hugo to parse, for example. Parameters: first `domain`, second the config for the endpoint and token. Usage example: ```js await getWebmentions("brainbaking.com", { endpoint: 'https://jam.brainbaking.com', token: 'lol' }) ```` #### 5.1 `send` Calls the set webmention endpoint using a `PUT`. Based on the RSS feed, see the [go-jamming](git.brainbaking.com/wgroeneveld/go-jamming) README. Same as `getWebmentions`. ### 6. YouTube Thanks to ideas from [rubenerd.com](https://rubenerd.com) and his [video.sh script](https://gitlab.com/rubenerd/rubenerd.com/-/blob/trunk/scripts/video.sh). This downloads a thumbnail using `youtube-dl`, smacks a play button on it using `convert`, and stores that in the specified folder. Use in conjunction with a Hugo shortcode to get rid of YouTube's iframes! This method will fail if you do not have ImageMagick installed locally. Usage example: ```js await download({ postDir: 'somewhere/posts', downloadDir: 'static/youtube-thumbs', overlayImg: 'playbtn.png' }) ``` It scans all `.md` files in the posts dir for `{{< youtube xxx >}}` shortcodes.