]>
Commit | Line | Data |
---|---|---|
1 | const { exec } = require('child_process'); | |
2 | const { resolve, join} = require('path'); | |
3 | const { rm, writeFile } = require('fs/promises'); | |
4 | const { promisify } = require('util'); | |
5 | ||
6 | const internals = { | |
7 | exec: promisify(exec), | |
8 | ||
9 | apiUrl: process.env.API_URL, | |
10 | apiToken: process.env.API_TOKEN, | |
11 | blogUrl: process.env.BLOG_URL, | |
12 | archiveUrl: process.env.ARCHIVE_URL, | |
13 | blogPublicUrl: process.env.BLOG_PUBLIC_URL, | |
14 | archivePublicUrl: process.env.ARCHIVE_PUBLIC_URL, | |
15 | tootToken: process.env.TOOT_TOKEN, | |
16 | mastodonDomain: process.env.MASTODON_DOMAIN, | |
17 | ||
18 | date: (new Date()).toISOString().split('T')[0], | |
19 | ||
20 | generateGemtext(title, text) { | |
21 | ||
22 | return `# ${title}\n\n${text}`; | |
23 | }, | |
24 | ||
25 | getText(posts) { | |
26 | ||
27 | return posts.map((post) => { | |
28 | ||
29 | const title = post.title || post.website_title | |
30 | return `=> ${post.url} ${title}\n${post.notes}`; | |
31 | }).join('\n\n'); | |
32 | }, | |
33 | ||
34 | getTitle(posts) { | |
35 | ||
36 | const title = posts[0].title || posts[0].website_title | |
37 | ||
38 | if (posts.length === 1) { | |
39 | return `Link: ${title}`; | |
40 | } | |
41 | return `${posts.length} links for ${internals.date}`; | |
42 | }, | |
43 | ||
44 | slugify(text) { | |
45 | ||
46 | return text.toLowerCase().replace(/[^a-z0-9 ]/g, '').replace(/ +/g, '-') | |
47 | }, | |
48 | ||
49 | async toot(title) { | |
50 | ||
51 | const body = new FormData(); | |
52 | body.set( | |
53 | 'status', | |
54 | `New post: ${title}\n\nAvailable on:\n\nāļø the gemini archive ${internals.archivePublicUrl}\n\n or, the ephemeral blog š: ${internals.blogPublicUrl}` | |
55 | ); | |
56 | return fetch(`https://${internals.mastodonDomain}/api/v1/statuses`, { | |
57 | method: 'POST', | |
58 | headers: { | |
59 | Authorization: `Bearer ${internals.tootToken}`, | |
60 | }, | |
61 | body | |
62 | }); | |
63 | }, | |
64 | ||
65 | async getBookmarks() { | |
66 | ||
67 | const url = join(internals.apiUrl, 'bookmarks') + '?q=%23linkblog'; | |
68 | const response = await fetch(url, { | |
69 | headers: { | |
70 | 'Content-Type': 'application/json', | |
71 | Authorization: `Token ${internals.apiToken}` | |
72 | } | |
73 | }); | |
74 | const data = await response.json(); | |
75 | ||
76 | return data.results; | |
77 | }, | |
78 | ||
79 | async updateBookmark(bookmark) { | |
80 | ||
81 | const url = join(internals.apiUrl, 'bookmarks', `${bookmark.id}`) | |
82 | const response = await fetch(url, { | |
83 | method: 'PATCH', | |
84 | body: JSON.stringify( | |
85 | { | |
86 | tag_names: bookmark.tag_names.map((tag) => tag === 'linkblog' ? 'linkblog-posted' : tag) | |
87 | } | |
88 | ), | |
89 | headers: { | |
90 | 'Content-Type': 'application/json', | |
91 | Authorization: `Token ${internals.apiToken}` | |
92 | } | |
93 | }); | |
94 | } | |
95 | }; | |
96 | ||
97 | ||
98 | async function run() { | |
99 | const bookmarks = await internals.getBookmarks(); | |
100 | ||
101 | if (bookmarks.length === 0) { | |
102 | console.error('No links to post'); | |
103 | return; | |
104 | } | |
105 | ||
106 | const title = internals.getTitle(bookmarks); | |
107 | const text = internals.getText(bookmarks); | |
108 | const gemtext = internals.generateGemtext(title, text); | |
109 | const filename = internals.slugify(title); | |
110 | ||
111 | const gemfile = resolve(join(__dirname, `${filename}.gmi`)); | |
112 | await writeFile(gemfile, gemtext); | |
113 | await internals.exec(`blog add ${gemfile}`); | |
114 | await internals.exec(`blog publish ${internals.blogUrl}`); | |
115 | await internals.exec(`blog publish-archive ${internals.archiveUrl}`); | |
116 | await rm(gemfile); | |
117 | ||
118 | for (const bookmark of bookmarks) { | |
119 | await internals.updateBookmark(bookmark); | |
120 | } | |
121 | ||
122 | if (internals.tootToken) { | |
123 | await internals.toot(title); | |
124 | } | |
125 | } | |
126 | ||
127 | run() | |
128 | .then(() => process.exit(0)) | |
129 | .catch((err) => { | |
130 | console.error(err); | |
131 | process.exit(1); | |
132 | }) |