bluesky quote bot

Deleted astralUtils, updated other files

+68 -176
+2 -2
.gitignore
··· 1 1 .vscode 2 2 .env 3 3 /public/*.js 4 - lastIndex.txt 5 - *.png 4 + *.png 5 + *.svg
+62 -65
deno.lock
··· 1 1 { 2 2 "version": "4", 3 3 "specifiers": { 4 - "jsr:@astral/astral@*": "0.4.8", 5 - "jsr:@deno-library/progress@1.4.9": "1.4.9", 6 - "jsr:@std/assert@0.223": "0.223.0", 7 - "jsr:@std/async@0.223.0": "0.223.0", 4 + "jsr:@denosaurs/plug@1.0.5": "1.0.5", 5 + "jsr:@gfx/canvas@0.5.7": "0.5.7", 6 + "jsr:@std/assert@0.214": "0.214.0", 7 + "jsr:@std/assert@0.217": "0.217.0", 8 8 "jsr:@std/dotenv@*": "0.225.2", 9 - "jsr:@std/fmt@0.221.0": "0.221.0", 10 - "jsr:@std/fs@0.223.0": "0.223.0", 11 - "jsr:@std/path@0.223.0": "0.223.0", 12 - "jsr:@zip-js/zip-js@2.7.41": "2.7.41", 9 + "jsr:@std/encoding@0.214": "0.214.0", 10 + "jsr:@std/encoding@0.217.0": "0.217.0", 11 + "jsr:@std/fmt@0.214": "0.214.0", 12 + "jsr:@std/fs@0.214": "0.214.0", 13 + "jsr:@std/fs@0.217.0": "0.217.0", 14 + "jsr:@std/path@0.214": "0.214.0", 15 + "jsr:@std/path@0.217": "0.217.0", 16 + "jsr:@std/path@0.217.0": "0.217.0", 13 17 "npm:@atcute/bluesky-richtext-builder@*": "1.0.1_@atcute+bluesky@1.0.7__@atcute+client@2.0.3_@atcute+client@2.0.3", 14 - "npm:@atcute/client@^2.0.3": "2.0.3" 18 + "npm:@atcute/client@^2.0.3": "2.0.3", 19 + "npm:tex-linebreak@*": "0.7.1" 15 20 }, 16 21 "jsr": { 17 - "@astral/astral@0.4.8": { 18 - "integrity": "7eb1f4e6d56ec9d5f4565cb2b7f166dc43ab0cbeffd49e5a9248094d2666fdd4", 22 + "@denosaurs/plug@1.0.5": { 23 + "integrity": "04cd988da558adc226202d88c3a434d5fcc08146eaf4baf0cea0c2284b16d2bf", 19 24 "dependencies": [ 20 - "jsr:@deno-library/progress", 21 - "jsr:@std/async", 22 - "jsr:@std/fs", 23 - "jsr:@std/path", 24 - "jsr:@zip-js/zip-js" 25 + "jsr:@std/encoding@0.214", 26 + "jsr:@std/fmt", 27 + "jsr:@std/fs@0.214", 28 + "jsr:@std/path@0.214" 25 29 ] 26 30 }, 27 - "@deno-library/progress@1.4.9": { 28 - "integrity": "86db695ae338cb1ae3ffa46d6f2e6a9cdef33a15c2ace6fa25354a91a9dd3909", 31 + "@gfx/canvas@0.5.7": { 32 + "integrity": "cd054e34f64763c7e6958bedc5fef00def929c20ebb89da5687112b5d402dbe3", 29 33 "dependencies": [ 30 - "jsr:@std/fmt" 34 + "jsr:@denosaurs/plug", 35 + "jsr:@std/encoding@0.217.0", 36 + "jsr:@std/fs@0.217.0", 37 + "jsr:@std/path@0.217.0" 31 38 ] 32 39 }, 33 - "@std/assert@0.223.0": { 34 - "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" 40 + "@std/assert@0.214.0": { 41 + "integrity": "55d398de76a9828fd3b1aa653f4dba3eee4c6985d90c514865d2be9bd082b140" 35 42 }, 36 - "@std/async@0.223.0": { 37 - "integrity": "b42f635d89fb8f9e0e7dc258fbc8f33233e4977f61e0b20da08a8b51fd7b2c5d", 38 - "dependencies": [ 39 - "jsr:@std/assert" 40 - ] 43 + "@std/assert@0.217.0": { 44 + "integrity": "c98e279362ca6982d5285c3b89517b757c1e3477ee9f14eb2fdf80a45aaa9642" 41 45 }, 42 46 "@std/dotenv@0.225.2": { 43 47 "integrity": "e2025dce4de6c7bca21dece8baddd4262b09d5187217e231b033e088e0c4dd23" 44 48 }, 45 - "@std/fmt@0.221.0": { 46 - "integrity": "379fed69bdd9731110f26b9085aeb740606b20428ce6af31ef6bd45ef8efa62a" 49 + "@std/encoding@0.214.0": { 50 + "integrity": "30a8713e1db22986c7e780555ffd2fefd1d4f9374d734bb41f5970f6c3352af5" 51 + }, 52 + "@std/encoding@0.217.0": { 53 + "integrity": "b03e8ff94c98d6b6a02c02c5cf8e5d203400155516248964fc4559abc04669dc" 54 + }, 55 + "@std/fmt@0.214.0": { 56 + "integrity": "40382cff88a0783b347b4d69b94cf931ab8e549a733916718cb866c08efac4d4" 47 57 }, 48 - "@std/fs@0.223.0": { 49 - "integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c" 58 + "@std/fs@0.214.0": { 59 + "integrity": "bc880fea0be120cb1550b1ed7faf92fe071003d83f2456a1e129b39193d85bea", 60 + "dependencies": [ 61 + "jsr:@std/assert@0.214", 62 + "jsr:@std/path@0.214" 63 + ] 50 64 }, 51 - "@std/path@0.223.0": { 52 - "integrity": "593963402d7e6597f5a6e620931661053572c982fc014000459edc1f93cc3989", 65 + "@std/fs@0.217.0": { 66 + "integrity": "0bfff5f3618d68c385b28b4ffbf3a15c98293a0f1186444458b62e0111ce77b2", 53 67 "dependencies": [ 54 - "jsr:@std/assert" 68 + "jsr:@std/assert@0.217", 69 + "jsr:@std/path@0.217" 55 70 ] 56 71 }, 57 - "@zip-js/zip-js@2.7.41": { 58 - "integrity": "a883116c1b46673de643f9cd3b6619d746271b96cd9a8e2b9d0bb8353d764d3d" 72 + "@std/path@0.214.0": { 73 + "integrity": "d5577c0b8d66f7e8e3586d864ebdf178bb326145a3611da5a51c961740300285", 74 + "dependencies": [ 75 + "jsr:@std/assert@0.214" 76 + ] 77 + }, 78 + "@std/path@0.217.0": { 79 + "integrity": "1217cc25534bca9a2f672d7fe7c6f356e4027df400c0e85c0ef3e4343bc67d11", 80 + "dependencies": [ 81 + "jsr:@std/assert@0.217" 82 + ] 59 83 } 60 84 }, 61 85 "npm": { ··· 74 98 }, 75 99 "@atcute/client@2.0.3": { 76 100 "integrity": "sha512-j9GryA5l+4F0BTQWa6/1XmsuSPSq+bqNCY3mrHUGD592hMqUZxgpYDLgRWL+719V287AW/56AwvFYlbjlENp7A==" 101 + }, 102 + "tex-linebreak@0.7.1": { 103 + "integrity": "sha512-DioAcgbUGvacQSNJBcHoIUl2MnjWpNGz3R9tDX6gjtn/c+59ySW1dyFGMhIpl+u7sT+ywRwYAoAtk+wA5xUIsg==" 77 104 } 78 - }, 79 - "remote": { 80 - "https://deno.land/x/imagescript@1.3.0/ImageScript.js": "cf90773c966031edd781ed176c598f7ed495e7694cd9b86c986d2d97f783cca0", 81 - "https://deno.land/x/imagescript@1.3.0/mod.ts": "18a6cb83c55e690c873505f6fe867364c678afb64934fe7aef593a6b92f79995", 82 - "https://deno.land/x/imagescript@1.3.0/png/src/crc.mjs": "5cf50de181d61dd00e66a240d811018ba5070afa8bba302f393604404604de84", 83 - "https://deno.land/x/imagescript@1.3.0/png/src/mem.mjs": "4968d400dae069b4bf0ef4767c1802fd2cc7d15d90eda4cfadf5b4cd19b96c6d", 84 - "https://deno.land/x/imagescript@1.3.0/png/src/png.mjs": "96ef0ceff1b5a6cd9304749e5f187b4ab238509fb5f9a8be8ee934240271ed8d", 85 - "https://deno.land/x/imagescript@1.3.0/png/src/zlib.mjs": "9867dc3fab1d31b664f9344b0d7e977f493d9c912a76c760d012ed2b89f7061c", 86 - "https://deno.land/x/imagescript@1.3.0/utils/buffer.js": "952cb1beb8827e50a493a5d1f29a4845e8c648789406d389dd51f51205ba02d8", 87 - "https://deno.land/x/imagescript@1.3.0/utils/crc32.js": "573d6222b3605890714ebc374e687ec2aa3e9a949223ea199483e47ca4864f7d", 88 - "https://deno.land/x/imagescript@1.3.0/utils/png.js": "fbed9117e0a70602645d70df9c103ff6e79c03e987bd5c1685dcb4200729b6de", 89 - "https://deno.land/x/imagescript@1.3.0/utils/wasm/font.js": "9e75d842608c057045698d6a7cdf5ffd27241b5cdea0391c89a1917b31294524", 90 - "https://deno.land/x/imagescript@1.3.0/utils/wasm/gif.js": "8b86f7b96486bb8ff50fbc7c7487f86cb5cef85e6acd71e1def78a1aa2f12e4f", 91 - "https://deno.land/x/imagescript@1.3.0/utils/wasm/jpeg.js": "75295e2fcf96b4f7bb894b3844fdaa8140d63169d28b466b5d5be89d59a7b6e6", 92 - "https://deno.land/x/imagescript@1.3.0/utils/wasm/png.js": "0659536a8dd8f892c8346e268b2754b4414fad0ec1e9794dfcde1ba1c804ee02", 93 - "https://deno.land/x/imagescript@1.3.0/utils/wasm/svg.js": "f5c8a9d1977b51a7c07549ceb6bbbaca9497321a193f28b3dc229a42d91bcf14", 94 - "https://deno.land/x/imagescript@1.3.0/utils/wasm/tiff.js": "c2d7bdaef094df25aae1752e75167f485e89275d76a1379e39d8949580b7af4f", 95 - "https://deno.land/x/imagescript@1.3.0/utils/wasm/zlib.js": "749875f83abffe24d3b977475a0cbd5f9b52bee1fbdbef61ec183cbfc17805f6", 96 - "https://deno.land/x/imagescript@1.3.0/v2/framebuffer.mjs": "add44ff184636659714b3c6d4b896f628545451abffbc30b5bcc2e8d9a73d012", 97 - "https://deno.land/x/imagescript@1.3.0/v2/ops/blur.mjs": "80716f1ffab8a2aeb54a036f583bf51a2b9dd37e005adc000add803df8e8a12f", 98 - "https://deno.land/x/imagescript@1.3.0/v2/ops/color.mjs": "5e72cdcbf97dc939a2795223f01e3cb0544c0c56b03ea2aa026050df58348814", 99 - "https://deno.land/x/imagescript@1.3.0/v2/ops/crop.mjs": "69431fa6f687fd9f0c31eff0ec27d7ac925275005e53a37f0c3fab4cc4d9a9ea", 100 - "https://deno.land/x/imagescript@1.3.0/v2/ops/fill.mjs": "cf1b9488314753fbc9ebf03410ac74c2a34ea5a69fb6892cd6e8366cd1930d93", 101 - "https://deno.land/x/imagescript@1.3.0/v2/ops/flip.mjs": "825a34a66567dcf15e76a719f1bf2f66fb106503cd69942292b1b0ae05c5718e", 102 - "https://deno.land/x/imagescript@1.3.0/v2/ops/index.mjs": "423ba687119be2bba8cec72890577d3afa3621b6b8108912242fe937a183f2aa", 103 - "https://deno.land/x/imagescript@1.3.0/v2/ops/iterator.mjs": "c2adf3d90ce00719a02c48c97634574176a3501ff026676259bd71aa8f5d69b9", 104 - "https://deno.land/x/imagescript@1.3.0/v2/ops/overlay.mjs": "7e6e2c2ffd25006d52597ab8babc5f8f503d388a3fdf2fbc0eaea02799a020c9", 105 - "https://deno.land/x/imagescript@1.3.0/v2/ops/resize.mjs": "814e78ebce8eaf8f1f918688db7b52a141405e06a36ed4b25d04413d69e7d17b", 106 - "https://deno.land/x/imagescript@1.3.0/v2/ops/rotate.mjs": "a1b65616717bd2eed8db406affea3263b4674dada46b56441ef38167a187455d", 107 - "https://deno.land/x/imagescript@1.3.0/v2/util/mem.mjs": "4968d400dae069b4bf0ef4767c1802fd2cc7d15d90eda4cfadf5b4cd19b96c6d" 108 105 } 109 106 }
+2 -2
main.ts
··· 1 - import { generateScreenshot } from "./utils/astralUtils.ts"; 1 + import { generatePng } from "./utils/canvasUtils.ts"; 2 2 import { createBskyPost } from "./utils/blueskyUtils.ts"; 3 3 import { load } from "jsr:@std/dotenv"; 4 4 ··· 64 64 let index = result.value ?? 0; 65 65 66 66 // Generate screenshot and upload it to Bluesky 67 - const { image, aspectRatio } = await generateScreenshot( 67 + const { image, aspectRatio } = generatePng( 68 68 data[index].quote, // "image.png" 69 69 ); 70 70 await createBskyPost(data[0], image, aspectRatio);
-106
utils/astralUtils.ts
··· 1 - import { launch } from "jsr:@astral/astral"; 2 - import { decode, Image } from "https://deno.land/x/imagescript@1.3.0/mod.ts"; 3 - import { generateStyledHTML } from "./textUtils.ts"; 4 - 5 - interface AspectRatio { 6 - width: number; 7 - height: number; 8 - } 9 - interface ResponseData { 10 - image: Uint8Array; 11 - aspectRatio: AspectRatio; 12 - } 13 - 14 - async function addPaddingToImage( 15 - imageData: Uint8Array, 16 - padding: number, 17 - ): Promise<{ paddedImage: Uint8Array; newAspectRatio: AspectRatio }> { 18 - // Decode the original image 19 - const decodedImage = await decode(imageData); 20 - if (!(decodedImage instanceof Image)) { 21 - throw new Error("Decoded image is not a static image"); 22 - } 23 - const originalImage = decodedImage; 24 - 25 - // Create a new image with padding 26 - const paddedWidth = originalImage.width + (padding * 2); 27 - const paddedHeight = originalImage.height + (padding * 2); 28 - const newImage = new Image(paddedWidth, paddedHeight); 29 - 30 - // Fill with ivory color (255, 255, 240) 31 - newImage.fill(0xFFFFF0FF); 32 - 33 - // Composite the original image onto the new one with padding offset 34 - newImage.composite(originalImage, padding, padding); 35 - 36 - // Encode the result back to PNG 37 - const paddedImage = await newImage.encode(); 38 - 39 - return { 40 - paddedImage, 41 - newAspectRatio: { 42 - width: paddedWidth, 43 - height: paddedHeight, 44 - }, 45 - }; 46 - } 47 - 48 - export async function generateScreenshot( 49 - string: string, 50 - outputPath: string | undefined = undefined, 51 - ): Promise<ResponseData> { 52 - const browser = await launch({ 53 - args: ["--no-sandbox"], 54 - }); 55 - 56 - const page = await browser.newPage(); 57 - 58 - const styledHTML = generateStyledHTML(string) 59 - .replace(/padding:\s*40px;/g, "padding: 0;") 60 - .replace( 61 - /font-size:\s*clamp\(14px,\s*12px\s*\+\s*0.75vw,\s*20px\);/, 62 - "font-size: 20px;", 63 - ); 64 - 65 - await page.setContent(` 66 - <!DOCTYPE html> 67 - <html lang="en"> 68 - <head> 69 - <meta charset="UTF-8"> 70 - <meta name="viewport" content="width=device-width, initial-scale=1.0"> 71 - <title>HTML Screenshot</title> 72 - <link href="https://fonts.googleapis.com/css2?family=Merriweather&display=swap" rel="stylesheet" /> 73 - <style> 74 - body { 75 - margin: 0; 76 - padding: 0; 77 - } 78 - </style> 79 - </head> 80 - <body> 81 - ${styledHTML} 82 - </body> 83 - </html> 84 - `); 85 - 86 - await page.waitForSelector(".styled-container"); 87 - 88 - // Select the element 89 - const element = await page.$(".styled-container"); 90 - if (!element) { 91 - throw new Error("Element not found!"); 92 - } 93 - 94 - const screenshot = await element.screenshot(); 95 - 96 - await browser.close(); 97 - 98 - // Add padding in post-processing using ImageScript 99 - const { paddedImage, newAspectRatio } = await addPaddingToImage( 100 - screenshot, 101 - 40, 102 - ); 103 - if (outputPath) Deno.writeFileSync(outputPath, paddedImage); 104 - 105 - return { image: paddedImage, aspectRatio: newAspectRatio }; 106 - }
+2 -1
utils/canvasUtils.ts
··· 211 211 }; 212 212 213 213 const pngCanvas: Canvas = generateCanvas(createLocalCanvas, quote); 214 + const aspectRatio = { width: pngCanvas.width, height: pngCanvas.height }; 214 215 if (path) { 215 216 pngCanvas.save(path); 216 217 } 217 - return pngCanvas.encode("png"); 218 + return { image: pngCanvas.encode("png"), aspectRatio}; 218 219 }