Schedule posts to Bluesky with Cloudflare workers.
skyscheduler.work
cf
tool
bsky-tool
cloudflare
bluesky
schedule
bsky
service
social-media
cloudflare-workers
1import remove from "just-remove";
2
3/** APPLICATION CONFIGURATIONS **/
4
5// minimum length of a post
6export const MIN_LENGTH: number = 1;
7// max amount of times something can be reposted
8export const MAX_REPOST_INTERVAL: number = 15;
9// max amount of time something can be reposted over
10export const MAX_REPOST_DAYS: number = 10;
11// max length of an animated gif in minutes
12export const MAX_GIF_LENGTH: number = 1;
13// if gifs should be allowed to upload
14export const GIF_UPLOAD_ALLOWED: boolean = false;
15// if we can preview anything other than images
16export const PREVENT_NON_IMAGE_PREVIEWS: boolean = false;
17// max posts per thread
18export const MAX_POSTS_PER_THREAD: number = 10;
19
20// This is the length of how much we keep in the DB after a post has been made
21export const MAX_POSTED_LENGTH: number = 50;
22
23// Dashboard password length settings
24export const MIN_DASHBOARD_PASS: number = 8;
25export const MAX_DASHBOARD_PASS: number = 30;
26
27// max amount of returns from bsky handle search
28export const BSKY_NAME_LOOKUP_LIMIT: number = 8; // 8 is the same value bsky uses
29// number of characters to activate a bsky handle search
30export const BSKY_NAME_TYPE_AHEAD_CHARS: number = 2;
31
32// the maximum size of an image file to generate a thumbnail for, in MB.
33// generation is done on client side using canvas elements.
34export const MAX_THUMBNAIL_SIZE: number = 15;
35
36// the maximum amount of repost posts an account can have at any one time.
37// because some people went incredibly overboard.
38export const MAX_REPOST_POSTS: number = 40;
39
40// a limit for the maximum number of repost rules a single post can have
41export const MAX_REPOST_RULES_PER_POST: number = 5;
42
43// if the user can edit the repost rules for their posts
44export const CAN_EDIT_REPOST_RULES: boolean = true;
45
46// if users can repost scheduled posts before they are posted
47export const CAN_REPOST_SCHEDULED_POSTS: boolean = true;
48
49/** INTERNAL LIMITS, DO NOT CHANGE **/
50// Maximums used internally, do not change these directly.
51export const MAX_REPOST_INTERVAL_LIMIT: number = MAX_REPOST_INTERVAL + 1;
52export const MAX_REPOST_IN_HOURS: number = (MAX_REPOST_DAYS * 24) + 1;
53
54// Helper conversion math
55export const MB_TO_BYTES: number = 1000 * 1000;
56export const TO_SEC: number = 60;
57export const TO_MS: number = TO_SEC*1000;
58
59// Max post length limit via
60// https://github.com/bluesky-social/social-app/blob/b38013a12ff22a3ebd3075baa0d98bc96302a316/src/lib/constants.ts#L63
61export const MAX_LENGTH: number = 300;
62
63// Alt text limit via
64// https://github.com/bluesky-social/social-app/blob/b38013a12ff22a3ebd3075baa0d98bc96302a316/src/lib/constants.ts#L69
65export const MAX_ALT_TEXT: number = 2000;
66
67// Image limit values via
68// https://github.com/bluesky-social/social-app/blob/b38013a12ff22a3ebd3075baa0d98bc96302a316/src/lib/constants.ts#L97
69export const BSKY_IMG_MAX_WIDTH: number = 2000;
70export const BSKY_IMG_MAX_HEIGHT: number = 2000;
71export const BSKY_IMG_SIZE_LIMIT_IN_MB: number = 1;
72export const BSKY_IMG_MIME_TYPES: string[] = [
73 "image/png",
74 "image/jpeg",
75 "image/webp",
76 "image/heic",
77 "image/bmp",
78 "image/avif",
79 "image/svg+xml"
80];
81
82// Used for human readable display
83export const BSKY_IMG_FILE_EXTS: string = [
84 "png",
85 "jpg",
86 "jpeg",
87 "bmp",
88 "webp",
89 "heic",
90 "svg"
91].join(", ");
92
93// BSky limits that are inferred
94export const BSKY_MIN_USERNAME_LENGTH: number = 4;
95export const BSKY_MAX_USERNAME_LENGTH: number = 256; // since these are domains, they should be about 256
96export const BSKY_MAX_APP_PASSWORD_LENGTH: number = 20;
97export const MAX_EMBEDS_PER_POST: number = 4;
98
99// Video limits values via
100// https://github.com/bluesky-social/social-app/blob/b38013a12ff22a3ebd3075baa0d98bc96302a316/src/lib/constants.ts#L184
101export const BSKY_VIDEO_MAX_DURATION: number = 3; // in minutes
102export const BSKY_VIDEO_MAX_SIZE_IN_MB: number = 100;
103export const BSKY_VIDEO_MIME_TYPES: string[] = [
104 "video/mp4",
105 "video/mpeg",
106 "video/webm",
107 "video/quicktime"
108];
109
110// Used for human readable display
111export const BSKY_GIF_MIME_TYPES: string[] = [
112 "image/gif"
113];
114
115// Used for human readable display
116export const BSKY_VIDEO_FILE_EXTS: string = remove([
117 "mp4",
118 "mpeg",
119 "mov",
120 "webm",
121 /* This is handled in a special case because bluesky */
122 (GIF_UPLOAD_ALLOWED ? "animated gif" : undefined)
123], [undefined]).join(", ");
124
125// Max size of files that can go to R2 without doing multipart uploads
126export const R2_FILE_SIZE_LIMIT_IN_MB: number = 100;
127export const R2_FILE_SIZE_LIMIT: number = R2_FILE_SIZE_LIMIT_IN_MB * MB_TO_BYTES;
128export const BSKY_IMG_SIZE_LIMIT: number = BSKY_IMG_SIZE_LIMIT_IN_MB * MB_TO_BYTES;
129export const BSKY_VIDEO_SIZE_LIMIT: number = BSKY_VIDEO_MAX_SIZE_IN_MB * MB_TO_BYTES;
130export const BSKY_VIDEO_LENGTH_LIMIT: number = BSKY_VIDEO_MAX_DURATION * TO_SEC;
131export const MAX_GIF_LENGTH_LIMIT: number = MAX_GIF_LENGTH * TO_SEC;
132// Max size of Cloudflare Images files
133export const CF_IMAGES_FILE_SIZE_LIMIT_IN_MB: number = 70;
134export const CF_IMAGES_FILE_SIZE_LIMIT: number = CF_IMAGES_FILE_SIZE_LIMIT_IN_MB * MB_TO_BYTES;
135export const CF_IMAGES_MAX_DIMENSION: number = 10000;