// #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "common.h" // clang-format off /* { "postId": 314507, "headline": "buy my stickers :0", "publishedAt": "2022-11-16T00:38:09.230Z", "filename": "314507-buy-my-stickers-0", "transparentShareOfPostId": null, "shareOfPostId": null, "tags": [], "blocks": [ { "type": "attachment", "attachment": { "attachmentId": "1c12f496-fa1a-46c7-aa02-a560a6af0d24", "altText": "A small black anodised aluminium laptop on a dark wood floor in direct hard summer sun. The edges of the sticker meld into the texture of the chassis in the highlight, and are lost in the shadow", "previewURL": "https://staging.cohostcdn.org/attachment/1c12f496-fa1a-46c7-aa02-a560a6af0d24/DSC_7491ccc.jpg", "fileURL": "https://staging.cohostcdn.org/attachment/1c12f496-fa1a-46c7-aa02-a560a6af0d24/DSC_7491ccc.jpg", "width": 5151, "height": 2897 } }, { "type": "attachment", "attachment": { "attachmentId": "6125e01e-bd98-4199-a469-aefc37777035", "altText": "Another shot of the same, but now it has a comparatively large sticker reading \"XEROX SIGMA 9 BALLS\" in the vacuum column sexion of an XDS XEROX SIGMA 9 tape-deck model", "previewURL": "https://staging.cohostcdn.org/attachment/6125e01e-bd98-4199-a469-aefc37777035/DSC_0807.JPG", "fileURL": "https://staging.cohostcdn.org/attachment/6125e01e-bd98-4199-a469-aefc37777035/DSC_0807.JPG", "width": 3680, "height": 5520 } }, { "type": "markdown", "markdown": { "content": "They are fun!¹ and [factually inaccurate](https://nabijaczleweli.xyz/content/blogn_t/006-UNIX-r-ATT.html)." } }, { "type": "markdown", "markdown": { "content": "As seen on https://store.nabijaczleweli.xyz." } }, { "type": "markdown", "markdown": { "content": "¹ According to user testimonials. Actual experience may vary. This is not renal advice." } }, { "type": "markdown", "markdown": { "content": "¹ Also available without balls." } }, { "type": "markdown", "markdown": { "content": "[!['¹UNIX® is a registered trademark of AT&T.' sharp, antiquated white font on black background](https://store.nabijaczleweli.xyz/assets/001-UNIX-tp+r-sp-120dpcm.png)](https://store.nabijaczleweli.xyz/001-UNIX-r-ATT.html)" } } ], "numComments": 0, "postingProject": { "projectId": 64525, "handle": "nabijaczleweli", "displayName": "наб", "avatarURL": "https://staging.cohostcdn.org/avatar/64525-86aae4bf-ce4c-46c4-818f-62b37dc26506-profile.png" } } */ // clang-format on struct blocks { struct attachment { std::optional altText; std::string fileURL; std::optional width; std::optional height; }; std::vector attachments; std::vector markdowns; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(blocks::attachment, altText, fileURL, width, height) void to_json(json & j, const blocks & b) { abort(); } void from_json(const json & j, blocks & b) { assert(j.is_array()); auto & a = j.get_ref(); auto i = std::begin(a); while(i != std::end(a) && (*i)["type"] == "attachment") { b.attachments.emplace_back((*i)["attachment"].template get()); ++i; } while(i != std::end(a)) { b.markdowns.emplace_back((*i)["markdown"]["content"].template get()); ++i; } } struct post { unsigned postId; // 314507 std::string headline; // "buy my stickers :0" std::string publishedAt; // "2022-11-16T00:38:09.230Z" std::string filename; // "314507-buy-my-stickers-0" std::string plainTextBody; std::optional transparentShareOfPostId; // null std::optional shareOfPostId; // null unsigned numComments; std::vector tags; // [] struct blocks blocks; // [] struct poster postingProject; // {...} }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(post, postId, headline, publishedAt, filename, plainTextBody, transparentShareOfPostId, shareOfPostId, numComments, tags, blocks, postingProject) void eject(post & p, std::vector & ps, int memfd, std::map & shared, std::vector & shared_only) { if(shared[p.postId]) return; printf("
\n", p.filename.data()); if(!p.transparentShareOfPostId) if(p.shareOfPostId) { // fprintf(stderr, "want %u\n", *p.shareOfPostId); auto sp = std::find_if(std::begin(ps), std::end(ps), [&](auto && pp) { return pp.postId == *p.shareOfPostId; }); if(sp == std::end(ps)) { sp = std::find_if(std::begin(shared_only), std::end(shared_only), [&](auto && pp) { return pp.postId == *p.shareOfPostId; }); assert(sp != std::end(shared_only)); } if(!shared[*p.shareOfPostId]) { eject(*sp, ps, memfd, shared, shared_only); shared[*p.shareOfPostId] = p.postId; } else { printf("
\n", sp->filename.data()); printf(" (another share of %1$s)\n", sp->filename.data()); printf("
\n"); } } printf("
\n", p.postId); auto user = uid_maps.find(p.postingProject.projectId); if(user != std::end(uid_maps)) printf("
\"\" %s
\n", user->second, p.postingProject.avatarURL.data(), (p.postingProject.displayName.empty() ? p.postingProject.handle : p.postingProject.displayName).data()); else printf("
\"\" %s
\n", p.postingProject.handle.data(), p.postingProject.avatarURL.data(), (p.postingProject.displayName.empty() ? p.postingProject.handle : p.postingProject.displayName).data()); printf(" \n", p.postId, p.publishedAt.data(), p.publishedAt.data(), p.publishedAt.data()); if(p.numComments) printf(" \n", p.postId, p.numComments); if(p.shareOfPostId && !p.transparentShareOfPostId) printf(" \n", *p.shareOfPostId); printf("
\n"); if(p.transparentShareOfPostId) { auto sp = std::find_if(std::begin(shared_only), std::end(shared_only), [&](auto && pp) { return pp.postId == *p.transparentShareOfPostId; }); assert(sp != std::end(shared_only)); eject(*sp, ps, memfd, shared, shared_only); } else { if(!p.headline.empty()) printf("

%s

\n", p.postId, p.headline.data()); if(!p.blocks.attachments.empty()) { printf(" \n"); } if(!p.blocks.markdowns.empty()) { printf("
\n"); markdownise(p.plainTextBody, parse_iso_like(p.publishedAt), memfd); printf("
\n"); } if(!p.tags.empty()) { printf("
\n"); for(auto && t : p.tags) printf(" #%s\n", t.data()); printf("
\n"); } } printf("
\n"); } int main() { sendfile(1, open("generator.pre-frag", O_RDONLY), nullptr, 128 * 1024 * 1024); int memfd = memfd_create(__FILE__, 0); std::map shared; // shared_id -> first_sharing auto ps = json::parse(stdin).template get>(); auto shared_only = json::parse(fdopen(3, "r")).template get>(); auto enc = [&](auto & p) { htmlencode(p.headline); for(auto && t : p.tags) htmlencode(t); for(auto && a : p.blocks.attachments) if(a.altText) htmlencode(*a.altText, true); // for(auto && m : p.blocks.markdowns) // htmlencode(m); }; for(auto && p : ps) enc(p); for(auto && p : shared_only) enc(p); std::fprintf(stderr, "%zu posts", ps.size()); for(auto && p : ps) { std::fputc('.', stderr); eject(p, ps, memfd, shared, shared_only); } std::fputc('\n', stderr); }