Merge branch 'trending-statuses' into 'main'
Trending Posts See merge request soapbox-pub/ditto!296
This commit is contained in:
commit
493a5c9fcf
|
@ -74,7 +74,7 @@ import {
|
||||||
homeTimelineController,
|
homeTimelineController,
|
||||||
publicTimelineController,
|
publicTimelineController,
|
||||||
} from '@/controllers/api/timelines.ts';
|
} from '@/controllers/api/timelines.ts';
|
||||||
import { trendingTagsController } from '@/controllers/api/trends.ts';
|
import { trendingStatusesController, trendingTagsController } from '@/controllers/api/trends.ts';
|
||||||
import { indexController } from '@/controllers/site.ts';
|
import { indexController } from '@/controllers/site.ts';
|
||||||
import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts';
|
import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts';
|
||||||
import { nostrController } from '@/controllers/well-known/nostr.ts';
|
import { nostrController } from '@/controllers/well-known/nostr.ts';
|
||||||
|
@ -194,6 +194,7 @@ app.get('/api/v2/search', searchController);
|
||||||
|
|
||||||
app.get('/api/pleroma/frontend_configurations', frontendConfigController);
|
app.get('/api/pleroma/frontend_configurations', frontendConfigController);
|
||||||
|
|
||||||
|
app.get('/api/v1/trends/statuses', trendingStatusesController);
|
||||||
app.get('/api/v1/trends/tags', trendingTagsController);
|
app.get('/api/v1/trends/tags', trendingTagsController);
|
||||||
app.get('/api/v1/trends', trendingTagsController);
|
app.get('/api/v1/trends', trendingTagsController);
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,35 @@
|
||||||
|
import { NostrEvent } from '@nostrify/nostrify';
|
||||||
|
import { sql } from 'kysely';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { type AppController } from '@/app.ts';
|
import { type AppController } from '@/app.ts';
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
import { DittoDB } from '@/db/DittoDB.ts';
|
||||||
|
import { hydrateEvents } from '@/storages/hydrate.ts';
|
||||||
|
import { Storages } from '@/storages.ts';
|
||||||
import { Time } from '@/utils.ts';
|
import { Time } from '@/utils.ts';
|
||||||
import { stripTime } from '@/utils/time.ts';
|
import { stripTime } from '@/utils/time.ts';
|
||||||
|
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
import { TrendsWorker } from '@/workers/trends.ts';
|
import { TrendsWorker } from '@/workers/trends.ts';
|
||||||
|
|
||||||
await TrendsWorker.open('data/trends.sqlite3');
|
await TrendsWorker.open('data/trends.sqlite3');
|
||||||
|
|
||||||
const limitSchema = z.coerce.number().catch(10).transform((value) => Math.min(Math.max(value, 0), 20));
|
const limitSchema = z.coerce.number().catch(10).transform((value) => Math.min(Math.max(value, 0), 20));
|
||||||
|
|
||||||
let cache = getTrends();
|
let trendingHashtagsCache = getTrendingHashtags();
|
||||||
|
|
||||||
Deno.cron('update trends cache', { minute: { every: 15 } }, async () => {
|
Deno.cron('update trends cache', { minute: { every: 15 } }, async () => {
|
||||||
const trends = await getTrends();
|
const trends = await getTrendingHashtags();
|
||||||
cache = Promise.resolve(trends);
|
trendingHashtagsCache = Promise.resolve(trends);
|
||||||
});
|
});
|
||||||
|
|
||||||
const trendingTagsController: AppController = async (c) => {
|
const trendingTagsController: AppController = async (c) => {
|
||||||
const limit = limitSchema.parse(c.req.query('limit'));
|
const limit = limitSchema.parse(c.req.query('limit'));
|
||||||
const trends = await cache;
|
const trends = await trendingHashtagsCache;
|
||||||
return c.json(trends.slice(0, limit));
|
return c.json(trends.slice(0, limit));
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getTrends() {
|
async function getTrendingHashtags() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const yesterday = new Date(now.getTime() - Time.days(1));
|
const yesterday = new Date(now.getTime() - Time.days(1));
|
||||||
const lastWeek = new Date(now.getTime() - Time.days(7));
|
const lastWeek = new Date(now.getTime() - Time.days(7));
|
||||||
|
@ -62,4 +68,49 @@ async function getTrends() {
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
export { trendingTagsController };
|
let trendingNotesCache = getTrendingNotes();
|
||||||
|
|
||||||
|
Deno.cron('update trending notes cache', { minute: { every: 15 } }, async () => {
|
||||||
|
const events = await getTrendingNotes();
|
||||||
|
trendingNotesCache = Promise.resolve(events);
|
||||||
|
});
|
||||||
|
|
||||||
|
const trendingStatusesController: AppController = async (c) => {
|
||||||
|
const store = await Storages.db();
|
||||||
|
const limit = limitSchema.parse(c.req.query('limit'));
|
||||||
|
|
||||||
|
const events = await trendingNotesCache
|
||||||
|
.then((events) => events.slice(0, limit))
|
||||||
|
.then((events) => hydrateEvents({ events, store }));
|
||||||
|
|
||||||
|
const statuses = await Promise.all(
|
||||||
|
events.map((event) => renderStatus(event, {})),
|
||||||
|
);
|
||||||
|
|
||||||
|
return c.json(statuses.filter(Boolean));
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getTrendingNotes(): Promise<NostrEvent[]> {
|
||||||
|
const kysely = await DittoDB.getInstance();
|
||||||
|
const since = Math.floor((Date.now() - Time.days(1)) / 1000);
|
||||||
|
|
||||||
|
const rows = await kysely
|
||||||
|
.selectFrom('nostr_events')
|
||||||
|
.selectAll('nostr_events')
|
||||||
|
.innerJoin('event_stats', 'event_stats.event_id', 'nostr_events.id')
|
||||||
|
.where('nostr_events.kind', '=', 1)
|
||||||
|
.where('nostr_events.created_at', '>', since)
|
||||||
|
.orderBy(
|
||||||
|
sql`(event_stats.reposts_count * 2) + (event_stats.replies_count) + (event_stats.reactions_count)`,
|
||||||
|
'desc',
|
||||||
|
)
|
||||||
|
.limit(20)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return rows.map((row) => ({
|
||||||
|
...row,
|
||||||
|
tags: JSON.parse(row.tags),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export { trendingStatusesController, trendingTagsController };
|
||||||
|
|
Loading…
Reference in New Issue