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
JavaScript
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}