diff --git a/bun.lockb b/bun.lockb index 10d527e..4da935d 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5a3d77a..f15566d 100644 --- a/package.json +++ b/package.json @@ -13,34 +13,34 @@ }, "devDependencies": { "@sveltejs/enhanced-img": "^0.3.10", - "@sveltejs/kit": "^2.15.1", + "@sveltejs/kit": "^2.17.1", "@sveltejs/vite-plugin-svelte": "^3.1.2", - "@tailwindcss/forms": "^0.5.9", - "@tailwindcss/typography": "^0.5.15", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", "@types/eslint": "^9.6.1", - "@types/node": "^22.10.3", + "@types/node": "^22.13.1", "autoprefixer": "^10.4.20", - "eslint": "^9.17.0", + "eslint": "^9.19.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.46.1", "globals": "^15.14.0", "mdsvex": "^0.12.3", - "postcss": "^8.4.49", + "postcss": "^8.5.1", "prettier": "^3.4.2", - "prettier-plugin-svelte": "^3.3.2", + "prettier-plugin-svelte": "^3.3.3", "svelte": "^4.2.19", "svelte-adapter-bun": "^0.5.2", "svelte-check": "^3.8.6", "sveltekit-rate-limiter": "^0.6.1", "tailwindcss": "^3.4.17", "tslib": "^2.8.1", - "typescript": "^5.7.2", - "typescript-eslint": "^8.19.0", - "vite": "^5.4.11" + "typescript": "^5.7.3", + "typescript-eslint": "^8.23.0", + "vite": "^5.4.14" }, "type": "module", "dependencies": { - "@neodrag/svelte": "^2.2.0", + "@neodrag/svelte": "^2.3.0", "@skyware/bot": "^0.3.8", "@std/toml": "npm:@jsr/std__toml", "base64url": "^3.0.1", diff --git a/src/components/note.svelte b/src/components/note.svelte index 0644807..6941878 100644 --- a/src/components/note.svelte +++ b/src/components/note.svelte @@ -1,9 +1,32 @@ +
-{#if !onlyContent}   {/if} +{#if !onlyContent} {/if} +{#if note.hasMedia}{/if} +{#if note.hasQuote}{/if} {#each note.outgoingLinks ?? [] as {name, link}} {@const color = outgoingLinkColors[name]} {name} diff --git a/src/lib/bluesky.ts b/src/lib/bluesky.ts index 11b8100..0b791d0 100644 --- a/src/lib/bluesky.ts +++ b/src/lib/bluesky.ts @@ -1,5 +1,5 @@ import { env } from '$env/dynamic/private' -import { Bot } from "@skyware/bot"; +import { Bot, Post } from "@skyware/bot"; import { get, writable } from 'svelte/store' const bskyClient = writable(null) @@ -17,4 +17,19 @@ const loginToBsky = async () => { const bot = new Bot({ service: "https://bsky.social" }) await bot.login({ identifier: 'gaze.systems', password: env.BSKY_PASSWORD ?? "" }) return bot +} + +export const getUserPosts = async (did: string, includeReposts: boolean = false, count: number = 10) => { + const client = await getBskyClient() + let feedCursor = undefined; + let posts: Post[] = [] + // fetch requested amount of posts + while (posts.length < count || feedCursor === undefined) { + let feedData = await client.getUserPosts( + did, { limit: count, filter: 'posts_no_replies', cursor: feedCursor } + ) + posts.push(...feedData.posts.filter((post) => !includeReposts && post.author.did === did)) + feedCursor = feedData.cursor + } + return posts } \ No newline at end of file diff --git a/src/lib/notes.ts b/src/lib/notes.ts deleted file mode 100644 index 9b73188..0000000 --- a/src/lib/notes.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { existsSync, readFileSync, writeFileSync } from 'fs' -import { nanoid } from 'nanoid' -import { env } from '$env/dynamic/private' - -export interface OutgoingLinkData { - name: string, - link: string, -} - -export interface Note { - content: string, - published: number, - outgoingLinks?: OutgoingLinkData[], - replyTo?: NoteId, -} -type NoteId = string - -export const notesFolder = `${env.WEBSITE_DATA_DIR}/note` -export const notesListFile = `${env.WEBSITE_DATA_DIR}/notes` -export const noteIdLength = 8; - -export const getNotePath = (id: NoteId) => { return `${notesFolder}/${id}` } -export const genNoteId = () => { - let id = nanoid(noteIdLength) - while (existsSync(getNotePath(id))) { - id = nanoid(noteIdLength) - } - return id -} -export const noteExists = (id: NoteId) => { return existsSync(getNotePath(id)) } -export const readNote = (id: NoteId): Note => { - return JSON.parse(readFileSync(getNotePath(id)).toString()) -} -export const findReplyRoot = (id: NoteId): {rootNote: Note, rootNoteId: NoteId} => { - let noteId: string | null = id - let current: {rootNote?: Note, rootNoteId?: NoteId} = {} - while (noteId !== null) { - current.rootNote = readNote(noteId) - current.rootNoteId = noteId - noteId = current.rootNote.replyTo ?? null - } - if (current.rootNote === undefined || current.rootNoteId === undefined) { - throw "no note with id found" - } - return { - rootNote: current.rootNote, - rootNoteId: current.rootNoteId, - } -} -export const writeNote = (id: NoteId, note: Note) => { - writeFileSync(getNotePath(id), JSON.stringify(note)) - // only append to note list if its not in it yet - let noteList = readNotesList() - if (noteList.indexOf(id) === -1) { - writeNotesList([id].concat(noteList)) - } -} -export const createNote = (id: NoteId, note: Note) => { - writeNote(id, note) - return id -} - -export const readNotesList = (): NoteId[] => { - return JSON.parse(readFileSync(notesListFile).toString()) -} -export const writeNotesList = (note_ids: NoteId[]) => { - writeFileSync(notesListFile, JSON.stringify(note_ids)) -} \ No newline at end of file diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 3b28ffe..df4eeac 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,18 +1,18 @@ +import { getUserPosts } from "$lib/bluesky.js" import { lastFmGetNowPlaying } from "$lib/lastfm" -import { readNote, readNotesList } from "$lib/notes.js" import { steamGetNowPlaying } from "$lib/steam" +import { noteFromBskyPost } from "../components/note.svelte" export const load = async ({}) => { const lastTrack = await lastFmGetNowPlaying() const lastGame = await steamGetNowPlaying() + const lastNote = noteFromBskyPost((await getUserPosts("did:plc:dfl62fgb7wtjj3fcbb72naae", false, 1))[0]) let banners: number[] = [] while (banners.length < 3) { const no = getBannerNo(banners) banners.push(no) } - const lastNoteId = readNotesList()[0] - const lastNote = readNote(lastNoteId) - return {banners, lastTrack, lastGame, lastNote, lastNoteId} + return {banners, lastTrack, lastGame, lastNote} } const getBannerNo = (others: number[]) => { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 2ccb709..7e5ec96 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -155,7 +155,7 @@ published on {renderDate(data.lastNote.published)}
- +
{#if data.lastTrack} diff --git a/src/routes/entries/+page.server.ts b/src/routes/entries/+page.server.ts index ce806dd..3bc4e89 100644 --- a/src/routes/entries/+page.server.ts +++ b/src/routes/entries/+page.server.ts @@ -10,6 +10,6 @@ export const load = (params) => { if (log_page !== null) { url.searchParams.append("page", log_page) } - var logs_result = load_logs({url}) + var logs_result = load_logs() return logs_result } \ No newline at end of file diff --git a/src/routes/log/+page.server.ts b/src/routes/log/+page.server.ts index 49a3672..021775a 100644 --- a/src/routes/log/+page.server.ts +++ b/src/routes/log/+page.server.ts @@ -1,41 +1,12 @@ -import { noteExists, readNote, readNotesList } from '$lib/notes' +import { getUserPosts } from '$lib/bluesky.js'; +import { noteFromBskyPost } from '../../components/note.svelte'; -const notesPerPage: number = 15 - -export const load = ({ url }) => { - return _load({ url }) +export const load = async ({ }) => { + return _load() } -export const _load = ({ url }: { url: URL }) => { - // get the note id to search for and display the page it is in - const noteId = url.searchParams.get("id") - // get the page no if one is provided, otherwise default to 1 - let page = parseInt(url.searchParams.get("page") || "1") - if (isNaN(page)) { page = 1 } - - // calculate page count - const notesList = readNotesList() - const pageCount = Math.ceil(notesList.length / notesPerPage) - - // find what page the note id if supplied is from - if (noteId !== null && noteExists(noteId)) { - const noteIndex = notesList.lastIndexOf(noteId) - if (noteIndex > -1) { - page = Math.floor(noteIndex / notesPerPage) + 1 - } +export const _load = async () => { + return { + feedPosts: (await getUserPosts("did:plc:dfl62fgb7wtjj3fcbb72naae", false, 13)).map(noteFromBskyPost), } - - // clamp page between our min and max - page = Math.min(page, pageCount) - page = Math.max(page, 1) - - // get the notes from the chosen page - const notes = new Map( - notesList.slice((page - 1) * notesPerPage, page * notesPerPage) - .map( - (id) => { return [id, readNote(id)] } - ) - ) - - return { notes, highlightedNote: noteId, page } } \ No newline at end of file diff --git a/src/routes/log/+page.svelte b/src/routes/log/+page.svelte index 52a26c2..f95dafc 100644 --- a/src/routes/log/+page.svelte +++ b/src/routes/log/+page.svelte @@ -4,17 +4,8 @@ import Note from '../../components/note.svelte'; export let data; - - const highlightedNote = data.notes.get(data.highlightedNote ?? '') ?? null - - {#if highlightedNote !== null} - - - {/if} - -
gazesystems

-{#each data.notes as [id, note], index} -{@const isHighlighted = id === data.highlightedNote} - -{#if index < data.notes.size - 1} +{#each data.feedPosts as note, index} + +{#if index < data.feedPosts.length - 1}
{/if} {/each} diff --git a/src/routes/log/_rss/+server.ts b/src/routes/log/_rss/+server.ts deleted file mode 100644 index e61687f..0000000 --- a/src/routes/log/_rss/+server.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { PUBLIC_BASE_URL } from '$env/static/public'; -import { readNote, readNotesList, type Note } from '$lib/notes.ts'; - -const logUrl = `${PUBLIC_BASE_URL}/log`; - -interface NoteData { - data: Note, - id: string, -} - -export const GET = async ({ }) => { - const log = readNotesList().map((id) => {return { data: readNote(id), id }}) - return new Response( - render(log), - { - headers: { - 'content-type': 'application/xml', - 'cache-control': 'no-store', - } - }) -}; - -const render = (log: NoteData[]) => ` - - - - dusk's notes (@gaze.systems) - ${logUrl} - a collection of random notes i write whenever, aka my microblogging spot - ${log.map((note) => ` - ${logUrl}/?id=${note.id} - ${logUrl}/?id=${note.id} - ${note.data.content} - ${new Date(note.data.published).toUTCString()} - `).join('')} - - - `; \ No newline at end of file diff --git a/src/routes/log/create/+server.ts b/src/routes/log/create/+server.ts deleted file mode 100644 index 03a4e3f..0000000 --- a/src/routes/log/create/+server.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { env } from '$env/dynamic/private'; -import { PUBLIC_BASE_URL } from '$env/static/public'; -import { getBskyClient } from '$lib/bluesky.js'; -import { createNote, findReplyRoot, genNoteId, readNote, type Note } from '$lib/notes'; -import type { Post, PostPayload, PostReference, ReplyRef } from '@skyware/bot'; - -interface NoteData { - content: string, - replyTo?: string, - embedUri?: string, - bskyPosse: boolean, -} - -export const POST = async ({ request }) => { - const token = request.headers.get('authorization') - if (token !== env.GAZEBOT_TOKEN) { - return new Response("rizz failed", { status: 403 }) - } - // get id - const noteId = genNoteId() - // get note data - const noteData: NoteData = await request.json() - console.log(`want to create note #${noteId} with data: `, noteData) - // get a date before we start publishing to other platforms - let note: Note = { - content: noteData.content, - published: Date.now(), - outgoingLinks: [], - replyTo: noteData.replyTo, - } - let errors: string[] = [] - let repliedNote: Note | null = null - if (noteData.replyTo !== undefined) { - repliedNote = readNote(noteData.replyTo) - } - // bridge to bsky if want to bridge - if (noteData.bskyPosse) { - const postContent = `${noteData.content} (${PUBLIC_BASE_URL}/log?id=${noteId})` - try { - const bot = await getBskyClient() - let postPayload: PostPayload = { - text: postContent, - createdAt: new Date(note.published), - external: noteData.embedUri, - } - let postRef: PostReference - // find parent and reply posts - let replyRef: ReplyRef | null = null - if (noteData.replyTo !== undefined && repliedNote !== null) { - const getBskyUri = (note: Note) => { return note.outgoingLinks?.find((v) => {return v.name === "bsky"})?.link } - const parentUri = getBskyUri(repliedNote) - if (parentUri !== undefined) { - const parentPost = await bot.getPost(parentUri) - postRef = await parentPost.reply(postPayload) - } else { - throw "a reply was requested but no reply is found" - } - } else { - postRef = await bot.post(postPayload) - } - note.outgoingLinks?.push({name: "bsky", link: postRef.uri}) - } catch(why) { - console.log(`failed to post note #${noteId} to bsky: `, why) - errors.push(`error while posting to bsky: ${why}`) - } - } - // create note (this should never fail otherwise it would defeat the whole purpose lol) - createNote(noteId, note) - // send back created note id and any errors that occurred - return new Response(JSON.stringify({ noteId, errors }), { - headers: { - 'content-type': 'application/json', - 'cache-control': 'no-store', - } - }) -}; diff --git a/src/styles/app.css b/src/styles/app.css index 0f1e888..e78b868 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -1,5 +1,9 @@ @import './prism-synthwave84.css'; +@import 'bluesky-profile-feed-embed/style.css'; +@import 'bluesky-profile-feed-embed/themes/light.css' (prefers-color-scheme: light); +@import 'bluesky-profile-feed-embed/themes/dim.css' (prefers-color-scheme: dark); + @tailwind base; @tailwind components; @tailwind utilities;