Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support overriding status template #18

Merged
merged 1 commit into from
Dec 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
uses: './'
with:
# This is the RSS feed you want to publish
rss-feed: 'https://githubraw.com/joschi/mastofeedbot/main/tests/simple.xml'
rss-feed: 'https://githubraw.com/joschi/mastofeedbot/main/tests/simple-no-cache.xml'
# Visibility of the posted status (public | unlisted | private | direct)
status-visibility: private
dry-run: false
Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
uses: './'
with:
# This is the RSS feed you want to publish
rss-feed: 'https://githubraw.com/joschi/mastofeedbot/main/tests/simple.xml'
rss-feed: 'https://githubraw.com/joschi/mastofeedbot/main/tests/simple-sensitive.xml'
# Visibility of the posted status (public | unlisted | private | direct)
status-visibility: unlisted
# Mark Mastodon status as sensitive content
Expand All @@ -94,3 +94,27 @@ jobs:
api-token: ${{ secrets.MASTODON_ACCESS_TOKEN }}
# This is a path to the cache file, using the above cache path
cache-file: ${{ github.workspace }}/mastofeedbot/cache.json

simple-template:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3
- name: Run action
uses: './'
with:
# This is the RSS feed you want to publish
rss-feed: 'https://githubraw.com/joschi/mastofeedbot/main/tests/simple-template.xml'
# Visibility of the posted status (public | unlisted | private | direct)
status-visibility: unlisted
# Template of status posted to Mastodon (Handlebars)
template: |
{{feedData.title}}: {{item.title}}

{{item.link}}
# This is your instance address
api-endpoint: https://social.dev-wiki.de/
# This is the secret you created earlier
api-token: ${{ secrets.MASTODON_ACCESS_TOKEN }}
# This is a path to the cache file, using the above cache path
cache-file: ${{ github.workspace }}/mastofeedbot/cache.json
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ jobs:
with:
# This is the RSS feed you want to publish
rss-feed: https://www.githubstatus.com/history.rss
# Template of status posted to Mastodon (Handlebars)
template: '{{item.title}} {{item.link}}'
# Visibility of the posted status (public | unlisted | private | direct)
status-visibility: public
# Mark Mastodon status as sensitive content
Expand All @@ -56,3 +58,28 @@ jobs:
```

5. Commit and publish your changes.

## Status template

The status template (`status-template`) is using [Handlebars](https://handlebarsjs.com/) as template engine.

The action is passing in an instance of `FeedData` (field `feedData`) and the current `FeedEntry` (field `item`) into the template:

```typescript
export interface FeedEntry {
link?: string;
title?: string;
description?: string;
published?: Date;
}

export interface FeedData {
link?: string;
title?: string;
description?: string;
generator?: string;
language?: string;
published?: Date;
entries?: Array<FeedEntry>;
}
```
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ inputs:
description: 'Visibility of the posted status (public | unlisted | private | direct)'
required: false
default: 'public'
template:
description: 'Template of status posted to Mastodon (Handlebars)'
required: true
default: '{{item.title}} {{item.link}}'
dry-run:
description: 'Only fetch RSS feed and update cache but skip posting to Mastodon.'
required: false
Expand Down
68 changes: 45 additions & 23 deletions dist/index.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions dist/index.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"dependencies": {
"@actions/core": "^1.10.0",
"@extractus/feed-extractor": "^6.1.7",
"handlebars": "^4.7.7",
"masto": "^4.11.1",
"mkdirp": "^1.0.4"
}
Expand Down
53 changes: 51 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"dependencies": {
"@actions/core": "^1.10.0",
"@extractus/feed-extractor": "^6.1.7",
"handlebars": "^4.7.7",
"masto": "^4.11.1",
"mkdirp": "^1.0.4"
}
Expand Down
38 changes: 25 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { login, StatusVisibility, type MastoClient } from 'masto';
import { readFile, writeFile } from 'fs/promises';
import * as core from '@actions/core';
import mkdirp from 'mkdirp';
import { type FeedEntry, read } from '@extractus/feed-extractor';
import { type FeedEntry, FeedData, read } from '@extractus/feed-extractor';
import crypto from 'crypto';
import Handlebars from "handlebars";

function sha256(data: string): string {
return crypto.createHash('sha256').update(data, 'utf-8').digest('hex')
Expand All @@ -28,11 +29,18 @@ async function writeCache(cacheFile: string, cacheLimit: number, cache: string[]
}

async function postItems(
apiEndpoint: string, apiToken: string, rss: FeedEntry[],
visibility: StatusVisibility, dryRun: boolean, sensitive: boolean, cache: string[]) {
apiEndpoint: string,
apiToken: string,
feedData: FeedData | undefined,
entries: FeedEntry[],
statusTemplate: HandlebarsTemplateDelegate<any>,
visibility: StatusVisibility,
dryRun: boolean,
sensitive: boolean,
cache: string[]) {
if (dryRun) {
// Add new items to cache
for (const item of rss) {
for (const item of entries) {
try {
const hash = sha256(<string>item.link);
core.debug(`Adding ${item.title} with hash ${hash} to cache`);
Expand Down Expand Up @@ -60,14 +68,14 @@ async function postItems(
}

// post the new items
for (const item of rss) {
for (const item of entries) {
try {
const hash = sha256(<string>item.link);
core.debug(`Posting ${item.title} with hash ${hash}`);

// post the item
const res = await masto.statuses.create({
status: `${item.title} ${item.link}`,
status: statusTemplate({ feedData, item }),
visibility,
sensitive
}, hash);
Expand All @@ -92,11 +100,11 @@ async function filterCachedItems(rss: FeedEntry[], cache: string[]): Promise<Fee
return rss;
}

async function getRss(rssFeed: string): Promise<FeedEntry[] | void> {
let rss: FeedEntry[];
async function getRss(rssFeed: string): Promise<FeedData | undefined> {
let rss: FeedData;
try {
rss = <FeedEntry[]>(await read(rssFeed)).entries;
core.debug(JSON.stringify(`Pre-filter feed items:\n\n${JSON.stringify(rss, null, 2)}`));
rss = <FeedData>(await read(rssFeed));
core.debug(JSON.stringify(`Pre-filter feed items:\n\n${JSON.stringify(rss.entries, null, 2)}`));
return rss;
} catch (e) {
core.setFailed(`Failed to parse RSS feed: ${(<Error>e).message}`);
Expand Down Expand Up @@ -129,22 +137,26 @@ export async function main(): Promise<void> {
core.debug(`cacheLimit: ${cacheLimit}`);
const statusVisibility: StatusVisibility = <StatusVisibility>core.getInput('status-visibility', { trimWhitespace: true });
core.debug(`statusVisibility: ${statusVisibility}`);
const template: string = core.getInput('template');
core.debug(`template: ${template}`);
const dryRun: boolean = core.getBooleanInput('dry-run');
core.debug(`dryRun: ${dryRun}`);
const sensitive: boolean = core.getBooleanInput('sensitive');
core.debug(`sensitive: ${sensitive}`);

// get the rss feed
let rss = await getRss(rssFeed);
const feedData: FeedData | undefined = await getRss(rssFeed);
const entries: FeedEntry[] = feedData?.entries ?? [];

// get the cache
const cache = await getCache(cacheFile);

// filter out the cached items
rss = await filterCachedItems(<FeedEntry[]>rss, cache);
const filteredEntries: FeedEntry[] = await filterCachedItems(entries, cache);

// post the new items
await postItems(apiEndpoint, apiToken, <FeedEntry[]>rss, statusVisibility, dryRun, sensitive, cache);
const statusTemplate = Handlebars.compile(template);
await postItems(apiEndpoint, apiToken, feedData, entries, statusTemplate, statusVisibility, dryRun, sensitive, cache);

// write the cache
await writeCache(cacheFile, cacheLimit, cache);
Expand Down
36 changes: 36 additions & 0 deletions tests/simple-no-cache.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<rss version="2.0">
<channel>
<link>https://raw.githubusercontent.com/joschi/mastofeedbot/main/tests/simple.rss</link>
<title>Channel Title</title>
<description>Channel description.</description>
<managingEditor>joe.user@example.com (Managing Editor)</managingEditor>
<lastBuildDate>Tue, 27 Dec 2022 07:17:21 +0000</lastBuildDate>
<pubDate>Tue, 27 Dec 2022 07:17:21 +0000</pubDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<image>
<url>https://avatars.githubusercontent.com/u/43951</url>
<link>https://raw.githubusercontent.com/joschi/mastofeedbot/main/tests/simple.rss</link>
<title>Image Title</title>
<description>Image description.</description>
</image>
<item>
<link>https://github.com/joschi/mastofeedbot/pull/1#no-cache</link>
<guid isPermaLink="false">44aefc891f5b1f96ae7393f3d1613254eae9c23b</guid>
<title>Item 1 Title</title>
<description>Item 1 Description</description>
<pubDate>Thu, 15 Dec 2022 19:02:13 +0000</pubDate>
<category>item1:category</category>
<author>author.item1@example.com (Author Item 1)</author>
</item>
<item>
<link>https://github.com/joschi/mastofeedbot/pull/2#no-cache</link>
<guid isPermaLink="false">55a26da9618a994e56c845306bd38641f58220af</guid>
<title>Item 2 Title</title>
<description>Item 2 Description</description>
<pubDate>Tue, 27 Dec 2022 19:02:13 +0000</pubDate>
<category>item2:category</category>
<author>author.item2@example.com (Author Item 2)</author>
</item>
</channel>
</rss>
36 changes: 36 additions & 0 deletions tests/simple-sensitive.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<rss version="2.0">
<channel>
<link>https://raw.githubusercontent.com/joschi/mastofeedbot/main/tests/simple.rss</link>
<title>Channel Title</title>
<description>Channel description.</description>
<managingEditor>joe.user@example.com (Managing Editor)</managingEditor>
<lastBuildDate>Tue, 27 Dec 2022 07:17:21 +0000</lastBuildDate>
<pubDate>Tue, 27 Dec 2022 07:17:21 +0000</pubDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<image>
<url>https://avatars.githubusercontent.com/u/43951</url>
<link>https://raw.githubusercontent.com/joschi/mastofeedbot/main/tests/simple.rss</link>
<title>Image Title</title>
<description>Image description.</description>
</image>
<item>
<link>https://github.com/joschi/mastofeedbot/pull/1#sensitive</link>
<guid isPermaLink="false">44aefc891f5b1f96ae7393f3d1613254eae9c23b</guid>
<title>Item 1 Title</title>
<description>Item 1 Description</description>
<pubDate>Thu, 15 Dec 2022 19:02:13 +0000</pubDate>
<category>item1:category</category>
<author>author.item1@example.com (Author Item 1)</author>
</item>
<item>
<link>https://github.com/joschi/mastofeedbot/pull/2#sensitive</link>
<guid isPermaLink="false">55a26da9618a994e56c845306bd38641f58220af</guid>
<title>Item 2 Title</title>
<description>Item 2 Description</description>
<pubDate>Tue, 27 Dec 2022 19:02:13 +0000</pubDate>
<category>item2:category</category>
<author>author.item2@example.com (Author Item 2)</author>
</item>
</channel>
</rss>
Loading