UNPKG

my-bluesky-data

Version:

Connects to your Bluesky acocunt and creates HTML of your latest post for use on your webpage, with hourly cron job to get updates

232 lines (222 loc) 10.4 kB
import dotenv from 'dotenv'; dotenv.config() import {AtpAgent} from '@atproto/api'; import { ParseTimeStamp } from './timestamp.mjs'; let feed let latestPost let hashtags let latestPostWithHashtagLinks let latestPostWithStyledHashtags let weburl let bskyElementLinkedHashtags = /*html*/`` let bskyElementStyledHashtags = /*html*/`` async function GetFeed() { feed = await agent.getAuthorFeed({actor: process.env.BSKY_ID}) } function SetLatestPost() { latestPost = feed.data.feed.find(obj => !obj.hasOwnProperty('reply')).post } function SetLatestPostWithHashtagLinks() { hashtags = latestPost.record.text.split(' ').filter(v=> v.startsWith('#')) latestPostWithHashtagLinks = latestPost.record.text if(hashtags.length > 0) { hashtags.forEach(hashtag => { let newlink = `<a class="bsky_hashtag_link" target="_blank" href="https://bsky.app/hashtag/${hashtag}">${hashtag}</a>` latestPostWithHashtagLinks = latestPostWithHashtagLinks.replace(hashtag, newlink) }) } } function SetLatestPostWithStyledHashtags() { hashtags = latestPost.record.text.split(' ').filter(v=> v.startsWith('#')) latestPostWithStyledHashtags = latestPost.record.text if(hashtags.length > 0) { hashtags.forEach(hashtag => { let newStyledHashtag = `<span class="bsky_hashtag">${hashtag}</span>` latestPostWithStyledHashtags = latestPostWithStyledHashtags.replace(hashtag, newStyledHashtag) }) } } function ConstructBskyElement(bsky_post_text) { let bskyImage = /*html*/`` if(latestPost.hasOwnProperty("embed")) { //gifs if(latestPost.embed.$type == 'app.bsky.embed.external#view') { bskyImage = /*html*/`<img id="bsky_post_image" src="${latestPost.embed.external.uri}" alt="${latestPost.embed.external.title}">` } //videos else if(latestPost.embed.$type == 'app.bsky.embed.video#view') { bskyImage = /*html*/`<img id="bsky_post_image" src="${latestPost.embed.thumbnail}" alt="Image from bsky.app">` } //images else { bskyImage = /*html*/`<img id="bsky_post_image" src="${latestPost.embed.images[0].fullsize}" alt="${latestPost.embed.images[0].alt}">` } } let uriparts = latestPost.uri.split("/") let lastUriChunk = uriparts[uriparts.length - 1] weburl = 'https://bsky.app/profile/' + process.env.BSKY_ID + `/post/` + lastUriChunk let bskyElement = /*html*/` <div id="bsky"> <div id="bsky_header"> <img id="bsky_avatar" src="${latestPost.author.avatar}" alt="Blusky Avatar"> <div id="bsky_name_handle"> <p id="bsky_display_name">${latestPost.author.displayName}</p> <small id="bsky_handle">@${latestPost.author.handle}</small> </div> <svg id="bsky_icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 -3.268 64 68.414"><path fill="#0085ff" d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805zm36.254 0C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745z"></path></svg> </div> <div id="bsky_post_content"> <p id="bsky_post_text">${bsky_post_text}</p> <!-- image --> ${bskyImage} </div> <small id="bsky_post_date">${ParseTimeStamp(latestPost.indexedAt)}</small> <div id="bsky_metrics"> <div class="bsky_post_metric"> <svg fill="none" width="22" viewBox="0 0 24 24" height="22" style="color: rgb(120, 142, 165); pointer-events: none;"><path fill="hsl(211, 20%, 56%)" fill-rule="evenodd" clip-rule="evenodd" d="M2.002 6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H12.28l-4.762 2.858A1 1 0 0 1 6.002 21v-2h-1a3 3 0 0 1-3-3V6Zm3-1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2a1 1 0 0 1 1 1v1.234l3.486-2.092a1 1 0 0 1 .514-.142h7a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-14Z"></path></svg> <p id="bsky_comment_amount">${latestPost.replyCount}</p> </div> <div class="bsky_post_metric"> <svg fill="none" width="22" viewBox="0 0 24 24" height="22" style="color: rgb(120, 142, 165);"><path fill="hsl(211, 20%, 56%)" fill-rule="evenodd" clip-rule="evenodd" d="M17.957 2.293a1 1 0 1 0-1.414 1.414L17.836 5H6a3 3 0 0 0-3 3v3a1 1 0 1 0 2 0V8a1 1 0 0 1 1-1h11.836l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.47-2.47a1.75 1.75 0 0 0 0-2.474l-2.47-2.47ZM20 12a1 1 0 0 1 1 1v3a3 3 0 0 1-3 3H6.164l1.293 1.293a1 1 0 1 1-1.414 1.414l-2.47-2.47a1.75 1.75 0 0 1 0-2.474l2.47-2.47a1 1 0 0 1 1.414 1.414L6.164 17H18a1 1 0 0 0 1-1v-3a1 1 0 0 1 1-1Z"></path></svg> <p id="bsky_repost_amount">${latestPost.repostCount + latestPost.quoteCount}</p> </div> <div class="bsky_post_metric"> <svg fill="none" width="22" viewBox="0 0 24 24" height="22" style="color: rgb(120, 142, 165); pointer-events: none;"><path fill="hsl(211, 20%, 56%)" fill-rule="evenodd" clip-rule="evenodd" d="M16.734 5.091c-1.238-.276-2.708.047-4.022 1.38a1 1 0 0 1-1.424 0C9.974 5.137 8.504 4.814 7.266 5.09c-1.263.282-2.379 1.206-2.92 2.556C3.33 10.18 4.252 14.84 12 19.348c7.747-4.508 8.67-9.168 7.654-11.7-.541-1.351-1.657-2.275-2.92-2.557Zm4.777 1.812c1.604 4-.494 9.69-9.022 14.47a1 1 0 0 1-.978 0C2.983 16.592.885 10.902 2.49 6.902c.779-1.942 2.414-3.334 4.342-3.764 1.697-.378 3.552.003 5.169 1.286 1.617-1.283 3.472-1.664 5.17-1.286 1.927.43 3.562 1.822 4.34 3.764Z"></path></svg> <p id="bsky_likes_amount">${latestPost.likeCount}</p> </div> </div> </div> ` return bskyElement } let bskyCSS = /*html*/` <style> #bsky_link { all: unset; cursor: pointer; } #bsky_link:focus { outline: 2px solid #0085ff; } #bsky { min-width: 200px; border-radius: .5rem; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } @media (prefers-color-scheme: dark) { #bsky{ background-color: #161e27; color: #ffffff; } } #bsky_header { padding: 1rem; padding-bottom: .25rem; height: 2rem; display: flex; align-items: center; } #bsky_avatar { height: 100%; border-radius: 50%; } #bsky_name_handle { margin-left: .25rem; } #bsky_display_name { font-weight: bold; } #bsky_icon { height: 100%; margin-left: auto; margin-right: 1rem; } #bsky_post_content { padding: .5rem; } #bsky_post_text { padding: .25rem; } .bsky_hashtag { color: rgb(35, 139, 254, 1); text-decoration: none; } .bsky_hashtag_link { color: rgb(35, 139, 254, 1); text-decoration: none; } .bsky_hashtag_link:hover { text-decoration: underline; } #bsky_post_image { width: 100%; border-radius: 10px; object-fit: cover; } #bsky_post_date { margin-left: 1rem; } #bsky_metrics { display: flex; flex-direction: row; justify-content: space-around; } .bsky_post_metric { display: flex; align-items: center; padding: .25rem; } </style> ` // exported functions function FeedJSON() { return feed.data.feed } function LatestBskyPostJSON() { return latestPost } function LatestBskyPostHTML() { return bskyCSS + bskyElementLinkedHashtags } function LatestBskyPostHTMLAsLink() { return bskyCSS + /*html*/` <a id="bsky_link" target="_blank" href="${weburl}">${bskyElementStyledHashtags}</a> ` } // init const agent = new AtpAgent({ service: 'https://bsky.social', persistSession: (evt, session) => { // handle session update --> for future Oauth stuff } }) try { await agent.login({ identifier: process.env.BSKY_ID, password: process.env.BSKY_PASS }) await GetFeed() SetLatestPost() SetLatestPostWithHashtagLinks() SetLatestPostWithStyledHashtags() bskyElementLinkedHashtags = ConstructBskyElement(latestPostWithHashtagLinks) bskyElementStyledHashtags = ConstructBskyElement(latestPostWithStyledHashtags) } catch (e) { console.log(e) } import cron from 'node-cron' cron.schedule('0 * * * *', async () => { try { await GetFeed() SetLatestPost() SetLatestPostWithHashtagLinks() SetLatestPostWithStyledHashtags() bskyElementLinkedHashtags = ConstructBskyElement(latestPostWithHashtagLinks) bskyElementStyledHashtags = ConstructBskyElement(latestPostWithStyledHashtags) } catch(e) { console.log(e) } }) export {FeedJSON, LatestBskyPostJSON, LatestBskyPostHTML, LatestBskyPostHTMLAsLink}