summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/e621/index.ts50
-rw-r--r--src/api/mastodon/index.ts58
-rw-r--r--src/api/misskey/index.ts26
-rw-r--r--src/config.ts34
-rw-r--r--src/index.ts36
5 files changed, 204 insertions, 0 deletions
diff --git a/src/api/e621/index.ts b/src/api/e621/index.ts
new file mode 100644
index 0000000..6aa6a35
--- /dev/null
+++ b/src/api/e621/index.ts
@@ -0,0 +1,50 @@
1import got from "got";
2import config from "../../config";
3
4export interface Post {
5 id: number;
6 file: {
7 url: string;
8 };
9 sources: readonly string[];
10
11 tags: {
12 general: readonly string[];
13 species: readonly string[];
14 character: readonly string[];
15 copyright: readonly string[];
16 artist: readonly string[];
17 invalid: readonly string[];
18 lore: readonly string[];
19 meta: readonly string[];
20 };
21}
22
23export const client = got.extend({
24 headers: {
25 "User-Agent": config.e621.userAgent,
26 },
27});
28
29export async function randomPost() {
30 const page = Math.floor(Math.random() * (config.e621.maxPage - 1)) + 1;
31
32 const response = await client
33 .get("https://e926.net/posts.json", {
34 searchParams: {
35 limit: 75,
36 page,
37 tags: config.e621.tags.join(" "),
38 },
39 })
40 .json<{ posts: readonly Post[] }>();
41
42 if (!response.posts.length) {
43 throw new Error("No posts received");
44 }
45
46 const postIndex = Math.floor(Math.random() * response.posts.length);
47 const post = response.posts[postIndex];
48
49 return post;
50}
diff --git a/src/api/mastodon/index.ts b/src/api/mastodon/index.ts
new file mode 100644
index 0000000..2d8636e
--- /dev/null
+++ b/src/api/mastodon/index.ts
@@ -0,0 +1,58 @@
1import got from "got";
2import FormData from "form-data";
3import fileType from "file-type";
4import { nanoid } from "nanoid";
5import config from "../../config";
6
7export interface Attachment {
8 id: string;
9}
10
11export interface Status {
12 url: string;
13}
14
15export const client = got.extend({
16 prefixUrl: config.mastodon.instance,
17 headers: {
18 Authorization: `Bearer ${config.mastodon.token}`,
19 },
20});
21
22export async function upload(buf: Buffer, filename: string) {
23 const type = await fileType.fromBuffer(buf);
24
25 const body = new FormData();
26 body.append("i", config.mastodon.token);
27 body.append("file", buf, { filename: `${filename}.${type.ext}`, contentType: type.mime });
28
29 return client.post("api/v1/media", { body }).json<Attachment>();
30}
31
32export async function createStatus(
33 postUrl: string,
34 sourceUrl: string | undefined,
35 spoiler: string[],
36 attachmentId: string
37) {
38 let lines = [postUrl];
39 if (sourceUrl) {
40 lines.push(`Source: ${sourceUrl}`);
41 }
42
43 const spoilerText = spoiler.length ? `CW: ${spoiler.join(", ")}` : undefined;
44
45 return client
46 .post("api/v1/statuses", {
47 headers: {
48 "Idempotency-Key": nanoid(),
49 },
50 json: {
51 status: lines.join("\n"),
52 media_ids: [attachmentId],
53 sensitive: true,
54 spoiler_text: spoilerText,
55 },
56 })
57 .json<Status>();
58}
diff --git a/src/api/misskey/index.ts b/src/api/misskey/index.ts
new file mode 100644
index 0000000..64bfe67
--- /dev/null
+++ b/src/api/misskey/index.ts
@@ -0,0 +1,26 @@
1/*
2import got from "got";
3import FormData from "form-data";
4import stream from "stream";
5import config from "../../config";
6
7export interface DriveFile {
8 id: string;
9}
10
11export const client = got.extend({
12 prefixUrl: config.misskey.instance,
13});
14
15export function upload(buf: Buffer, filename: string) {
16 const body = new FormData();
17 body.append("i", config.misskey.token);
18 body.append("file", buf, { filename });
19
20 return client.post("drive/files/create", { body }).json<DriveFile>();
21};
22
23export function createNote() {
24
25}
26*/
diff --git a/src/config.ts b/src/config.ts
new file mode 100644
index 0000000..f051888
--- /dev/null
+++ b/src/config.ts
@@ -0,0 +1,34 @@
1export default {
2 e621: {
3 userAgent: "@feralbot@botsin.space (by RedFoxxo)",
4 tags: [
5 "feral",
6 "-anthro",
7 "-human",
8 "-meme",
9 "-humor",
10 "-photography_(artwork)",
11 "-portrait",
12 "-comic",
13 "-saliva",
14 "-friendship_is_magic",
15 "-my_little_pony",
16 "-type:swf",
17 "-type:webm",
18 "-type:gif",
19 "status:active",
20 "score:>=20",
21 "inpool:false",
22 ],
23 maxPage: 131,
24 },
25 /*misskey: {
26 instance: "https://mk.vulpes.one/",
27 token: process.env.MISSKEY_TOKEN,
28 },*/
29 mastodon: {
30 instance: "https://botsin.space/",
31 token: process.env.MASTODON_TOKEN,
32 },
33 cw: ["gun"],
34};
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..7185e23
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,36 @@
1import * as e621 from "./api/e621";
2import * as mastodon from "./api/mastodon";
3import config from "./config";
4
5(async () => {
6 console.log("Fetching post...");
7
8 const post = await e621.randomPost();
9 const source = post.sources.length ? post.sources[0] : undefined;
10 const cws = config.cw.filter((w) => post.tags.general.includes(w));
11
12 console.log(`Got ${post.id}`);
13 console.log(`Downloading image...`);
14
15 const file = await e621.client.get(post.file.url).buffer();
16
17 /*console.log(`Compressing...`);
18
19 const compressedFile = await sharp(file)
20 .resize(1000, 1000, {
21 fit: "inside",
22 withoutEnlargement: true,
23 })
24 .jpeg({ quality: 85, mozjpeg: true })
25 .toBuffer();*/
26
27 console.log(`Uploading...`);
28
29 const attachment = await mastodon.upload(file, post.id.toString(10));
30
31 console.log(`Posting status...`);
32
33 const status = await mastodon.createStatus(`https://e926.net/posts/${post.id}`, source, cws, attachment.id);
34
35 console.log(`Done! ${status.url}`);
36})();