import type { MastodonStatus } from 'wildebeest/backend/src/types/status' import { getFollowingId } from 'wildebeest/backend/src/mastodon/follow' import type { Actor } from 'wildebeest/backend/src/activitypub/actors/' import { toMastodonStatusFromRow } from './status' import { PUBLIC_GROUP } from 'wildebeest/backend/src/activitypub/activities' import type { Cache } from 'wildebeest/backend/src/cache' export async function pregenerateTimelines(domain: string, db: D1Database, cache: Cache, actor: Actor) { const timeline = await getHomeTimeline(domain, db, actor) await cache.put(actor.id + '/timeline/home', timeline) } export async function getHomeTimeline(domain: string, db: D1Database, actor: Actor): Promise> { const following = await getFollowingId(db, actor) // follow ourself to see our statuses in the our home timeline following.push(actor.id.toString()) const QUERY = ` SELECT objects.*, actors.id as actor_id, actors.cdate as actor_cdate, actors.properties as actor_properties, outbox_objects.actor_id as publisher_actor_id, (SELECT count(*) FROM actor_favourites WHERE actor_favourites.object_id=objects.id) as favourites_count, (SELECT count(*) FROM actor_reblogs WHERE actor_reblogs.object_id=objects.id) as reblogs_count, (SELECT count(*) FROM actor_replies WHERE actor_replies.in_reply_to_object_id=objects.id) as replies_count, (SELECT count(*) > 0 FROM actor_reblogs WHERE actor_reblogs.object_id=objects.id AND actor_reblogs.actor_id=?) as reblogged, (SELECT count(*) > 0 FROM actor_favourites WHERE actor_favourites.object_id=objects.id AND actor_favourites.actor_id=?) as favourited FROM outbox_objects INNER JOIN objects ON objects.id = outbox_objects.object_id INNER JOIN actors ON actors.id = outbox_objects.actor_id WHERE objects.type = 'Note' AND outbox_objects.actor_id IN (SELECT value FROM json_each(?)) AND json_extract(objects.properties, '$.inReplyTo') IS NULL AND outbox_objects.target = '${PUBLIC_GROUP}' ORDER by outbox_objects.published_date DESC LIMIT ? ` const DEFAULT_LIMIT = 20 const { success, error, results } = await db .prepare(QUERY) .bind(actor.id.toString(), actor.id.toString(), JSON.stringify(following), DEFAULT_LIMIT) .all() if (!success) { throw new Error('SQL error: ' + error) } if (!results) { return [] } const out: Array = [] for (let i = 0, len = results.length; i < len; i++) { const status = await toMastodonStatusFromRow(domain, db, results[i]) if (status !== null) { out.push(status) } } return out } export enum LocalPreference { NotSet, OnlyLocal, OnlyRemote, } function localPreferenceQuery(preference: LocalPreference): string { switch (preference) { case LocalPreference.NotSet: return '1' case LocalPreference.OnlyLocal: return 'objects.local = 1' case LocalPreference.OnlyRemote: return 'objects.local = 0' } } export async function getPublicTimeline( domain: string, db: D1Database, localPreference: LocalPreference, offset: number = 0 ): Promise> { const QUERY = ` SELECT objects.*, actors.id as actor_id, actors.cdate as actor_cdate, actors.properties as actor_properties, outbox_objects.actor_id as publisher_actor_id, (SELECT count(*) FROM actor_favourites WHERE actor_favourites.object_id=objects.id) as favourites_count, (SELECT count(*) FROM actor_reblogs WHERE actor_reblogs.object_id=objects.id) as reblogs_count, (SELECT count(*) FROM actor_replies WHERE actor_replies.in_reply_to_object_id=objects.id) as replies_count FROM outbox_objects INNER JOIN objects ON objects.id=outbox_objects.object_id INNER JOIN actors ON actors.id=outbox_objects.actor_id WHERE objects.type='Note' AND ${localPreferenceQuery(localPreference)} AND json_extract(objects.properties, '$.inReplyTo') IS NULL AND outbox_objects.target = '${PUBLIC_GROUP}' ORDER by outbox_objects.published_date DESC LIMIT ?1 OFFSET ?2 ` const DEFAULT_LIMIT = 20 const { success, error, results } = await db.prepare(QUERY).bind(DEFAULT_LIMIT, offset).all() if (!success) { throw new Error('SQL error: ' + error) } if (!results) { return [] } const out: Array = [] for (let i = 0, len = results.length; i < len; i++) { const status = await toMastodonStatusFromRow(domain, db, results[i]) if (status !== null) { out.push(status) } } return out }