summaryrefslogtreecommitdiffstats
path: root/src/services
diff options
context:
space:
mode:
Diffstat (limited to 'src/services')
-rw-r--r--src/services/dedupe.ts65
-rw-r--r--src/services/jobs.ts54
2 files changed, 119 insertions, 0 deletions
diff --git a/src/services/dedupe.ts b/src/services/dedupe.ts
new file mode 100644
index 0000000..2eeb5ee
--- /dev/null
+++ b/src/services/dedupe.ts
@@ -0,0 +1,65 @@
1import fs from "fs/promises";
2import path from "path";
3import * as f from "fp-ts";
4import * as t from "io-ts";
5
6export const E926DedupeEntryC = t.type({
7 provider: t.literal("e926"),
8 id: t.number,
9});
10
11export const DedupeEntryC = E926DedupeEntryC;
12
13export type E926DedupeEntry = t.TypeOf<typeof E926DedupeEntryC>;
14
15export type DedupeEntry = t.TypeOf<typeof DedupeEntryC>;
16
17export class Dedupe {
18 private entries: DedupeEntry[] = [];
19
20 private readonly filePath: string;
21
22 private isLoaded = false;
23
24 constructor(private max: number, filename: string) {
25 this.filePath = path.join(process.cwd(), filename);
26 }
27
28 private async load() {
29 if (this.isLoaded) {
30 return;
31 }
32
33 try {
34 await fs.stat(this.filePath);
35 } catch {
36 await this.save();
37 }
38
39 const fileContent = await fs.readFile(this.filePath, "utf8");
40 const entries = t.array(DedupeEntryC).decode(fileContent);
41
42 if (f.either.isRight(entries)) {
43 this.entries = entries.right;
44 }
45 }
46
47 private async save() {
48 await fs.writeFile(this.filePath, JSON.stringify(this.entries ?? []), "utf8");
49 }
50
51 async check(entry: DedupeEntry) {
52 await this.load();
53
54 const has = !!this.entries.find((e) => e.provider === entry.provider && e.id === entry.id);
55
56 if (!has) {
57 this.entries.push(entry);
58 await this.save();
59 }
60
61 return has;
62 }
63}
64
65export default new Dedupe(50, "dedupe.json");
diff --git a/src/services/jobs.ts b/src/services/jobs.ts
new file mode 100644
index 0000000..cf3b894
--- /dev/null
+++ b/src/services/jobs.ts
@@ -0,0 +1,54 @@
1import * as e621 from "../api/e621";
2import * as mastodon from "../api/mastodon";
3import config from "../config";
4import Sharp from "sharp";
5
6export async function postRandomPicture() {
7 console.log("Fetching random post...");
8
9 const queryIndex = Math.floor(Math.random() * config.e621.queries.length);
10 const query = config.e621.queries[queryIndex];
11 const post = await e621.getRandomPost(query);
12
13 console.log(`Got ${post.id} via query ${queryIndex}`);
14
15 await handlePost(post);
16}
17
18export async function postSpecificPicture(id: number) {
19 console.log(`Fetching post ${id}...`);
20
21 const post = await e621.getPostById(id);
22
23 console.log(`Got ${post.id}`);
24
25 await handlePost(post);
26}
27
28async function handlePost(post: e621.Post) {
29 const source = post.sources.length ? post.sources[0] : undefined;
30 const cws = config.cw.filter((w) => post.tags.general.includes(w));
31
32 console.log(`Downloading image...`);
33
34 let file = await e621.client.get(post.file.url).buffer();
35
36 if (Buffer.byteLength(file) > 1024 * 1024 * 9) {
37 console.log(`Compressing...`);
38
39 file = await Sharp(file)
40 .resize(1800, 1800, { fit: "inside", withoutEnlargement: true })
41 .jpeg({ quality: 80, mozjpeg: true })
42 .toBuffer();
43 }
44
45 console.log(`Uploading...`);
46
47 const attachment = await mastodon.upload(file, post.id.toString(10));
48
49 console.log(`Posting status...`);
50
51 const status = await mastodon.createStatus(`https://e926.net/posts/${post.id}`, source, cws, attachment.id);
52
53 console.log(`Done! ${status.url}`);
54}