fedi-imagebot/main.ts
2023-01-03 21:18:04 +01:00

103 lines
3 KiB
TypeScript

const MASTODON_INSTANCE_URL = Deno.env.get("MASTODON_INSTANCE_URL"); // e.g. https://botsin.space
const MASTODON_ACCESS_TOKEN = Deno.env.get("MASTODON_ACCESS_TOKEN"); // you can get this at $INSTANCE_URL/settings/applications
// first we need to find out which files we can post, and which ones we already
// have posted
console.log("Picking file to post…");
const alreadyPostedFiles = new Set();
for await (const dirEntry of Deno.readDir("./posts/.posted")) {
if (dirEntry.isSymlink) {
alreadyPostedFiles.add(dirEntry.name);
}
}
const availableFiles = [];
for await (const dirEntry of Deno.readDir("./posts/")) {
if (dirEntry.name === ".gitignore") continue;
if (dirEntry.isFile && !alreadyPostedFiles.has(dirEntry.name)) {
availableFiles.push(dirEntry.name);
}
}
console.log(`Found ${availableFiles.length} files which are not yet posted`);
if (availableFiles.length === 0) {
console.log("Nothing left to post at the moment. Bye!");
Deno.exit();
}
const fileToPost =
availableFiles[Math.floor(Math.random() * availableFiles.length)];
const bytes = await Deno.readFile(`./posts/${fileToPost}`);
// we need two requests
// 1. upload media
// 2. create status with media id
// see https://docs.joinmastodon.org/methods/media/#v2
const mediaData = new FormData();
mediaData.append("file", new Blob([bytes]));
type MediaResponse = {
"id": string;
"type": string;
"url": string;
"preview_url": string;
"remote_url": null;
"text_url": string;
"meta": {
"focus": {
"x": number;
"y": number;
};
"original": {
"width": number;
"height": number;
"size": string;
"aspect": number;
};
"small": {
"width": number;
"height": number;
"size": string;
"aspect": number;
};
};
"description": string;
"blurhash": string;
};
console.log("Sending post request to", `${MASTODON_INSTANCE_URL}/api/v2/media`);
const mediaRequest = await fetch(`${MASTODON_INSTANCE_URL}/api/v2/media`, {
method: "POST",
headers: {
Authorization: `Bearer ${MASTODON_ACCESS_TOKEN}`,
},
body: mediaData,
}).then((res) => res.json()) as unknown as MediaResponse;
console.log("mediaRequest", mediaRequest);
// see https://docs.joinmastodon.org/methods/statuses/#create
const statusData = new FormData();
statusData.append("media_ids[]", mediaRequest.id);
console.log(
"Sending post request to",
`${MASTODON_INSTANCE_URL}/api/v1/statuses`,
);
const statusRequest = await fetch(`${MASTODON_INSTANCE_URL}/api/v1/statuses`, {
method: "POST",
headers: {
Authorization: `Bearer ${MASTODON_ACCESS_TOKEN}`,
},
body: statusData,
}).then((res) => res.json());
console.log("statusRequest", statusRequest);
console.log("Linking posted file so we can skip it in the future…");
// FIXME: This requires unrestricted `--allow-read` and `--allow-write` at the;
// deno should provide a finer-grained permission prompt here
await Deno.symlink(`./posts/${fileToPost}`, `./posts/.posted/${fileToPost}`);
console.log("Done!");