Blueskyãã¯ããã¦ãããããã§ãããããªã®ã§ã
Node.jsã§æ稿ãã¦ã¿ãã¨ãã®åå¿é²(*´Ïï½*)
楽ã ãã©ãã¡ãã£ã¨çãããæãããããã
å©ç¨ããããã±ã¼ã¸(@atproto/api
)
- bluesky-social/atproto: Social networking technology created by Bluesky
- atproto/packages/api · GitHub
â ï¸ This is not production-ready software. This project is in active development â ï¸
å®ç¨¼ååãã®ã½ããã¦ã§ã¢ã§ã¯ãªããç¾å¨éçºä¸ã§ãã
ã¨ãã注ææ¸ãããããã©ãå ¬å¼ã®APIã¯ã©ã¤ã¢ã³ããç¨æããã¦ããæãã
ãã°ã¤ã³
ãã°ã¤ã³ã¯ãããªæããBskyAgent
ã§agentãä½ã£ã¦login
ããã
import { BskyAgent, RichText } from "@atproto/api"; const service = "https://bsky.social"; const identifier = "ãã°ã¤ã³ããã¡ã¼ã«ã¢ãã¬ã¹"; const password = "App Password"; // Agentã®åæå const agent = new BskyAgent({ service }); // ãã°ã¤ã³ await agent.login({ identifier, password });
APIãã¼ã¯ã³ã®ä»çµã¿ã¯ãªããã©ãApp Passwordã¨ããçºè¡ã§ãããã¹ã¯ã¼ããããã®ã§ã
ããã使ãã¨ããæãã
- App Passwordsã®ãã¼ã¸: https://bsky.app/settings/app-passwords
- Settingsãã¼ã¸ã®Advanced->App passwordsãã
ããã¹ãã®æ稿
ã·ã³ãã«ãªããã¹ãã¯ãããªæã
await agent.post({ text: "ãªãã»ã©", });
URLãã¡ã³ã·ã§ã³ããªã³ã¯ãã
ããã ã¨ãURLãã¡ã³ã·ã§ã³ãªã©ã®ãªã³ã¯ã¯ä»ä¸ãããªãæããªã®ã§ã
RichText
ã使ã£ã¦ãæ¤åºã»å¤æãããå¿
è¦ãããã
const rt = new RichText({ text: "ãªãã»ã©...\nhttps://github.com/bluesky-social/atproto" }); // ãªã³ã¯ãã¡ã³ã·ã§ã³ã®èªåæ¤åºã»å¤æ await rt.detectFacets(agent); await agent.post({ text: rt.text, fasets: rt.facets, });
ãªã³ã¯ã«ã¼ãã表示ãã
ããã ã¨ãã ãªã³ã¯ãããã ãã®ããã¹ãã«ãªãã
OGPç»åãªã©ã®ãªã³ã¯ã«ã¼ããèªåã§ãã¼ã¿ã渡ããªãã¨ãããªãã
å
容ã¯ãããªæãã
const embed = { $type: "app.bsky.embed.external", external: { uri: "ã¯ãªãã¯æã®URL", thumb: { $type: "blob", ref: { $link: "Blueskyä¸ã«ããç»åã®ID" }, mimeType: "image/jpeg", // mimeType size: 40627 // ç»åã®ãµã¤ãº }, title: "ãªã³ã¯ã«ã¼ãã®ã¿ã¤ãã«, description: "ãªã³ã¯ã«ã¼ãã®description" } } await agent.post({ text: rt.text, fasets: rt.facets, embed: embed, });
ãã®embed
ã«è¨å®ããæ
å ±ãåå¾ããããã«ã
- ããã¹ãããURLã®åå¾
- URLããOGPæ å ±ãåå¾
- ç»åãã¢ãããã¼ããã¦IDãåå¾
- æ稿
ããããªãå¿ è¦ãããã
ããã¹ãããURLã®åå¾
detectFacets()
ãå®è¡ãããã¨ã ã¨ã
æ¤åºãããçµæãå
¥ã£ã¦ããã®ã§ãããæ´»ç¨ããã
const rt = new RichText({ text: "ãªãã»ã©...\nhttps://github.com/bluesky-social/atproto" }); await rt.detectFacets(agent); console.log(JSON.stringfy(rt, null, 2)); // { // "text": "ã¤ã¾ãã¯ãã\nhttps://github.com/\nãããããã¨ãï¼", // "facets": [ // { // "index": { // "byteStart": 19, // "byteEnd": 38 // }, // "features": [ // { // "$type": "app.bsky.richtext.facet#link", // "uri": "https://github.com/" // } // ] // } // ] // }
å°ãé·ãã¦ãç
©éã ãã©ãæåã«è¦ã¤ãã£ã
facets[].features[].uri
ãè¿ãã
// RichTextããURLãåå¾ãã async function findUrlInText(rt: RichText): Promise<string | null> { if (rt.facets.length < 1) return null; for (const facet of rt.facets) { if (facet.features.length < 1) continue; for (const feature of facet.features) { if (feature.$type != "app.bsky.richtext.facet#link") continue; else if (feature.uri == null) continue; return feature.uri as string; } } return null; }
URLããOGPæ å ±ãåå¾
ãããã使ã£ã¦ãã¿ã¤ããURLããæ
å ±ãåå¾ããã
ãã®ã¾ã¾ã ã¨ã¢ãããã¼ãã§å¤±æããå ´åãããã®ã§ã
ç»åãµã¤ãºãå°ãããã¦ããå¿
è¦ãããã£ã½ãã
- open-graph-scraper ... OGæ å ±ã®åå¾
- sharp or imagescript ... ç»åãµã¤ãºã®å¤æ
æ £ãã¦ããsharpã使ã£ã¦ä¾ã
type OgInfo = { siteUrl: string; ogImageUrl: string; type: string; description: string; title: string; imageData: Uint8Array; }; async function getOgInfo(url: string): Promise<OgInfo> { // open-graph-scraperã§URLããOGæ å ±ãåå¾ const { result } = await ogs({ url: url }); // fetchã§ç»åãã¼ã¿ãåå¾ const res = await fetch(result.ogImage?.at(0)?.url || ""); const buffer = await res.arrayBuffer(); // sharpã§800pxäºãªãµã¤ãº const compressedImage = await sharp(buffer) .resize(800, null, { fit: "inside", withoutEnlargement: true }) .jpeg({ quality: 80, progressive: true }) .toBuffer(); // OgInfoãè¿ã return { siteUrl: url, ogImageUrl: result.ogImage?.at(0)?.url || "", type: result.ogImage?.at(0)?.type || "", description: result.ogDescription || "", title: result.ogTitle || "", imageData: new Uint8Array(compressedImage), }; }
ç»åãã¢ãããã¼ããã¦IDãåå¾
OGæ
å ±ã¨OGç»åãã¼ã¿ãåå¾ã§ããã®ã§ã
Blueskyå´ã«ã¢ãããã¼ãããã
import { BskyAgent } from "@atproto/api"; type UnPromise<T> = T extends Promise<infer R> ? R : never; type UploadImageResponse = UnPromise<ReturnType<BskyAgent["uploadBlob"]>>; async function uploadImage(ogInfo: OgInfo): Promise<UploadImageResponse> { return await this.agent.uploadBlob(ogInfo.imageData, { encoding: "image/jpeg", }); }
åå®ç¾©ããã¾ãè¦ã¤ããããªãã£ãã®ã§ãããºã«ã解ãã¦ããæãã«ãã
æ稿ãã
const rt = new RitchText({ text: [ "ã¤ã¾ãã¯ãã", "https://github.com/", "@kirapuka.bsky.social", "ãããããã¨ãï¼", ].join("\n"), }); await rt.detectFacets(agent); // ããã¹ãããURLã®åå¾ const url = await findUrlInText(rt); // URLããOGPæ å ±ãåå¾ const ogInfo = await getOgInfo(url); // ç»åãã¢ãããã¼ããã¦IDãåå¾ const uploadedRes = await uploadImage(ogInfo); const embed = { $type: "app.bsky.embed.external", external: { uri: ogInfo.siteUrl, thumb: { $type: "blob", ref: { $link: uploadedRes.data.blob.ref.toString(), }, mimeType: uploadedRes.data.blob.mimeType, size: uploadedRes.data.blob.size, }, title: ogInfo.title, description: ogInfo.description, }, } } // æ稿 await agent.post({ text: rt.text, fasets: rt.facets, embed: embed, });
ã§ãã(*´Ïï½*)!!
以ä¸!! ããã§ããããã§ããããã(*´Ïï½*)
ãã¾ã: ãã®ä»ã®API
@atproto/apiã®READMEãè¦ãã¨ã
ããããã§ããã£ã½ãã
// Feeds and content await agent.getTimeline(params, opts) await agent.getAuthorFeed(params, opts) await agent.getPostThread(params, opts) await agent.getPost(params) await agent.getPosts(params, opts) await agent.getLikes(params, opts) await agent.getRepostedBy(params, opts) await agent.post(record) await agent.deletePost(postUri) await agent.like(uri, cid) await agent.deleteLike(likeUri) await agent.repost(uri, cid) await agent.deleteRepost(repostUri) await agent.uploadBlob(data, opts) // Social graph await agent.getFollows(params, opts) await agent.getFollowers(params, opts) await agent.follow(did) await agent.deleteFollow(followUri) // Actors await agent.getProfile(params, opts) await agent.upsertProfile(updateFn) await agent.getProfiles(params, opts) await agent.getSuggestions(params, opts) await agent.searchActors(params, opts) await agent.searchActorsTypeahead(params, opts) await agent.mute(did) await agent.unmute(did) // Notifications await agent.listNotifications(params, opts) await agent.countUnreadNotifications(params, opts) await agent.updateSeenNotifications() // Identity await agent.resolveHandle(params, opts) await agent.updateHandle(params, opts) // Session management await agent.createAccount(params) await agent.login(params) await agent.resumeSession(session)
åèã«ãããµã¤ããã¾
- åèè¨äº
- å ¬å¼ããã¥ã¡ã³ã