From 6dd8a9231822b5b9e453e4055ecc06ac64e44009 Mon Sep 17 00:00:00 2001 From: Bad Manners <me@badmanners.xyz> Date: Thu, 8 Aug 2024 09:49:11 -0300 Subject: [PATCH] Add posts to latest story --- README.md | 2 +- package-lock.json | 5 +++-- package.json | 3 ++- scripts/export-story.ts | 27 ++++++++++++------------- src/content/config.ts | 20 ++++++++++++------ src/content/stories/playing-it-safe.md | 21 ++++++++++++++----- src/i18n/index.ts | 2 +- src/pages/api/export-story/[...slug].ts | 2 +- 8 files changed, 51 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index eec5f18..0878ec1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Static website built in Astro + Typescript + TailwindCSS. ```bash git clone https://git.badmanners.xyz/badmanners/gallery.badmanners.xyz cd gallery.badmanners.xyz -npm install && npm run sync +npm install ``` ### Local development diff --git a/package-lock.json b/package-lock.json index 96648c9..d6b5889 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,13 @@ { "name": "gallery-badmanners-xyz", - "version": "1.6.1", + "version": "1.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gallery-badmanners-xyz", - "version": "1.6.1", + "version": "1.6.2", + "hasInstallScript": true, "dependencies": { "@astrojs/check": "^0.9.2", "@astrojs/rss": "^4.0.7", diff --git a/package.json b/package.json index 58ef62a..8dfb0fb 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "gallery-badmanners-xyz", "type": "module", - "version": "1.6.1", + "version": "1.6.2", "scripts": { + "postinstall": "astro sync", "dev": "astro dev", "start": "astro dev", "build": "astro check && astro build", diff --git a/scripts/export-story.ts b/scripts/export-story.ts index 178ce38..9290f10 100644 --- a/scripts/export-story.ts +++ b/scripts/export-story.ts @@ -51,7 +51,7 @@ const isLibreOfficeRunning = async () => async function exportStory(slug: string, options: { outputDir: string }) { /* Check that LibreOffice is not running */ if (await isLibreOfficeRunning()) { - console.error("LibreOffice cannot be open while this command is running!"); + console.error("ERROR: LibreOffice cannot be open while this command is running!"); process.exit(1); } /* Check that outputDir is valid */ @@ -63,8 +63,8 @@ async function exportStory(slug: string, options: { outputDir: string }) { files = []; console.log(`Created directory at ${await mkdir(outputDir, { recursive: true })}`); } - if (files.length > 1) { - console.error(`Directory ${outputDir} is not empty!`); + if (files.length > 0) { + console.error(`ERROR: Directory ${outputDir} is not empty!`); process.exit(1); } /* Check if Astro development server needs to be spawned */ @@ -94,8 +94,8 @@ async function exportStory(slug: string, options: { outputDir: string }) { throw new Error(); } } catch { - console.error("Astro dev server didn't respond in time!"); - devServerProcess && devServerProcess.kill("SIGINT"); + console.error("ERROR: Astro dev server didn't respond in time!"); + devServerProcess && devServerProcess.kill(); devServerProcess = null; process.exit(1); } @@ -138,8 +138,8 @@ async function exportStory(slug: string, options: { outputDir: string }) { } finally { if (devServerProcess) { console.log("Shutting down the Astro development server..."); - if (!devServerProcess.kill("SIGINT")) { - console.error("Unable to shut down Astro dev server!"); + if (!devServerProcess.kill("SIGTERM")) { + console.error("WARNING: Unable to shut down Astro dev server!"); } devServerProcess = null; } @@ -150,13 +150,11 @@ async function exportStory(slug: string, options: { outputDir: string }) { await writeFile(pathJoin(outputDir, `${slug}.md`), storyText.replaceAll(/=(?==)/g, "= ").replaceAll("*", "\\*")); const tempDir = await mkdtemp(pathJoin(tmpdir(), "export-story-")); await writeFile(pathJoin(tempDir, "temp.txt"), storyText.replaceAll(/\n\n+/g, "\n")); - spawnSync("libreoffice", [ - "--convert-to", - "rtf:Rich Text Format", - "--outdir", - tempDir, - pathJoin(tempDir, "temp.txt"), - ]); + spawnSync( + "libreoffice", + ["--convert-to", "rtf:Rich Text Format", "--outdir", tempDir, pathJoin(tempDir, "temp.txt")], + { stdio: "ignore" }, + ); const rtfText = await readFile(pathJoin(tempDir, "temp.rtf"), "utf-8"); const rtfStyles = getRTFStyles(rtfText); await writeFile( @@ -164,6 +162,7 @@ async function exportStory(slug: string, options: { outputDir: string }) { rtfText.replaceAll(rtfStyles["Preformatted Text"], rtfStyles["Normal"]), ); console.log("Success!"); + process.exit(0); } await program diff --git a/src/content/config.ts b/src/content/config.ts index 4a8ba48..0a889cb 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -29,12 +29,14 @@ function parseRegex<R extends { [key: string]: string }>(regex: RegExp) { * from `website` - a pre-parsed object containing the link and username. */ const websiteLinks = z.object({ - website: z - .string() - .url() - .transform((link) => { - link; - }), + website: z.object({ link: z.string().url() }).or( + z + .string() + .url() + .transform((link) => { + link; + }), + ), eka: z.object({ link: z.string().url(), username: z.string() }).or( z.string().transform((link, ctx) => { const { username } = parseRegex<{ username: string }>( @@ -189,6 +191,12 @@ const publishedContent = z.object({ )(link, ctx); return { link, postId }; }), + twitter: z.string().transform((link, ctx) => { + const { user, postId } = parseRegex<{ user: string; postId: string }>( + /^(?:https?:\/\/)(?:www\.)?(?:twitter\.com|x\.com)\/(?<user>[a-zA-Z0-9_-]+)\/status\/(?<postId>[1-9]\d*)\/?$/, + )(link, ctx); + return { link, user, postId }; + }), bluesky: z.string().transform((link, ctx) => { const { user, postId } = parseRegex<{ user: string; postId: string }>( /^(?:https?:\/\/)bsky\.app\/profile\/(?<user>(?:[a-zA-Z0-9_-]+\.)+[a-z]+)\/post\/(?<postId>[a-z0-9]+)\/?$/, diff --git a/src/content/stories/playing-it-safe.md b/src/content/stories/playing-it-safe.md index 436148f..0b956c0 100644 --- a/src/content/stories/playing-it-safe.md +++ b/src/content/stories/playing-it-safe.md @@ -5,12 +5,21 @@ pubDate: 2024-08-08 authors: bad-manners wordCount: 9900 contentWarning: > - Contains: Size difference safe vore/endosoma (cock vore, unbirth, oral vore), with willing male anthro rhinoceros predator, willing female anthro beaver predator, and semi-willing male anthro squirrel prey. Also includes straight sex, prey transfer, condom filling with inflation and vore, implied full tour, office setting, and many puns. + Contains: Size difference safe vore/endosoma (cock vore, unbirth, oral vore), with willing male anthro rhinoceros predator, willing female anthro beaver predator, and mostly willing male anthro squirrel prey. Also includes straight sex, prey transfer, condom filling with inflation and vore, implied full tour, office setting, and many puns. thumbnail: /src/assets/thumbnails/bm_20_playing_it_safe.png description: | A white-collar squirrel asks his boss about a raise, but he gets far more than he bargained for. - Another self-indulgent story in time for Vore Day, as I work on more stuff on the side! I didn't mark this one as "non-fatal vore" (except for the tags), but the content is in line with everything that I've written and will write, i.e. without fatality or digestion. It's just that I currently prefer the terms "safe vore" and "endosoma", since they are more descriptive and both affirmations instead of a negation, and I'll probably keep using them going forward. + Another self-indulgent story in time for Vore Day, as I work on more stuff on the side! I didn't mark this one as "non-fatal vore", but the content is in line with everything that I've written so far. It's just that I currently prefer the terms "safe vore" and "endosoma", since they are more descriptive when combined (and are both affirmations instead of a negation), so I'll use them together going forward. +posts: + eka: https://aryion.com/g4/view/1036741 + furaffinity: https://www.furaffinity.net/view/57675502/ + inkbunny: https://inkbunny.net/s/3391838 + sofurry: https://www.sofurry.com/view/2161474 + weasyl: https://www.weasyl.com/~badmanners/submissions/2401625/playing-it-safe + twitter: https://x.com/BadManners__/status/1821526198150680589 + bluesky: https://bsky.app/profile/badmanners.xyz/post/3kz7gvvwyvt2c + mastodon: https://meow.social/@BadManners/112926390644544264 tags: - oral vore - cock vore @@ -21,7 +30,7 @@ tags: - female predator - male prey - willing predator - - semi-willing prey + - willing prey - size difference - prey transfer - implied full tour @@ -477,9 +486,11 @@ Everything lurched forward, turning and churning as rhino seed lapped against hi As much as he tried not to wreak havoc onto the squirrel while he dressed back up, Enzo could feel the shifting fluids in his core. It'd have to be something they'd both have to grow used to. He also recovered the pants that he'd taken off before Rafa came into his office, along with his other clothes. Before fastening the belt, he carefully buttoned his shirt. And, to his surprise, his size still fit! It was definitely tighter, and it also compressed his bulge a bit more – but nobody would even be able to tell that there was something different about him. -Enzo checked his wrist and buttoned his sleeves. "Sorry about the commotion in there, Rafael. I didn't mean to TAKE-OUT on you... Either way, do you remember what day it is?" He grabbed the spent towels, and tossed them in a basket hidden by the corner of his office. "It's Thursday. And tomorrow is Friday, that's your day off." He tucked his shirt into his pants, taking the opportunity to press his hand against his soft belly. "Which means you only have to be back at the office on Monday. That's more than three days from now. More than enough for my body to push you all the way out the other end..." +Enzo checked his wrist and buttoned his sleeves. "Sorry about the commotion in there, Rafael. I didn't mean to TAKE-OUT on you... Either way, do you remember what day it is?" He grabbed the spent towels, and tossed them in a basket hidden by the corner of his office. "It's Thursday. And tomorrow is Friday, that's your day off." He tucked his shirt into his pants, taking the opportunity to press his hand against his soft belly. "Which means you only have to be back at the office on Monday. That's more than three days from now. Pretty safe to say that it's more than enough for my body to push you all the way out the other end..." -He put on his shoes, then picked his phone and car key. He could feel the writhing in reaction to his words. "Here's some FOOD for thought. I'll give you that raise I promised, plus overtime. But! If you manage to get that condom all the way through my guts, without it leaking or bursting, I'll give you an even bigger bonus to your paycheck. How does that sound? Don't worry, I'll give you plenty of time to RUMINATE on it!" +He put on his shoes, then picked his phone and car key. He could feel the writhing in reaction to his words. + +"Here's some FOOD for thought. I'll give you that raise I promised, plus overtime for your whole stay. But! If you manage to get that condom all the way through my guts, without it leaking or bursting... _And_ nobody finds out about you when I come into the office tomorrow... Then I'll give you an even bigger bonus to your paycheck. How does that sound? Don't worry, I'll give you plenty of time to RUMINATE on it!" The rhino laughed, patting his belly over his shirt. He felt some pressure coming from his gut, before he released a loud belch. He smacked his lips, noticing that his breath vaguely tasted like vanilla. diff --git a/src/i18n/index.ts b/src/i18n/index.ts index b63ef6a..d248527 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -242,7 +242,7 @@ type TranslationArgs<K extends TranslationKey> = export function t<K extends TranslationKey>(lang: Lang, key: K, ...args: TranslationArgs<K>): string { if (key in UI_STRINGS) { const translation: string | ((...args: TranslationArgs<K>) => string) = - (UI_STRINGS[key] as any)[lang] || UI_STRINGS[key][DEFAULT_LANG]; + (UI_STRINGS[key] as any)[lang] ?? UI_STRINGS[key][DEFAULT_LANG]; return typeof translation === "function" ? translation(...args) : translation; } throw new Error(`No translation entry found for key "${key}"`); diff --git a/src/pages/api/export-story/[...slug].ts b/src/pages/api/export-story/[...slug].ts index 47eea8e..4ea70e8 100644 --- a/src/pages/api/export-story/[...slug].ts +++ b/src/pages/api/export-story/[...slug].ts @@ -24,7 +24,7 @@ type ExportWebsiteName = typeof WEBSITE_LIST extends ReadonlyArray<{ website: in function getUsernameForWebsite(user: CollectionEntry<"users">, website: Website): string { const link = user.data.links[website]; - if (link?.username) { + if (link && "username" in link && link.username) { return link.username; } throw new Error(`Cannot get "${website}" username for user "${user.id}"`);