chore: migrated to forgejo
This commit is contained in:
parent
082d230cd8
commit
9efbdeac34
9 changed files with 207 additions and 94 deletions
|
@ -5,6 +5,7 @@ on:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: deploy
|
group: deploy
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
@ -12,7 +13,7 @@ concurrency:
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
if: github.repository == 'teidesu/tei.su' # do not run on forks
|
if: github.repository == 'teidesu/tei.su' # do not run on forks
|
||||||
runs-on: ubuntu-latest
|
runs-on: node22
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
|
@ -29,7 +30,7 @@ jobs:
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: git.stupid.fish
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ jobs:
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/teidesu/tei.su
|
images: git.stupid.fish/teidesu/tei.su
|
||||||
tags: type=sha
|
tags: type=sha
|
||||||
flavor: latest=true
|
flavor: latest=true
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
|
@ -50,7 +51,7 @@ jobs:
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: node22
|
||||||
needs: publish
|
needs: publish
|
||||||
steps:
|
steps:
|
||||||
- uses: teidesu/desu-deploy@main
|
- uses: teidesu/desu-deploy@main
|
|
@ -7,6 +7,7 @@ export default antfu({
|
||||||
typescript: true,
|
typescript: true,
|
||||||
astro: true,
|
astro: true,
|
||||||
solid: true,
|
solid: true,
|
||||||
|
yaml: false,
|
||||||
rules: {
|
rules: {
|
||||||
'curly': ['error', 'multi-line'],
|
'curly': ['error', 'multi-line'],
|
||||||
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { z } from 'zod'
|
||||||
|
|
||||||
import { ffetch } from '../../utils/fetch.ts'
|
import { ffetch } from '../../utils/fetch.ts'
|
||||||
|
|
||||||
|
import type { LastSeenItem } from './index.ts'
|
||||||
|
|
||||||
const ENDPOINT = 'https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed'
|
const ENDPOINT = 'https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed'
|
||||||
const TTL = 3 * 60 * 60 * 1000 // 3 hours
|
const TTL = 3 * 60 * 60 * 1000 // 3 hours
|
||||||
const STALE_TTL = 8 * 60 * 60 * 1000 // 8 hours
|
const STALE_TTL = 8 * 60 * 60 * 1000 // 8 hours
|
||||||
|
@ -15,7 +17,7 @@ const schema = z.object({
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const bskyLastSeen = new AsyncResource<z.infer<typeof schema>>({
|
export const bskyLastSeen = new AsyncResource<LastSeenItem | null>({
|
||||||
async fetcher() {
|
async fetcher() {
|
||||||
const res = await ffetch(ENDPOINT, {
|
const res = await ffetch(ENDPOINT, {
|
||||||
query: {
|
query: {
|
||||||
|
@ -29,8 +31,24 @@ export const bskyLastSeen = new AsyncResource<z.infer<typeof schema>>({
|
||||||
})),
|
})),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const post = res.feed[0].post
|
||||||
|
|
||||||
|
const postId = post.uri.match(/at:\/\/did:web:tei.su\/app\.bsky\.feed\.post\/([a-zA-Z0-9]+)/)
|
||||||
|
if (postId) {
|
||||||
return {
|
return {
|
||||||
data: res.feed[0].post,
|
data: {
|
||||||
|
source: 'bsky',
|
||||||
|
sourceLink: 'https://bsky.app/profile/did:web:tei.su',
|
||||||
|
time: new Date(post.record.createdAt).getTime(),
|
||||||
|
text: post.record.text.slice(0, 40) || '[no text]',
|
||||||
|
link: `https://bsky.app/profile/did:web:tei.su/post/${postId[1]}`,
|
||||||
|
},
|
||||||
|
expiresIn: TTL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
expiresIn: TTL,
|
expiresIn: TTL,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
80
src/backend/service/last-seen/forgejo.ts
Normal file
80
src/backend/service/last-seen/forgejo.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import { AsyncResource } from '@fuman/utils'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
import { ffetch } from '../../utils/fetch.ts'
|
||||||
|
|
||||||
|
import type { LastSeenItem } from './index.ts'
|
||||||
|
|
||||||
|
const ENDPOINT = 'https://git.stupid.fish/api/v1/users/teidesu/activities/feeds?only-performed-by=true'
|
||||||
|
const TTL = 1 * 60 * 60 * 1000 // 1 hour
|
||||||
|
const STALE_TTL = 4 * 60 * 60 * 1000 // 4 hours
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
// create_repo,rename_repo,star_repo,watch_repo,commit_repo,create_issue,create_pull_request,transfer_repo,push_tag,comment_issue,
|
||||||
|
// merge_pull_request,close_issue,reopen_issue,close_pull_request,reopen_pull_request,delete_tag,delete_branch,mirror_sync_push,
|
||||||
|
// mirror_sync_create,mirror_sync_delete,approve_pull_request,reject_pull_request,comment_pull,publish_release,pull_review_dismissed,
|
||||||
|
// pull_request_ready_for_review,auto_merge_pull_request
|
||||||
|
op_type: z.string(),
|
||||||
|
content: z.string(),
|
||||||
|
repo: z.object({
|
||||||
|
full_name: z.string(),
|
||||||
|
html_url: z.string(),
|
||||||
|
}),
|
||||||
|
created: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// {"Commits":[{"Sha1":"2ee71666823761350bd28a0db9ef9140cd3814cb","Message":"docs: fix docs config\n","AuthorEmail":"alina@tei.su","AuthorName":"alina sireneva","CommitterEmail":"alina@tei.su","CommitterName":"alina sireneva","Timestamp":"2025-01-03T22:42:30+03:00"}],"HeadCommit":{"Sha1":"2ee71666823761350bd28a0db9ef9140cd3814cb","Message":"docs: fix docs config\n","AuthorEmail":"alina@tei.su","AuthorName":"alina sireneva","CommitterEmail":"alina@tei.su","CommitterName":"alina sireneva","Timestamp":"2025-01-03T22:42:30+03:00"},"CompareURL":"teidesu/mtcute/compare/db9b083d3595e78240146e8a590fe70049259612...2ee71666823761350bd28a0db9ef9140cd3814cb","Len":1}
|
||||||
|
const CommitEventSchema = z.object({
|
||||||
|
Commits: z.array(z.object({
|
||||||
|
Message: z.string(),
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
|
function mkItem(item: z.infer<typeof schema>, text: string) {
|
||||||
|
return {
|
||||||
|
source: 'forgejo',
|
||||||
|
sourceLink: 'https://git.stupid.fish/teidesu',
|
||||||
|
time: new Date(item.created).getTime(),
|
||||||
|
text: item.repo.full_name,
|
||||||
|
link: item.repo.html_url,
|
||||||
|
suffix: `: ${text}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const forgejoLastSeen = new AsyncResource<LastSeenItem | null>({
|
||||||
|
async fetcher() {
|
||||||
|
const res = await ffetch(ENDPOINT).parsedJson(z.array(schema))
|
||||||
|
|
||||||
|
// for simplicity (and lack of proper documentation) we'll just support a few common events and return the first supported one
|
||||||
|
|
||||||
|
let result: LastSeenItem | null = null
|
||||||
|
|
||||||
|
for (const item of res) {
|
||||||
|
if (item.op_type === 'commit_repo') {
|
||||||
|
const commits = CommitEventSchema.parse(JSON.parse(item.content)).Commits
|
||||||
|
|
||||||
|
result = mkItem(
|
||||||
|
item,
|
||||||
|
commits.length === 1 && commits[0].Message.length > 0
|
||||||
|
? `${commits[0].Message.slice(0, 40)}`
|
||||||
|
: `pushed ${commits.length} commits`,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
} else if (item.op_type === 'close_pull_request') {
|
||||||
|
result = mkItem(item, 'closed pull request')
|
||||||
|
break
|
||||||
|
} else if (item.op_type === 'merge_pull_request') {
|
||||||
|
result = mkItem(item, 'merged pull request')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: result,
|
||||||
|
expiresIn: TTL,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
swr: true,
|
||||||
|
swrValidator: ({ currentFetchedAt }) => Date.now() - currentFetchedAt < STALE_TTL,
|
||||||
|
})
|
|
@ -3,6 +3,8 @@ import { z } from 'zod'
|
||||||
|
|
||||||
import { ffetch } from '../../utils/fetch.ts'
|
import { ffetch } from '../../utils/fetch.ts'
|
||||||
|
|
||||||
|
import type { LastSeenItem } from './index.ts'
|
||||||
|
|
||||||
const ENDPOINT = 'https://api.github.com/users/teidesu/events/public?per_page=1'
|
const ENDPOINT = 'https://api.github.com/users/teidesu/events/public?per_page=1'
|
||||||
const TTL = 1 * 60 * 60 * 1000 // 1 hour
|
const TTL = 1 * 60 * 60 * 1000 // 1 hour
|
||||||
const STALE_TTL = 4 * 60 * 60 * 1000 // 4 hours
|
const STALE_TTL = 4 * 60 * 60 * 1000 // 4 hours
|
||||||
|
@ -16,7 +18,7 @@ const schema = z.object({
|
||||||
created_at: z.string(),
|
created_at: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const githubLastSeen = new AsyncResource<z.infer<typeof schema>>({
|
export const githubLastSeen = new AsyncResource<LastSeenItem | null>({
|
||||||
async fetcher() {
|
async fetcher() {
|
||||||
const res = await ffetch(ENDPOINT, {
|
const res = await ffetch(ENDPOINT, {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -25,8 +27,38 @@ export const githubLastSeen = new AsyncResource<z.infer<typeof schema>>({
|
||||||
},
|
},
|
||||||
}).parsedJson(z.array(schema))
|
}).parsedJson(z.array(schema))
|
||||||
|
|
||||||
|
const data = res[0]
|
||||||
|
|
||||||
|
const eventTextMapper: Record<string, () => string> = {
|
||||||
|
CreateEvent: () => `${data.payload.ref_type} created`,
|
||||||
|
DeleteEvent: () => `${data.payload.ref_type} deleted`,
|
||||||
|
ForkEvent: () => 'forked',
|
||||||
|
GollumEvent: () => 'wiki updated',
|
||||||
|
IssueCommentEvent: () => `issue comment ${data.payload.action}`,
|
||||||
|
IssuesEvent: () => `issue ${data.payload.action}`,
|
||||||
|
PublicEvent: () => 'made public',
|
||||||
|
PullRequestEvent: () => `pr ${data.payload.action}`,
|
||||||
|
PushEvent: () => `pushed ${data.payload.distinct_size} commits`,
|
||||||
|
ReleaseEvent: () => `release ${data.payload.action}`,
|
||||||
|
WatchEvent: () => 'starred',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventTextMapper[data.type]) {
|
||||||
return {
|
return {
|
||||||
data: res[0],
|
data: {
|
||||||
|
source: 'github',
|
||||||
|
sourceLink: 'https://github.com/teidesu',
|
||||||
|
time: new Date(data.created_at).getTime(),
|
||||||
|
text: data.repo.name,
|
||||||
|
suffix: `: ${eventTextMapper[data.type]()}`,
|
||||||
|
link: `https://github.com/${data.repo.name}`,
|
||||||
|
},
|
||||||
|
expiresIn: TTL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
expiresIn: TTL,
|
expiresIn: TTL,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { bskyLastSeen } from './bsky.ts'
|
||||||
import { githubLastSeen } from './github'
|
import { githubLastSeen } from './github'
|
||||||
import { lastfm } from './lastfm'
|
import { lastfm } from './lastfm'
|
||||||
import { shikimoriLastSeen } from './shikimori'
|
import { shikimoriLastSeen } from './shikimori'
|
||||||
|
import { forgejoLastSeen } from './forgejo.ts'
|
||||||
|
|
||||||
export interface LastSeenItem {
|
export interface LastSeenItem {
|
||||||
source: string
|
source: string
|
||||||
|
@ -20,93 +21,33 @@ export async function fetchLastSeen() {
|
||||||
bskyData,
|
bskyData,
|
||||||
shikimoriData,
|
shikimoriData,
|
||||||
githubData,
|
githubData,
|
||||||
|
forgejoData,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
lastfm.get(),
|
lastfm.get(),
|
||||||
bskyLastSeen.get(),
|
bskyLastSeen.get(),
|
||||||
shikimoriLastSeen.get(),
|
shikimoriLastSeen.get(),
|
||||||
githubLastSeen.get(),
|
githubLastSeen.get(),
|
||||||
|
forgejoLastSeen.get(),
|
||||||
])
|
])
|
||||||
|
|
||||||
const res: LastSeenItem[] = []
|
const res: LastSeenItem[] = []
|
||||||
|
|
||||||
if (lastfmData) {
|
if (lastfmData) res.push(lastfmData)
|
||||||
res.push({
|
if (bskyData) res.push(bskyData)
|
||||||
source: 'last.fm',
|
if (shikimoriData) res.push(shikimoriData)
|
||||||
sourceLink: 'https://last.fm/user/teidesu',
|
|
||||||
time: Number(lastfmData.date!.uts) * 1000,
|
if (githubData && forgejoData) {
|
||||||
text: `${lastfmData.name} – ${lastfmData.artist['#text']}`,
|
// only push the last one
|
||||||
link: lastfmData.url,
|
if (forgejoData.time > githubData.time) {
|
||||||
})
|
res.push(forgejoData)
|
||||||
|
} else {
|
||||||
|
res.push(githubData)
|
||||||
|
}
|
||||||
|
} else if (githubData) {
|
||||||
|
res.push(githubData)
|
||||||
|
} else if (forgejoData) {
|
||||||
|
res.push(forgejoData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bskyData) {
|
return res
|
||||||
const postId = bskyData.uri.match(/at:\/\/did:web:tei.su\/app\.bsky\.feed\.post\/([a-zA-Z0-9]+)/)
|
|
||||||
if (postId) {
|
|
||||||
res.push({
|
|
||||||
source: 'bsky',
|
|
||||||
sourceLink: 'https://bsky.app/profile/did:web:tei.su',
|
|
||||||
time: new Date(bskyData.record.createdAt).getTime(),
|
|
||||||
text: bskyData.record.text.slice(0, 40) || '[no text]',
|
|
||||||
link: `https://bsky.app/profile/did:web:tei.su/post/${postId[1]}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shikimoriData) {
|
|
||||||
// thx morr for this fucking awesome api
|
|
||||||
|
|
||||||
const mapper: Record<string, string> = {
|
|
||||||
'Просмотрено': 'completed',
|
|
||||||
'Прочитано': 'completed',
|
|
||||||
'Добавлено в список': 'added',
|
|
||||||
'Брошено': 'dropped',
|
|
||||||
}
|
|
||||||
let event = mapper[shikimoriData.description]
|
|
||||||
|
|
||||||
if (!event && shikimoriData.description.match(/^Просмотрен.*эпизод(ов)?$/)) {
|
|
||||||
event = 'watched'
|
|
||||||
}
|
|
||||||
if (!event && shikimoriData.description.match(/^(Просмотрено|Прочитано) и оценено/)) {
|
|
||||||
event = 'completed'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event) {
|
|
||||||
res.push({
|
|
||||||
source: 'shiki',
|
|
||||||
sourceLink: 'https://shikimori.one/teidesu',
|
|
||||||
time: new Date(shikimoriData.created_at).getTime(),
|
|
||||||
text: shikimoriData.target.name,
|
|
||||||
suffix: `: ${event}`,
|
|
||||||
link: `https://shikimori.one${shikimoriData.target.url}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (githubData) {
|
|
||||||
const eventTextMapper: Record<string, () => string> = {
|
|
||||||
CreateEvent: () => `${githubData.payload.ref_type} created`,
|
|
||||||
DeleteEvent: () => `${githubData.payload.ref_type} deleted`,
|
|
||||||
ForkEvent: () => 'forked',
|
|
||||||
GollumEvent: () => 'wiki updated',
|
|
||||||
IssueCommentEvent: () => `issue comment ${githubData.payload.action}`,
|
|
||||||
IssuesEvent: () => `issue ${githubData.payload.action}`,
|
|
||||||
PublicEvent: () => 'made public',
|
|
||||||
PullRequestEvent: () => `pr ${githubData.payload.action}`,
|
|
||||||
PushEvent: () => `pushed ${githubData.payload.distinct_size} commits`,
|
|
||||||
ReleaseEvent: () => `release ${githubData.payload.action}`,
|
|
||||||
WatchEvent: () => 'starred',
|
|
||||||
}
|
|
||||||
if (eventTextMapper[githubData.type]) {
|
|
||||||
res.push({
|
|
||||||
source: 'github',
|
|
||||||
sourceLink: 'https://github.com/teidesu',
|
|
||||||
time: new Date(githubData.created_at).getTime(),
|
|
||||||
text: githubData.repo.name,
|
|
||||||
suffix: `: ${eventTextMapper[githubData.type]()}`,
|
|
||||||
link: `https://github.com/${githubData.repo.name}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.sort((a, b) => b.time - a.time)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { zodValidate } from '~/utils/zod'
|
||||||
import { env } from '~/backend/env'
|
import { env } from '~/backend/env'
|
||||||
import { ffetch } from '../../utils/fetch.ts'
|
import { ffetch } from '../../utils/fetch.ts'
|
||||||
|
|
||||||
|
import type { LastSeenItem } from './index.ts'
|
||||||
|
|
||||||
const LASTFM_TTL = 1000 * 60 * 5 // 5 minutes
|
const LASTFM_TTL = 1000 * 60 * 5 // 5 minutes
|
||||||
const LASTFM_STALE_TTL = 1000 * 60 * 60 // 1 hour
|
const LASTFM_STALE_TTL = 1000 * 60 * 60 // 1 hour
|
||||||
const LASTFM_USERNAME = 'teidesu'
|
const LASTFM_USERNAME = 'teidesu'
|
||||||
|
@ -19,7 +21,6 @@ const LastfmTrack = z.object({
|
||||||
nowplaying: z.literal('true'),
|
nowplaying: z.literal('true'),
|
||||||
}).partial().optional(),
|
}).partial().optional(),
|
||||||
})
|
})
|
||||||
export type LastfmTrack = z.infer<typeof LastfmTrack>
|
|
||||||
|
|
||||||
const ResponseSchema = z.object({
|
const ResponseSchema = z.object({
|
||||||
recenttracks: z.object({
|
recenttracks: z.object({
|
||||||
|
@ -34,7 +35,7 @@ const ResponseSchema = z.object({
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const lastfm = new AsyncResource<LastfmTrack>({
|
export const lastfm = new AsyncResource<LastSeenItem | null>({
|
||||||
async fetcher({ current }) {
|
async fetcher({ current }) {
|
||||||
const res = await ffetch('https://ws.audioscrobbler.com/2.0/', {
|
const res = await ffetch('https://ws.audioscrobbler.com/2.0/', {
|
||||||
query: {
|
query: {
|
||||||
|
@ -43,7 +44,7 @@ export const lastfm = new AsyncResource<LastfmTrack>({
|
||||||
api_key: LASTFM_TOKEN,
|
api_key: LASTFM_TOKEN,
|
||||||
format: 'json',
|
format: 'json',
|
||||||
limit: '1',
|
limit: '1',
|
||||||
from: current?.date?.uts,
|
from: current ? Math.floor(current.time / 1000) : undefined,
|
||||||
},
|
},
|
||||||
}).parsedJson(ResponseSchema)
|
}).parsedJson(ResponseSchema)
|
||||||
|
|
||||||
|
@ -55,7 +56,13 @@ export const lastfm = new AsyncResource<LastfmTrack>({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: track,
|
data: {
|
||||||
|
source: 'last.fm',
|
||||||
|
sourceLink: 'https://last.fm/user/teidesu',
|
||||||
|
time: Number(track.date!.uts) * 1000,
|
||||||
|
text: `${track.name} – ${track.artist['#text']}`,
|
||||||
|
link: track.url,
|
||||||
|
},
|
||||||
expiresIn: LASTFM_TTL,
|
expiresIn: LASTFM_TTL,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { z } from 'zod'
|
||||||
|
|
||||||
import { ffetch } from '../../utils/fetch.ts'
|
import { ffetch } from '../../utils/fetch.ts'
|
||||||
|
|
||||||
|
import type { LastSeenItem } from './index.ts'
|
||||||
|
|
||||||
const ENDPOINT = 'https://shikimori.one/api/users/698215/history?limit=1'
|
const ENDPOINT = 'https://shikimori.one/api/users/698215/history?limit=1'
|
||||||
const TTL = 3 * 60 * 60 * 1000 // 3 hours
|
const TTL = 3 * 60 * 60 * 1000 // 3 hours
|
||||||
const STALE_TTL = 8 * 60 * 60 * 1000 // 8 hours
|
const STALE_TTL = 8 * 60 * 60 * 1000 // 8 hours
|
||||||
|
@ -16,12 +18,43 @@ const schema = z.object({
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const shikimoriLastSeen = new AsyncResource<z.infer<typeof schema>>({
|
export const shikimoriLastSeen = new AsyncResource<LastSeenItem | null>({
|
||||||
async fetcher() {
|
async fetcher() {
|
||||||
const res = await ffetch(ENDPOINT).parsedJson(z.array(schema))
|
const res = (await ffetch(ENDPOINT).parsedJson(z.array(schema)))[0]
|
||||||
|
|
||||||
|
// thx morr for this fucking awesome api
|
||||||
|
|
||||||
|
const mapper: Record<string, string> = {
|
||||||
|
'Просмотрено': 'completed',
|
||||||
|
'Прочитано': 'completed',
|
||||||
|
'Добавлено в список': 'added',
|
||||||
|
'Брошено': 'dropped',
|
||||||
|
}
|
||||||
|
let event = mapper[res.description]
|
||||||
|
|
||||||
|
if (!event && res.description.match(/^Просмотрен.*эпизод(ов)?$/)) {
|
||||||
|
event = 'watched'
|
||||||
|
}
|
||||||
|
if (!event && res.description.match(/^(Просмотрено|Прочитано) и оценено/)) {
|
||||||
|
event = 'completed'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
source: 'shiki',
|
||||||
|
sourceLink: 'https://shikimori.one/teidesu',
|
||||||
|
time: new Date(res.created_at).getTime(),
|
||||||
|
text: res.target.name,
|
||||||
|
suffix: `: ${event}`,
|
||||||
|
link: `https://shikimori.one${res.target.url}`,
|
||||||
|
},
|
||||||
|
expiresIn: TTL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: res[0],
|
data: null,
|
||||||
expiresIn: TTL,
|
expiresIn: TTL,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,7 @@ import Header from './Header.astro'
|
||||||
{' '}
|
{' '}
|
||||||
{import.meta.env.VITE_BUILD_DATE}
|
{import.meta.env.VITE_BUILD_DATE}
|
||||||
{' / '}
|
{' / '}
|
||||||
<Link href="//github.com/teidesu/tei.su" target="_blank">
|
<Link href="//git.stupid.fish/teidesu/tei.su" target="_blank">
|
||||||
source code
|
source code
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue