]>
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 | return `=> ${post.url} ${post.title}\n${post.notes}`; | |
30 | }).join('\n\n'); | |
31 | }, | |
32 | ||
33 | getTitle(posts) { | |
34 | ||
35 | if (posts.length === 1) { | |
36 | return `Link: ${posts[0].title}`; | |
37 | } | |
38 | return `${posts.length} links for ${internals.date}`; | |
39 | }, | |
40 | ||
41 | slugify(text) { | |
42 | ||
43 | return text.toLowerCase().replace(/[^a-z0-9 ]/g, '').replace(/ +/g, '-') | |
44 | }, | |
45 | ||
46 | async toot(title) { | |
47 | ||
48 | const body = new FormData(); | |
49 | body.set( | |
50 | 'status', | |
51 | `New post: ${title}\n\nAvailable on:\n\nāļø the gemini archive ${internals.archivePublicUrl}\n\n or, the ephemeral blog š: ${internals.blogPublicUrl}` | |
52 | ); | |
53 | return fetch(`https://${internals.mastodonDomain}/api/v1/statuses`, { | |
54 | method: 'POST', | |
55 | headers: { | |
56 | Authorization: `Bearer ${internals.tootToken}`, | |
57 | }, | |
58 | body | |
59 | }); | |
60 | }, | |
61 | ||
62 | async getBookmarks() { | |
63 | ||
64 | const url = join(internals.apiUrl, 'bookmarks') + '?q=%23linkblog'; | |
65 | const response = await fetch(url, { | |
66 | headers: { | |
67 | 'Content-Type': 'application/json', | |
68 | Authorization: `Token ${internals.apiToken}` | |
69 | } | |
70 | }); | |
71 | const data = await response.json(); | |
72 | ||
73 | return data.results; | |
74 | }, | |
75 | ||
76 | async updateBookmark(bookmark) { | |
77 | ||
78 | console.log(bookmark.tag_names); | |
79 | console.log(bookmark.tag_names.map((tag) => tag === 'linkblog' ? 'linkblog-posted' : tag)); | |
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 | const data = await response.text(); | |
96 | console.log(url); | |
97 | console.log(data); | |
98 | } | |
99 | }; | |
100 | ||
101 | ||
102 | async function run() { | |
103 | const bookmarks = await internals.getBookmarks(); | |
104 | ||
105 | if (bookmarks.length === 0) { | |
106 | console.error('No links to post'); | |
107 | return; | |
108 | } | |
109 | ||
110 | const title = internals.getTitle(bookmarks); | |
111 | const text = internals.getText(bookmarks); | |
112 | const gemtext = internals.generateGemtext(title, text); | |
113 | const filename = internals.slugify(title); | |
114 | ||
115 | const gemfile = resolve(join(__dirname, `${filename}.gmi`)); | |
116 | await writeFile(gemfile, gemtext); | |
117 | await internals.exec(`blog add ${gemfile}`); | |
118 | await internals.exec(`blog publish ${internals.blogUrl}`); | |
119 | await internals.exec(`blog publish-archive ${internals.archiveUrl}`); | |
120 | await rm(gemfile); | |
121 | ||
122 | for (const bookmark of bookmarks) { | |
123 | await internals.updateBookmark(bookmark); | |
124 | } | |
125 | ||
126 | if (internals.tootToken) { | |
127 | await internals.toot(title); | |
128 | } | |
129 | } | |
130 | ||
131 | run() | |
132 | .then(() => process.exit(0)) | |
133 | .catch((err) => { | |
134 | console.error(err); | |
135 | process.exit(1); | |
136 | }) |