Compare commits
6 Commits
efdce954ac
...
ce0871b052
Author | SHA1 | Date | |
---|---|---|---|
ce0871b052 | |||
22c773c38e | |||
3e640524e8 | |||
6bc35b1629 | |||
c06b5b010c | |||
61bec8904e |
31
src/lib/bluesky.ts
Normal file
31
src/lib/bluesky.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { env } from '$env/dynamic/private'
|
||||||
|
import { Agent, CredentialSession, RichText } from '@atproto/api'
|
||||||
|
import { get, writable } from 'svelte/store'
|
||||||
|
|
||||||
|
const bskyClient = writable<null | Agent>(null)
|
||||||
|
|
||||||
|
export const getBskyClient = async () => {
|
||||||
|
let client = get(bskyClient)
|
||||||
|
if (client === null) {
|
||||||
|
client = await loginToBsky()
|
||||||
|
bskyClient.set(client)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
export const postToBsky = async (text: string) => {
|
||||||
|
let client = await getBskyClient()
|
||||||
|
const rt = new RichText({ text })
|
||||||
|
await rt.detectFacets(client)
|
||||||
|
const {uri} = await client.post({
|
||||||
|
text: rt.text,
|
||||||
|
facets: rt.facets,
|
||||||
|
})
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginToBsky = async () => {
|
||||||
|
const creds = new CredentialSession(new URL("https://bsky.social"))
|
||||||
|
await creds.login({ identifier: 'gaze.systems', password: env.BSKY_PASSWORD ?? "" })
|
||||||
|
return new Agent(creds)
|
||||||
|
}
|
@ -1,9 +1,4 @@
|
|||||||
import type { Cookies } from '@sveltejs/kit'
|
import type { Cookies } from '@sveltejs/kit'
|
||||||
import { env } from '$env/dynamic/private'
|
|
||||||
import { get, writable } from 'svelte/store'
|
|
||||||
import { existsSync, readFileSync } from 'fs'
|
|
||||||
import { Agent, CredentialSession } from '@atproto/api'
|
|
||||||
import SGDB from 'steamgriddb'
|
|
||||||
|
|
||||||
export const scopeCookies = (cookies: Cookies, path: string) => {
|
export const scopeCookies = (cookies: Cookies, path: string) => {
|
||||||
return {
|
return {
|
||||||
@ -18,78 +13,3 @@ export const scopeCookies = (cookies: Cookies, path: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`
|
|
||||||
export const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0'));
|
|
||||||
|
|
||||||
export const loginToBsky = async () => {
|
|
||||||
const creds = new CredentialSession(new URL("https://bsky.social"))
|
|
||||||
await creds.login({ identifier: 'gaze.systems', password: env.BSKY_PASSWORD ?? "" })
|
|
||||||
return new Agent(creds)
|
|
||||||
}
|
|
||||||
export const bskyClient = writable<null | Agent>(null)
|
|
||||||
|
|
||||||
const cachedLastTrack = writable<{track: LastTrack | null, since: number}>({track: null, since: 0})
|
|
||||||
export type LastTrack = {name: string, artist: string, image: string | null, link: string}
|
|
||||||
export const lastFmGetNowPlaying: () => Promise<LastTrack | null> = async () => {
|
|
||||||
var cached = get(cachedLastTrack)
|
|
||||||
if (Date.now() - cached.since < 10 * 1000) {
|
|
||||||
return cached.track
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const API_URL = "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=yusdacra&api_key=da1911d405b5b37383e200b8f36ee9ec&format=json&limit=1"
|
|
||||||
var resp = await (await fetch(API_URL)).json()
|
|
||||||
var track = resp.recenttracks.track[0] ?? null
|
|
||||||
if (!(track['@attr'].nowplaying ?? null)) {
|
|
||||||
throw "no nowplaying track found"
|
|
||||||
}
|
|
||||||
var data = {
|
|
||||||
name: track.name,
|
|
||||||
artist: track.artist['#text'],
|
|
||||||
image: track.image[2]['#text'] ?? null,
|
|
||||||
link: track.url,
|
|
||||||
}
|
|
||||||
cachedLastTrack.set({track: data, since: Date.now()})
|
|
||||||
return data
|
|
||||||
} catch(why) {
|
|
||||||
console.log("could not fetch last fm: ", why)
|
|
||||||
cachedLastTrack.set({track: null, since: Date.now()})
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const steamgriddbClient = writable<SGDB | null>(null);
|
|
||||||
const cachedLastGame = writable<{game: LastGame | null, since: number}>({game: null, since: 0})
|
|
||||||
export type LastGame = {name: string, link: string, icon: string, pfp: string}
|
|
||||||
export const steamGetNowPlaying: () => Promise<LastGame | null> = async () => {
|
|
||||||
var griddbClient = get(steamgriddbClient)
|
|
||||||
if (griddbClient === null) {
|
|
||||||
griddbClient = new SGDB(env.STEAMGRIDDB_API_KEY)
|
|
||||||
steamgriddbClient.set(griddbClient)
|
|
||||||
}
|
|
||||||
var cached = get(cachedLastGame)
|
|
||||||
if (Date.now() - cached.since < 10 * 1000) {
|
|
||||||
return cached.game
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const API_URL = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${env.STEAM_API_KEY}&steamids=76561198106829949&format=json`
|
|
||||||
var profile = (await (await fetch(API_URL)).json()).response.players[0]
|
|
||||||
if (!profile.gameid) {
|
|
||||||
throw "no game is being played"
|
|
||||||
}
|
|
||||||
var icons = await griddbClient.getIconsBySteamAppId(profile.gameid, ['official'])
|
|
||||||
console.log(icons)
|
|
||||||
var game = {
|
|
||||||
name: profile.gameextrainfo,
|
|
||||||
link: `https://store.steampowered.com/app/${profile.gameid}`,
|
|
||||||
icon: icons[0].thumb.toString(),
|
|
||||||
pfp: profile.avatarmedium,
|
|
||||||
}
|
|
||||||
cachedLastGame.set({game, since: Date.now()})
|
|
||||||
return game
|
|
||||||
} catch(why) {
|
|
||||||
console.log("could not fetch steam: ", why)
|
|
||||||
cachedLastGame.set({game: null, since: Date.now()})
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
34
src/lib/lastfm.ts
Normal file
34
src/lib/lastfm.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { get, writable } from "svelte/store"
|
||||||
|
|
||||||
|
const GET_RECENT_TRACKS_ENDPOINT = "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=yusdacra&api_key=da1911d405b5b37383e200b8f36ee9ec&format=json&limit=1"
|
||||||
|
const CACHE_EXPIRY_SECONDS = 10
|
||||||
|
|
||||||
|
type LastTrack = {name: string, artist: string, image: string | null, link: string}
|
||||||
|
type CachedLastTrack = {track: LastTrack | null, since: number}
|
||||||
|
const cachedLastTrack = writable<CachedLastTrack>({track: null, since: 0})
|
||||||
|
|
||||||
|
export const lastFmGetNowPlaying: () => Promise<LastTrack | null> = async () => {
|
||||||
|
var cached = get(cachedLastTrack)
|
||||||
|
if (Date.now() - cached.since < CACHE_EXPIRY_SECONDS * 1000) {
|
||||||
|
return cached.track
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var resp = await (await fetch(GET_RECENT_TRACKS_ENDPOINT)).json()
|
||||||
|
var track = resp.recenttracks.track[0] ?? null
|
||||||
|
if (!(track['@attr'].nowplaying ?? null)) {
|
||||||
|
throw "no nowplaying track found"
|
||||||
|
}
|
||||||
|
var data = {
|
||||||
|
name: track.name,
|
||||||
|
artist: track.artist['#text'],
|
||||||
|
image: track.image[2]['#text'] ?? null,
|
||||||
|
link: track.url,
|
||||||
|
}
|
||||||
|
cachedLastTrack.set({track: data, since: Date.now()})
|
||||||
|
return data
|
||||||
|
} catch(why) {
|
||||||
|
console.log("could not fetch last fm: ", why)
|
||||||
|
cachedLastTrack.set({track: null, since: Date.now()})
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,15 @@ import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|||||||
import { nanoid } from 'nanoid'
|
import { nanoid } from 'nanoid'
|
||||||
import { env } from '$env/dynamic/private'
|
import { env } from '$env/dynamic/private'
|
||||||
|
|
||||||
|
export interface OutgoingLinkData {
|
||||||
|
name: string,
|
||||||
|
link: string,
|
||||||
|
}
|
||||||
|
|
||||||
export interface Note {
|
export interface Note {
|
||||||
content: string,
|
content: string,
|
||||||
published: number,
|
published: number,
|
||||||
|
outgoingLinks?: OutgoingLinkData[],
|
||||||
}
|
}
|
||||||
type NoteId = string
|
type NoteId = string
|
||||||
|
|
||||||
@ -32,8 +38,7 @@ export const writeNote = (id: NoteId, note: Note) => {
|
|||||||
writeNotesList([id].concat(noteList))
|
writeNotesList([id].concat(noteList))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const createNote = (note: Note) => {
|
export const createNote = (id: NoteId, note: Note) => {
|
||||||
const id = genNoteId()
|
|
||||||
writeNote(id, note)
|
writeNote(id, note)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
45
src/lib/steam.ts
Normal file
45
src/lib/steam.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
import SGDB from "steamgriddb";
|
||||||
|
import { get, writable } from "svelte/store";
|
||||||
|
|
||||||
|
const STEAM_ID = "76561198106829949"
|
||||||
|
const GET_PLAYER_SUMMARY_ENDPOINT = `http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${env.STEAM_API_KEY}&steamids=${STEAM_ID}&format=json`
|
||||||
|
const CACHE_EXPIRY_SECONDS = 10
|
||||||
|
|
||||||
|
type LastGame = {name: string, link: string, icon: string, pfp: string}
|
||||||
|
type CachedLastGame = {game: LastGame | null, since: number}
|
||||||
|
|
||||||
|
const steamgriddbClient = writable<SGDB | null>(null);
|
||||||
|
const cachedLastGame = writable<CachedLastGame>({game: null, since: 0})
|
||||||
|
|
||||||
|
export const steamGetNowPlaying: () => Promise<LastGame | null> = async () => {
|
||||||
|
var griddbClient = get(steamgriddbClient)
|
||||||
|
if (griddbClient === null) {
|
||||||
|
griddbClient = new SGDB(env.STEAMGRIDDB_API_KEY)
|
||||||
|
steamgriddbClient.set(griddbClient)
|
||||||
|
}
|
||||||
|
var cached = get(cachedLastGame)
|
||||||
|
if (Date.now() - cached.since < CACHE_EXPIRY_SECONDS * 1000) {
|
||||||
|
return cached.game
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var profile = (await (await fetch(GET_PLAYER_SUMMARY_ENDPOINT)).json()).response.players[0]
|
||||||
|
if (!profile.gameid) {
|
||||||
|
throw "no game is being played"
|
||||||
|
}
|
||||||
|
var icons = await griddbClient.getIconsBySteamAppId(profile.gameid, ['official'])
|
||||||
|
console.log(icons)
|
||||||
|
var game = {
|
||||||
|
name: profile.gameextrainfo,
|
||||||
|
link: `https://store.steampowered.com/app/${profile.gameid}`,
|
||||||
|
icon: icons[0].thumb.toString(),
|
||||||
|
pfp: profile.avatarmedium,
|
||||||
|
}
|
||||||
|
cachedLastGame.set({game, since: Date.now()})
|
||||||
|
return game
|
||||||
|
} catch(why) {
|
||||||
|
console.log("could not fetch steam: ", why)
|
||||||
|
cachedLastGame.set({game: null, since: Date.now()})
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
57
src/lib/visits.ts
Normal file
57
src/lib/visits.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { env } from "$env/dynamic/private";
|
||||||
|
import { scopeCookies } from "$lib";
|
||||||
|
import type { Cookies } from "@sveltejs/kit";
|
||||||
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
||||||
|
import { get, writable } from "svelte/store";
|
||||||
|
|
||||||
|
const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`
|
||||||
|
const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0'));
|
||||||
|
|
||||||
|
export const incrementVisitCount = (request: Request, cookies: Cookies) => {
|
||||||
|
let currentVisitCount = get(visitCount)
|
||||||
|
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
||||||
|
const ua = request.headers.get('user-agent')
|
||||||
|
const isBot = ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk)/) !== null : true
|
||||||
|
if (!isBot) {
|
||||||
|
const scopedCookies = scopeCookies(cookies, '/')
|
||||||
|
// parse the last visit timestamp from cookies if it exists
|
||||||
|
const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0")
|
||||||
|
// get unix timestamp
|
||||||
|
const currentTime = new Date().getTime()
|
||||||
|
const timeSinceVisit = currentTime - visitedTimestamp
|
||||||
|
// check if this is the first time a client is visiting or if an hour has passed since they last visited
|
||||||
|
if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
|
||||||
|
// increment current and write to the store
|
||||||
|
currentVisitCount += 1; visitCount.set(currentVisitCount)
|
||||||
|
// update the cookie with the current timestamp
|
||||||
|
scopedCookies.set('visitedTimestamp', currentTime.toString())
|
||||||
|
// write the visit count to a file so we can load it later again
|
||||||
|
writeFileSync(visitCountFile, currentVisitCount.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentVisitCount
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notifyDarkVisitors = (url: URL, request: Request) => {
|
||||||
|
fetch('https://api.darkvisitors.com/visits', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`,
|
||||||
|
'content-type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
request_path: url.pathname,
|
||||||
|
request_method: request.method,
|
||||||
|
request_headers: request.headers,
|
||||||
|
})
|
||||||
|
}).catch((why) => {
|
||||||
|
console.log("failed sending dark visitors analytics:", why)
|
||||||
|
return null
|
||||||
|
}).then(async (resp) => {
|
||||||
|
if (resp !== null) {
|
||||||
|
const msg = await resp.json()
|
||||||
|
const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})`
|
||||||
|
console.log(`sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,62 +1,17 @@
|
|||||||
import { env } from '$env/dynamic/private';
|
import { incrementVisitCount, notifyDarkVisitors } from '$lib/visits.js';
|
||||||
import { scopeCookies, visitCount, visitCountFile } from '$lib';
|
|
||||||
import { writeFileSync } from 'fs';
|
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
export const csr = true;
|
export const csr = true;
|
||||||
export const ssr = true;
|
export const ssr = true;
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
export const trailingSlash = 'always';
|
export const trailingSlash = 'always';
|
||||||
|
|
||||||
export async function load({ request, cookies, url, setHeaders, fetch }) {
|
export async function load({ request, cookies, url, setHeaders }) {
|
||||||
fetch('https://api.darkvisitors.com/visits', {
|
notifyDarkVisitors(url, request) // no await so it doesnt block load
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`,
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
request_path: url.pathname,
|
|
||||||
request_method: request.method,
|
|
||||||
request_headers: request.headers,
|
|
||||||
})
|
|
||||||
}).catch((why) => {
|
|
||||||
console.log("failed sending dark visitors analytics:", why)
|
|
||||||
return null
|
|
||||||
}).then(async (resp) => {
|
|
||||||
if (resp !== null) {
|
|
||||||
const msg = await resp.json()
|
|
||||||
const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})`
|
|
||||||
console.log(`sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let currentVisitCount = get(visitCount)
|
|
||||||
// check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
|
|
||||||
const ua = request.headers.get('user-agent')
|
|
||||||
const isBot = ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk)/) !== null : true
|
|
||||||
if (!isBot) {
|
|
||||||
const scopedCookies = scopeCookies(cookies, '/')
|
|
||||||
// parse the last visit timestamp from cookies if it exists
|
|
||||||
const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0")
|
|
||||||
// get unix timestamp
|
|
||||||
const currentTime = new Date().getTime()
|
|
||||||
const timeSinceVisit = currentTime - visitedTimestamp
|
|
||||||
// check if this is the first time a client is visiting or if an hour has passed since they last visited
|
|
||||||
if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
|
|
||||||
// increment current and write to the store
|
|
||||||
currentVisitCount += 1; visitCount.set(currentVisitCount)
|
|
||||||
// update the cookie with the current timestamp
|
|
||||||
scopedCookies.set('visitedTimestamp', currentTime.toString())
|
|
||||||
// write the visit count to a file so we can load it later again
|
|
||||||
writeFileSync(visitCountFile, currentVisitCount.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeaders({ 'Cache-Control': 'no-cache' })
|
setHeaders({ 'Cache-Control': 'no-cache' })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
route: url.pathname,
|
route: url.pathname,
|
||||||
visitCount: currentVisitCount,
|
visitCount: incrementVisitCount(request, cookies),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { lastFmGetNowPlaying, steamGetNowPlaying } from "$lib"
|
import { lastFmGetNowPlaying } from "$lib/lastfm"
|
||||||
|
import { steamGetNowPlaying } from "$lib/steam"
|
||||||
|
|
||||||
export const load = async ({}) => {
|
export const load = async ({}) => {
|
||||||
const lastTrack = await lastFmGetNowPlaying()
|
const lastTrack = await lastFmGetNowPlaying()
|
||||||
|
@ -15,6 +15,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const highlightedNote = data.notes.get(data.highlightedNote ?? '') ?? null
|
const highlightedNote = data.notes.get(data.highlightedNote ?? '') ?? null
|
||||||
|
|
||||||
|
// this is ASS this should be a tailwind class
|
||||||
|
const getTextShadowStyle = (color: string) => {
|
||||||
|
return `text-shadow: 0 0 1px theme(colors.ralsei.black), 0 0 5px ${color};`
|
||||||
|
}
|
||||||
|
const outgoingLinkColors: Record<string, string> = {
|
||||||
|
bsky: "rgb(0, 133, 255)",
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@ -33,15 +41,19 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<pre class="language-bash"><code class="language-bash"><nobr>
|
<pre class="language-bash"><code class="language-bash"><nobr>
|
||||||
<Token v="[" punct/>gazesystems <Token v="/log/" keywd/><Token v="]$" punct/> <Token v="source" funct/> log.nu
|
<Token v="[" punct/>gazesystems <Token v="/" keywd/><Token v="]$" punct/> <Token v="source" funct/> <Token v="scripts/log.nu" />
|
||||||
<br>
|
<br>
|
||||||
<Token v="[" punct/>gazesystems <Token v="/log/" keywd/><Token v="]$" punct/> <Token v="ls" funct/> log <Token v="|" punct/> <Token v="each" funct/> <Token v="{" punct/><Token v="|" punct/>file<Token v="|" punct/> <Token v="render" funct/> <Token v="(" punct/><Token v="open" funct/> $file.name<Token v=")" punct/><Token v="}" punct/>
|
<Token v="[" punct/>gazesystems <Token v="/" keywd/><Token v="]$" punct/> <Token v="ls" funct/> <Token v="log" /> <Token v="|" punct/> <Token v="each" funct/> <Token v="{" punct/><Token v="|" punct/><Token v="file"/><Token v="|" punct/> <Token v="render" funct/> <Token v="(" punct/><Token v="open" funct/> <Token v="$file.name" /><Token v=")" punct/><Token v="}" punct/>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
{#each data.notes as [noteId, note], index}
|
{#each data.notes as [noteId, note], index}
|
||||||
{@const isHighlighted = noteId === data.highlightedNote}
|
{@const isHighlighted = noteId === data.highlightedNote}
|
||||||
<div class="text-wrap break-words max-w-[70ch] leading-none">
|
<div class="text-wrap break-words max-w-[70ch] leading-none">
|
||||||
<Token v={renderDate(note.published)} small={!isHighlighted}/> <Token v={noteId} keywd small={!isHighlighted}/><Token v="#" punct/> <Token v={note.content} str/>
|
<Token v={renderDate(note.published)} small={!isHighlighted}/> <Token v={noteId} keywd small={!isHighlighted}/><Token v="#" punct/> <Token v={note.content} str/>
|
||||||
|
{#each note.outgoingLinks ?? [] as {name, link}}
|
||||||
|
{@const color = outgoingLinkColors[name]}
|
||||||
|
<span class="text-sm"><Token v="(" punct/><a style="color: {color};{getTextShadowStyle(color)}" href={link}>{name}</a><Token v=")" punct/></span>
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if index < data.notes.size - 1}
|
{#if index < data.notes.size - 1}
|
||||||
<div class="mt-3"/>
|
<div class="mt-3"/>
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
import { PUBLIC_BASE_URL } from '$env/static/public';
|
import { PUBLIC_BASE_URL } from '$env/static/public';
|
||||||
import { bskyClient, loginToBsky } from '$lib';
|
import { postToBsky } from '$lib/bluesky';
|
||||||
import { createNote } from '$lib/notes.js';
|
import { createNote, genNoteId, type Note } from '$lib/notes';
|
||||||
import { RichText } from '@atproto/api';
|
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
interface NoteData {
|
interface NoteData {
|
||||||
content: string,
|
content: string,
|
||||||
@ -15,30 +13,33 @@ export const POST = async ({ request }) => {
|
|||||||
if (token !== env.GAZEBOT_TOKEN) {
|
if (token !== env.GAZEBOT_TOKEN) {
|
||||||
return new Response("rizz failed", { status: 403 })
|
return new Response("rizz failed", { status: 403 })
|
||||||
}
|
}
|
||||||
|
// get id
|
||||||
|
const noteId = genNoteId()
|
||||||
// get note data
|
// get note data
|
||||||
const noteData: NoteData = await request.json()
|
const noteData: NoteData = await request.json()
|
||||||
console.log("want to create note with data: ", noteData)
|
console.log(`want to create note #${noteId} with data: `, noteData)
|
||||||
// create note
|
// get a date before we start publishing to other platforms
|
||||||
const published = Date.now()
|
let note: Note = {
|
||||||
const noteId = createNote({ content: noteData.content, published })
|
content: noteData.content,
|
||||||
|
published: Date.now(),
|
||||||
|
outgoingLinks: [],
|
||||||
|
}
|
||||||
|
let errors: string[] = []
|
||||||
// bridge to bsky if want to bridge
|
// bridge to bsky if want to bridge
|
||||||
if (noteData.bskyPosse) {
|
if (noteData.bskyPosse) {
|
||||||
let client = get(bskyClient)
|
const postContent = `${noteData.content} (${PUBLIC_BASE_URL}/log?id=${noteId})`
|
||||||
if (client === null) {
|
try {
|
||||||
client = await loginToBsky()
|
const bskyUrl = await postToBsky(postContent)
|
||||||
bskyClient.set(client)
|
note.outgoingLinks?.push({name: "bsky", link: bskyUrl})
|
||||||
|
} catch(why) {
|
||||||
|
console.log(`failed to post note #${noteId} to bsky: `, why)
|
||||||
|
errors.push(`error while posting to bsky: ${why}`)
|
||||||
}
|
}
|
||||||
const rt = new RichText({
|
|
||||||
text: `${noteData.content} (${PUBLIC_BASE_URL}/log?id=${noteId})`,
|
|
||||||
})
|
|
||||||
await rt.detectFacets(client)
|
|
||||||
await client.post({
|
|
||||||
text: rt.text,
|
|
||||||
facets: rt.facets,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// send back created note id
|
// create note (this should never fail otherwise it would defeat the whole purpose lol)
|
||||||
return new Response(JSON.stringify({ noteId }), {
|
createNote(noteId, note)
|
||||||
|
// send back created note id and any errors that occurred
|
||||||
|
return new Response(JSON.stringify({ noteId, errors }), {
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
'cache-control': 'no-store',
|
'cache-control': 'no-store',
|
||||||
|
Loading…
Reference in New Issue
Block a user