Schedule posts to Bluesky with Cloudflare workers. skyscheduler.work
cf tool bsky-tool cloudflare bluesky schedule bsky service social-media cloudflare-workers
at main 110 lines 2.8 kB view raw
1import has from "just-has"; 2import isEmpty from "just-is-empty"; 3import { EmbedData, PostLabel } from "../types"; 4import { RepostInfo } from "./repost"; 5import { MAX_REPOST_RULES_PER_POST } from "../limits"; 6 7// Basically a copy of the schema 8export class Post { 9 // guid for post 10 postid: string; 11 // SkyScheduler User Id 12 user: string; 13 // post data 14 text: string; 15 embeds?: EmbedData[]; 16 label: PostLabel; 17 // post flags 18 postNow: boolean; 19 posted?: boolean; 20 isRepost?: boolean; 21 // repost metadata 22 repostInfo?: RepostInfo[]; 23 scheduledDate?: string; 24 repostCount?: number; 25 // atproto data 26 cid?: string; 27 uri?: string; 28 // thread data 29 threadOrder: number; 30 rootPost?: string; 31 parentPost?: string; 32 33 constructor(data: any) { 34 if (has(data, "userId")) 35 this.user = data.userId; 36 else 37 this.user = data.user; 38 39 if (has(data, "uuid")) 40 this.postid = data.uuid; 41 else 42 this.postid = data.postid; 43 44 if (has(data, "embedContent")) 45 this.embeds = data.embedContent; 46 else if (has(data, "embeds")) 47 this.embeds = data.embeds; 48 49 if (has(data, "contentLabel")) 50 this.label = data.contentLabel; 51 else 52 this.label = data.label; 53 54 if (has(data, "content")) 55 this.text = data.content; 56 else 57 this.text = data.text; 58 59 this.postNow = data.postNow; 60 this.threadOrder = data.threadOrder; 61 62 if (has(data, "repostCount")) 63 this.repostCount = data.repostCount; 64 65 if (data.scheduledDate) 66 this.scheduledDate = data.scheduledDate; 67 68 if (data.repostInfo) 69 this.repostInfo = data.repostInfo; 70 71 if (data.rootPost) 72 this.rootPost = data.rootPost; 73 74 if (data.parentPost) 75 this.parentPost = data.parentPost; 76 77 // ATProto data 78 if (data.uri) 79 this.uri = data.uri; 80 if (data.cid) 81 this.cid = data.cid; 82 83 if (has(data, "isRepost")) 84 this.isRepost = data.isRepost; 85 86 if (has(data, "posted")) 87 this.posted = data.posted; 88 89 // if a cid flag appears for the object and it's a thread root, then the post (if marked not posted) is posted. 90 if (this.posted == false && !isEmpty(data.cid) && this.isThreadRoot) 91 this.posted = true; 92 } 93 getURI(): string|null { 94 return this.uri ? "https://bsky.app/profile/" + this.uri.replace("at://","").replace("app.bsky.feed.","") : null; 95 } 96 getUser(): string { 97 return this.user; 98 } 99 canAddMoreRepostRules(): boolean { 100 if (!this.posted) 101 return false; 102 103 return !this.isChildPost && (this.repostInfo === undefined || this.repostInfo.length < MAX_REPOST_RULES_PER_POST); 104 } 105 hasEmbeds(): boolean { 106 return this.embeds !== undefined && this.embeds.length > 0; 107 } 108 get isThreadRoot() { return this.threadOrder == 0; } 109 get isChildPost() { return this.parentPost !== undefined; } 110};