Something went wrong. Try again.
Schedule posts and reposts to Bluesky (and other PDSes) with Cloudflare workers. skyscheduler.work
scheduling social-media cloudflare bsky service cloudflare-workers bsky-tool bluesky
Something went wrong. Try again.
7.7 kB · 165 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165/*********************************************************//**** APPLICATION LIMITS ****//*********************************************************/
// minimum length of a postexport const MIN_LENGTH: number = 1;// max amount of iterations a single repost rule can haveexport const MAX_REPOST_INTERVAL: number = 15;// max amount of time content can be reposted overexport const MAX_REPOST_DAYS: number = 10;// max amount of posts per threadexport const MAX_POSTS_PER_THREAD: number = 10;// the maximum amount of repost posts (content not made from the app) an account can have at any one time.export const MAX_REPOST_POSTS: number = 60;// a limit for the maximum number of repost rules a single post can haveexport const MAX_REPOST_RULES_PER_POST: number = 10;// Max length of a repost titleexport const MAX_REPOST_TITLE_LENGTH: number = 100;// How far in the future can a post be scheduled in years.export const MAX_FUTURE_DATE_VALUE: number = 2.2;
/** Storage settings **//* ------------------ */// How much of the content we keep in the DB after a post has been made.// (Only if TRUNCATE_POSTED_CONTENT is true)export const MAX_POSTED_LENGTH: number = 50;
// how long we should hold onto a posted post that has no additional updates (no reposts, etc)export const MAX_HOLD_DAYS_BEFORE_PURGE: number = 7;
/** Account settings **//* ------------------ */// Dashboard password length settingsexport const MIN_DASHBOARD_PASS: number = 8;export const MAX_DASHBOARD_PASS: number = 30;
// How long between when we detect an account session is no longer valid// and when we force the user to log back in// (this is in seconds)export const ACCOUNT_EXPIRE_FORCE_LOGOUT: number = 5;
/** Media content **//* --------------- */// if gifs should be allowed to upload (this does require video processing on bsky end)export const GIF_UPLOAD_ALLOWED: boolean = false;// max length of an animated gif in minutes// gifs are big so this has a separate configurable settingexport const MAX_GIF_LENGTH: number = 1;
// the maximum size of an image file to generate a thumbnail for, in MB.// generation is done on client side using canvas elements, so keep this low for mobile.export const MAX_THUMBNAIL_SIZE: number = 15;
// the maximum width of an image in pixels (this is used to clamp when compressing)// set to 0 to uncap the limit// NOTE: the higher this value, the more likely that the images will fail to compress properly.export const MAX_IMAGE_WIDTH: number = 3000;
/** AutoSuggest Settings **//* ---------------------- */// max amount of returns from bsky handle searchexport const BSKY_NAME_LOOKUP_LIMIT: number = 8; // 8 is the same value bsky uses// number of characters to activate a bsky handle searchexport const BSKY_NAME_TYPE_AHEAD_CHARS: number = 2;
/*********************************************************//**** INTERNAL/SERVICE LIMITS, DO NOT CHANGE DIRECTLY ****//*********************************************************/
// internal calculation values, pulled from aboveexport const MAX_REPOST_INTERVAL_LIMIT: number = MAX_REPOST_INTERVAL + 1;export const MAX_REPOST_IN_HOURS: number = (MAX_REPOST_DAYS * 24) + 1;
// used in rendering the value so that it looks nice for the user// not used in the actual calculations.export const FUZZY_MAX_FUTURE_DATE: number = MAX_FUTURE_DATE_VALUE > 1 ? Math.floor(MAX_FUTURE_DATE_VALUE) : MAX_FUTURE_DATE_VALUE;
// Helper conversion mathexport const MB_TO_BYTES: number = 1000 * 1000;export const TO_SEC: number = 60;export const TO_MS: number = TO_SEC * 1000;
// Max post length limit via// https://github.com/bluesky-social/social-app/blob/b38013a12ff22a3ebd3075baa0d98bc96302a316/src/lib/constants.ts#L63export const MAX_LENGTH: number = 300;
// Alt text limit via// https://github.com/bluesky-social/social-app/blob/b38013a12ff22a3ebd3075baa0d98bc96302a316/src/lib/constants.ts#L69export const MAX_ALT_TEXT: number = 2000;
// Image limit values via// https://github.com/bluesky-social/social-app/blob/b236f274c3524a0e091a12741b6b3855ae9283ef/src/lib/constants.ts#L102export const BSKY_IMG_SIZE_LIMIT_IN_MB: number = 2;export const BSKY_IMG_MIME_TYPES: string[] = [ "image/png", "image/jpeg", "image/webp", "image/heic", "image/bmp", "image/avif", "image/svg+xml",];
// Used for human readable displayexport const BSKY_IMG_FILE_EXTS_STR: string = ["png", "jpg", "jpeg", "bmp", "webp", "heic", "svg"].join(", ");
// BSky limits that are inferredexport const BSKY_MIN_USERNAME_LENGTH: number = 4;export const BSKY_MAX_USERNAME_LENGTH: number = 256; // since these are domains, they should be about 256export const BSKY_MAX_APP_PASSWORD_LENGTH: number = 20;
// max amount of videos per postexport const MAX_VIDEOS_PER_POST: number = 1;// max number of images per postexport const MAX_IMAGES_PER_POST: number = 10;// max uploadable objects per postexport const MAX_FILES_PER_POST: number = Math.max(MAX_VIDEOS_PER_POST, MAX_IMAGES_PER_POST);// max quote posts/lists/feeds per postexport const MAX_RECORDS_PER_POST: number = 1;// max number of weblinks per postexport const MAX_WEBLINKS_PER_POST: number = 1;// max extra data that can be added to a postexport const MAX_METADATA_PER_POST: number = Math.max(MAX_WEBLINKS_PER_POST, MAX_RECORDS_PER_POST);// max amount of embed data per post object (you can have 1 extra data + the max amount of files)export const MAX_EMBEDS_PER_POST: number = MAX_METADATA_PER_POST + MAX_FILES_PER_POST;
// the max number of associatedRefs that a weblink can have.// This is very egregious, and is only done to lower down any potential abuse.export const MAX_ASSOCIATEDREFS_PER_LINK: number = 100;
// Video limits values via// https://github.com/bluesky-social/social-app/blob/b38013a12ff22a3ebd3075baa0d98bc96302a316/src/lib/constants.ts#L184export const BSKY_VIDEO_MAX_DURATION: number = 3; // in minutes// NOTE: this is still at 100MB due to reliability concerns, also we have no multipart upload support atm// but dropzone technically could support it.export const BSKY_VIDEO_MAX_SIZE_IN_MB: number = 100;
// At some point in time, bsky made it so that video blob refs can only ever reference MP4.// no backwards compatibility, no notice. Awesome. Super.export const BSKY_VIDEO_MIME_TYPES: string[] = ["video/mp4"];
export const BSKY_VIDEO_FILE_EXTS: string[] = ["mp4"];
// Used for human readable displayexport const BSKY_GIF_MIME_TYPES: string[] = ["image/gif"];
// Used for human readable display// eslint-disable-next-line @typescript-eslint/no-unnecessary-conditionexport const BSKY_VIDEO_FILES_STR: string = ((GIF_UPLOAD_ALLOWED) ? [...BSKY_VIDEO_FILE_EXTS, "animated gif"] : BSKY_VIDEO_FILE_EXTS).join(", ");
// Max size of files that can go to R2 without doing multipart uploadsexport const R2_FILE_SIZE_LIMIT_IN_MB: number = 100;export const R2_FILE_SIZE_LIMIT: number = R2_FILE_SIZE_LIMIT_IN_MB * MB_TO_BYTES;export const BSKY_IMG_SIZE_LIMIT: number = BSKY_IMG_SIZE_LIMIT_IN_MB * MB_TO_BYTES;export const BSKY_VIDEO_SIZE_LIMIT_IN_MB: number = Math.min(R2_FILE_SIZE_LIMIT_IN_MB, BSKY_VIDEO_MAX_SIZE_IN_MB);export const BSKY_VIDEO_SIZE_LIMIT: number = BSKY_VIDEO_SIZE_LIMIT_IN_MB * MB_TO_BYTES;export const BSKY_VIDEO_LENGTH_LIMIT: number = BSKY_VIDEO_MAX_DURATION * TO_SEC;export const MAX_GIF_LENGTH_LIMIT: number = MAX_GIF_LENGTH * TO_SEC;// Limits for Cloudflare Imagesexport const CF_IMAGES_FILE_SIZE_LIMIT_IN_MB: number = 70;export const CF_IMAGES_FILE_SIZE_LIMIT: number = CF_IMAGES_FILE_SIZE_LIMIT_IN_MB * MB_TO_BYTES;export const CF_IMAGES_MAX_DIMENSION: number = 10000;// Limits for Cloudflare KVexport const CF_KV_MIN_TTL: number = 60;