54 lines
2.2 KiB
JavaScript
54 lines
2.2 KiB
JavaScript
import rndstr from "rndstr";
|
|
import { PromoReads, PromoNotes, Notes, Users } from "../../../models/index.js";
|
|
import { shouldHideEUsersFor } from "./generate-minor-badge-visibility-query.js";
|
|
import { readPromo } from "./read-promo.js";
|
|
const systemTags = new Set([
|
|
"adservice",
|
|
"videoservice",
|
|
"audioservice",
|
|
"imageservice",
|
|
"karaokeservice",
|
|
"lua4frozen"
|
|
]);
|
|
function isExplicitAd(note) {
|
|
return note.tags.includes("explicit") || note.user?.minorBadges?.includes("E") === true;
|
|
}
|
|
export async function injectPromo(timeline, user, preferredTag) {
|
|
// TODO: readやexpireフィルタはクエリ側でやる
|
|
const readDay = new Date().toISOString().slice(0, 10);
|
|
const reads = user ? await PromoReads.findBy({
|
|
userId: user.id,
|
|
readDay
|
|
}) : [];
|
|
let promos = await PromoNotes.find();
|
|
promos = promos.filter((n)=>n.expiresAt.getTime() > Date.now());
|
|
promos = promos.filter((n)=>n.remainingCredits > 0);
|
|
promos = promos.filter((n)=>!reads.map((r)=>r.noteId).includes(n.noteId));
|
|
if (promos.length === 0) return;
|
|
const candidates = [];
|
|
for (const promo of promos){
|
|
const note = await Notes.findOneBy({
|
|
id: promo.noteId
|
|
});
|
|
if (!note?.tags.includes("adservice")) continue;
|
|
note.user = await Users.findOneByOrFail({
|
|
id: note.userId
|
|
});
|
|
if (user && shouldHideEUsersFor(user) && isExplicitAd(note)) continue;
|
|
candidates.push(note);
|
|
}
|
|
if (candidates.length === 0) return;
|
|
const normalizedPreferredTag = preferredTag?.trim().toLowerCase().replace(/^#/, "");
|
|
const priority = normalizedPreferredTag && !systemTags.has(normalizedPreferredTag) ? candidates.filter((note)=>note.tags.includes(normalizedPreferredTag)) : [];
|
|
const pool = priority.length > 0 ? priority : candidates;
|
|
// Pick random promo
|
|
const note = pool[Math.floor(Math.random() * pool.length)];
|
|
const promo = promos.find((promo)=>promo.noteId === note.id);
|
|
note._prId_ = rndstr("a-z0-9", 8);
|
|
if (user && promo) {
|
|
await readPromo(note, promo, user);
|
|
}
|
|
// Inject promo
|
|
timeline.splice(Math.min(3, timeline.length), 0, note);
|
|
}
|