A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at feat/feed-generator 100 lines 2.7 kB view raw
1import type { Context } from "../context.ts"; 2import { eq } from "drizzle-orm"; 3import { Effect, Match, pipe } from "effect"; 4import tables from "../schema/mod.ts"; 5 6export default function (ctx: Context, did?: string) { 7 return Effect.runPromise( 8 pipe( 9 retrieve({ 10 ctx, 11 params: { 12 did, 13 }, 14 }), 15 Effect.flatMap(presentation), 16 Effect.retry({ times: 3 }), 17 Effect.timeout("120 seconds"), 18 Effect.catchAll((error) => 19 Effect.fail(new Error(`Failed to retrieve scrobbles chart: ${error}`)), 20 ), 21 ), 22 ); 23} 24 25export type Params = { 26 did?: string; 27 artisturi?: string; 28 albumuri?: string; 29 songuri?: string; 30}; 31 32const retrieve = ({ params, ctx }: { params: Params; ctx: Context }) => { 33 return Effect.tryPromise({ 34 try: () => { 35 const match = Match.type<Params>().pipe( 36 Match.when({ did: (did) => !!did }, ({ did }) => 37 ctx.analytics.post("library.getScrobblesPerDay", { 38 user_did: did, 39 }), 40 ), 41 Match.when({ artisturi: (uri) => !!uri }, ({ artisturi }) => 42 ctx.analytics.post("library.getArtistScrobbles", { 43 artist_id: artisturi, 44 }), 45 ), 46 Match.when({ albumuri: (uri) => !!uri }, ({ albumuri }) => 47 ctx.analytics.post("library.getAlbumScrobbles", { 48 album_id: albumuri, 49 }), 50 ), 51 Match.when( 52 { songuri: (uri) => !!uri && uri.includes("app.rocksky.scrobble") }, 53 ({ songuri }) => 54 ctx.db 55 .select() 56 .from(tables.scrobbles) 57 .leftJoin( 58 tables.tracks, 59 eq(tables.scrobbles.trackId, tables.tracks.id), 60 ) 61 .where(eq(tables.scrobbles.uri, songuri)) 62 .execute() 63 .then(([row]) => row?.tracks?.uri) 64 .then((uri) => 65 ctx.analytics.post("library.getTrackScrobbles", { 66 track_id: uri, 67 }), 68 ), 69 ), 70 Match.when( 71 { songuri: (uri) => !!uri && !uri.includes("app.rocksky.scrobble") }, 72 ({ songuri }) => 73 ctx.analytics.post("library.getTrackScrobbles", { 74 track_id: songuri, 75 }), 76 ), 77 Match.orElse(() => 78 ctx.analytics.post("library.getScrobblesPerDay", {}), 79 ), 80 ); 81 82 return match(params); 83 }, 84 catch: (error) => new Error(`Failed to retrieve scrobbles chart: ${error}`), 85 }); 86}; 87 88interface ChartsView { 89 scrobbles: any; 90} 91 92const presentation = ({ 93 data, 94}: { 95 data: any; 96}): Effect.Effect<ChartsView, never> => { 97 return Effect.sync(() => ({ 98 scrobbles: data, 99 })); 100};