Add catfishing endpoint and UI

This commit is contained in:
Joscha 2025-09-14 16:52:52 +02:00
parent 804000f681
commit 746d4b9fee
8 changed files with 353 additions and 0 deletions

View file

@ -0,0 +1,49 @@
<script setup lang="ts">
import { useApiRequest } from "@/apiRequest";
import { ref, useTemplateRef } from "vue";
import CError from "./CError.vue";
const { disabled, error, makeRequest } = useApiRequest();
const form = useTemplateRef<HTMLFormElement>("form");
const day = ref<number>(448);
const feed = ref(true);
function submit() {
const data = new URLSearchParams();
if (typeof day.value === "number") data.append("day", day.value.toFixed());
data.append("feed", String(feed.value));
void makeRequest("api/catfishing", data);
}
</script>
<template>
<form ref="form" @submit.prevent="submit">
<h2>catfishing</h2>
<label class="wide">
Day:
<input v-model="day" type="number" min="1" :disabled />
</label>
<div class="wide">
<label><input v-model="feed" type="checkbox" :disabled /> Feed</label>
</div>
<button :disabled>Print</button>
<CError :message="error" />
</form>
</template>
<style scoped>
form {
display: flex;
flex-direction: column;
gap: 16px;
}
.wide {
display: flex;
flex-direction: column;
}
</style>

View file

@ -2,6 +2,7 @@
import { ref } from "vue";
import CDocumentBanner from "./CDocumentBanner.vue";
import CDocumentCalendar from "./CDocumentCalendar.vue";
import CDocumentsCatfishing from "./CDocumentCatfishing.vue";
import CDocumentCells from "./CDocumentCells.vue";
import CDocumentChat from "./CDocumentChat.vue";
import CDocumentEgg from "./CDocumentEgg.vue";
@ -14,6 +15,7 @@ import CDocumentXkcd from "./CDocumentXkcd.vue";
const mode = ref<
| "banner"
| "calendar"
| "catfishing"
| "cells"
| "chat"
| "egg"
@ -30,6 +32,7 @@ const mode = ref<
<button v-if="mode" class="close" @click="mode = undefined">X</button>
<CDocumentBanner v-if="mode === 'banner'" />
<CDocumentCalendar v-if="mode === 'calendar'" />
<CDocumentsCatfishing v-if="mode === 'catfishing'" />
<CDocumentCells v-if="mode === 'cells'" />
<CDocumentChat v-if="mode === 'chat'" />
<CDocumentEgg v-if="mode === 'egg'" />
@ -44,6 +47,7 @@ const mode = ref<
<div class="list">
<button @click="mode = 'banner'">Banner</button>
<button @click="mode = 'calendar'">Calendar</button>
<button @click="mode = 'catfishing'">Catfishing</button>
<button @click="mode = 'cells'">Cellular Automaton</button>
<button @click="mode = 'chat'">Chat Message</button>
<button @click="mode = 'egg'">Easter Egg</button>

View file

@ -10,6 +10,7 @@ pub mod sunrise;
pub mod text;
pub mod tictactoe;
pub mod xkcd;
pub mod catfishing;
fn typst_with_lib() -> Typst {
Typst::new().with_file("/lib/main.typ", include_str!("documents/lib/main.typ"))

View file

@ -0,0 +1,223 @@
{
"day": 448,
"articles": [
{
"title": "Met Gala",
"categories": [
"1948 establishments in New York City",
"Annual events in New York City",
"Balls in the United States",
"Fashion events in the United States",
"Metropolitan Museum of Art",
"Recurring events established in 1948",
"Vogue (magazine)"
]
},
{
"title": "Auto-Tune",
"categories": [
"1990s in music",
"2000s in music",
"2010s in music",
"Audiovisual introductions in 1997",
"Effects units",
"Music controversies",
"Pitch modification software"
]
},
{
"title": "Claus von Stauffenberg",
"categories": [
"1907 births",
"1944 deaths",
"Counts in Germany",
"Executed failed assassins of Adolf Hitler",
"Executed German assassins",
"Executed members of the 20 July plot",
"Executed military personnel",
"Eyepatch wearers",
"Failed assassins of Heinrich Himmler",
"German amputees",
"German anti-communists",
"German Army officers of World War II",
"German blind people",
"German military personnel who were court-martialed",
"German nationalist assassins",
"German Roman Catholics",
"Military personnel from the Kingdom of Bavaria",
"People educated at Eberhard-Ludwigs-Gymnasium",
"People executed by Nazi Germany by firing squad",
"People from Bavaria executed by Nazi Germany",
"People from Günzburg (district)",
"Recipients of the Gold German Cross",
"Reichswehr personnel",
"Roman Catholics in the German Resistance"
]
},
{
"title": "Apollo Theater",
"categories": [
"1910s architecture in the United States",
"1914 establishments in New York City",
"African-American arts organizations",
"African-American culture",
"African-American theatre",
"Black theatre",
"Burlesque theatres",
"George Keister buildings",
"Historically African-American theaters and music venues",
"Music venues in Manhattan",
"Neoclassical architecture in New York City",
"New York City Designated Landmarks in Manhattan",
"New York City interior landmarks",
"Theatres completed in 1914",
"Theatres in Harlem",
"Theatres on the National Register of Historic Places in Manhattan",
"Tourist attractions in Manhattan"
]
},
{
"title": "Atomium",
"categories": [
"Buildings and structures completed in 1958",
"Buildings and structures in Brussels",
"Copyright infringement",
"Expo 58",
"Modernist architecture",
"Modernist architecture in Belgium",
"Symbols of Brussels",
"Tourist attractions in Brussels",
"World's fair architecture in Belgium"
]
},
{
"title": "Manufacturing Consent",
"categories": [
"1988 non-fiction books",
"Books about media bias",
"Books about politics of the United States",
"Books about propaganda",
"Books about public opinion",
"Books by Edward S. Herman",
"Books by Noam Chomsky",
"Collaborative non-fiction books",
"English-language non-fiction books",
"Non-fiction books adapted into films",
"Pantheon Books books",
"Works about the information economy"
]
},
{
"title": "The Shining (film)",
"categories": [
"1980 films",
"1980 horror films",
"1980s American films",
"1980s British films",
"1980s English-language films",
"1980s ghost films",
"American haunted house films",
"American psychological horror films",
"British haunted house films",
"British psychological horror films",
"English-language horror films",
"Fiction about familicide",
"Fiction about haunted houses",
"Films about alcoholism",
"Films about domestic violence",
"Films about dysfunctional families",
"Films about father-son relationships",
"Films about mother-son relationships",
"Films about telepathy",
"Films about twin sisters",
"Films about writers",
"Films based on American horror novels",
"Films based on works by Stephen King",
"Films directed by Stanley Kubrick",
"Films produced by Stanley Kubrick",
"Films scored by Wendy Carlos",
"Films set during snowstorms",
"Films set in Colorado",
"Films set in hotels",
"Films set in mazes",
"Films shot at EMI-Elstree Studios",
"Films shot in chronological order",
"Films shot in Hertfordshire",
"Films shot in Montana",
"Films shot in Oregon",
"Films with screenplays by Diane Johnson",
"Films with screenplays by Stanley Kubrick",
"Saturn Award-winning films",
"United States National Film Registry films",
"Warner Bros. films"
]
},
{
"title": "Witch of Endor",
"categories": [
"11th-century BC women",
"Ancient occultists",
"Necromancy",
"People from the Kingdom of Israel (united monarchy)",
"Samuel",
"Saul",
"Spiritual mediums",
"Unnamed people of the Bible",
"Ventriloquists",
"Women in the Hebrew Bible"
]
},
{
"title": "Nowruz",
"categories": [
"Ancient Iranian religion",
"Culture of Iran",
"Festivals in Albania",
"Festivals in Azerbaijan",
"Festivals in Georgia (country)",
"Festivals in Iran",
"Festivals in Iraq",
"Festivals in Kazakhstan",
"Festivals in Kosovo",
"Festivals in Kyrgyzstan",
"Festivals in Russia",
"Festivals in Syria",
"Festivals in Turkey",
"Festivals in Uzbekistan",
"Goldfish in culture",
"Intangible Cultural Heritage of India",
"March observances",
"Masterpieces of the Oral and Intangible Heritage of Humanity",
"National symbols of Iran",
"New Year celebrations",
"Observances on non-Gregorian calendars",
"Observances set by the Solar Hijri calendar",
"Persian culture",
"Persian festivals",
"Persian words and phrases",
"Public holidays in Afghanistan",
"Public holidays in Albania",
"Public holidays in Azerbaijan",
"Public holidays in Georgia (country)",
"Public holidays in Iraq",
"Public holidays in Kazakhstan",
"Public holidays in Kyrgyzstan",
"Public holidays in Uzbekistan",
"Spring (season) in Iran",
"Spring equinox",
"Zoroastrian festivals"
]
},
{
"title": "Nagorno-Karabakh",
"categories": [
"Armenia-Azerbaijan relations",
"Armenian irredentism",
"Enclaves and exclaves",
"Historical regions in Azerbaijan",
"Subdivisions of Azerbaijan"
]
}
],
"feed": false
}

View file

@ -0,0 +1 @@
../lib

View file

@ -0,0 +1,19 @@
#import "lib/main.typ" as lib;
#show: it => lib.init(it)
#let data = json("data.json")
= Catfishing (day #data.day)
#for article in data.articles [
#v(24pt)
#for cat in article.categories [
- #cat
]
#v(24pt)
#line(length: 100%)
]
#if data.feed {
lib.feed
}

View file

@ -0,0 +1,55 @@
use axum::{
Form,
extract::State,
response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
use crate::server::{Server, somehow};
#[derive(Serialize, Deserialize)]
struct ArticleInfo {
title: String,
categories: Vec<String>,
}
#[derive(Serialize)]
struct Data {
day: u32,
articles: Vec<ArticleInfo>,
feed: bool,
}
#[derive(Deserialize)]
pub struct FormData {
pub day: u32,
pub feed: bool,
}
pub async fn post(server: State<Server>, Form(form): Form<FormData>) -> somehow::Result<Response> {
let client = reqwest::Client::builder()
.user_agent(crate::USER_AGENT)
.build()?;
let url = format!("https://static.catfishing.net/daily/{}.json", form.day);
let articles = client
.get(url)
.send()
.await?
.json::<Vec<ArticleInfo>>()
.await?;
let data = Data {
day: form.day,
articles,
feed: form.feed,
};
let typst = super::typst_with_lib()
.with_json("/data.json", &data)
.with_main_file(include_str!("main.typ"));
server.print_typst(typst).await?;
Ok(().into_response())
}

View file

@ -45,6 +45,7 @@ pub async fn run(
// API
.route("/api/banner", post(documents::banner::post))
.route("/api/calendar", post(documents::calendar::post))
.route("/api/catfishing", post(documents::catfishing::post))
.route("/api/cells", post(documents::cells::post))
.route("/api/chat", post(documents::chat::post))
.route("/api/egg", post(documents::egg::post))