Implement featured note injection
This commit is contained in:
45
src/server/api/common/inject-featured.ts
Normal file
45
src/server/api/common/inject-featured.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import rndstr from 'rndstr';
|
||||
import { Note } from '../../../models/entities/note';
|
||||
import { User } from '../../../models/entities/user';
|
||||
import { Notes, UserProfiles } from '../../../models';
|
||||
import { generateMuteQuery } from './generate-mute-query';
|
||||
import { ensure } from '../../../prelude/ensure';
|
||||
|
||||
// TODO: リアクション、Renote、返信などをしたノートは除外する
|
||||
|
||||
export async function injectFeatured(timeline: Note[], user?: User | null) {
|
||||
if (timeline.length < 5) return;
|
||||
|
||||
if (user) {
|
||||
const profile = await UserProfiles.findOne(user.id).then(ensure);
|
||||
if (!profile.injectFeaturedNote) return;
|
||||
}
|
||||
|
||||
const max = 30;
|
||||
const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで
|
||||
|
||||
const query = Notes.createQueryBuilder('note')
|
||||
.addSelect('note.score')
|
||||
.where('note.userHost IS NULL')
|
||||
.andWhere(`note.score > 0`)
|
||||
.andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) })
|
||||
.andWhere(`note.visibility = 'public'`)
|
||||
.leftJoinAndSelect('note.user', 'user');
|
||||
|
||||
if (user) generateMuteQuery(query, user);
|
||||
|
||||
const notes = await query
|
||||
.orderBy('note.score', 'DESC')
|
||||
.take(max)
|
||||
.getMany();
|
||||
|
||||
if (notes.length === 0) return;
|
||||
|
||||
// Pick random one
|
||||
const featured = notes[Math.floor(Math.random() * notes.length)];
|
||||
|
||||
(featured as any)._featuredId_ = rndstr('a-z0-9', 8);
|
||||
|
||||
// Inject featured
|
||||
timeline.splice(3, 0, featured);
|
||||
}
|
@ -4,14 +4,14 @@ import { User } from '../../../models/entities/user';
|
||||
import { PromoReads, PromoNotes, Notes, Users } from '../../../models';
|
||||
import { ensure } from '../../../prelude/ensure';
|
||||
|
||||
export async function injectPromo(user: User, timeline: Note[]) {
|
||||
export async function injectPromo(timeline: Note[], user?: User | null) {
|
||||
if (timeline.length < 5) return;
|
||||
|
||||
// TODO: readやexpireフィルタはクエリ側でやる
|
||||
|
||||
const reads = await PromoReads.find({
|
||||
const reads = user ? await PromoReads.find({
|
||||
userId: user.id
|
||||
});
|
||||
}) : [];
|
||||
|
||||
let promos = await PromoNotes.find();
|
||||
|
||||
@ -20,15 +20,15 @@ export async function injectPromo(user: User, timeline: Note[]) {
|
||||
|
||||
if (promos.length === 0) return;
|
||||
|
||||
// Pick random promo
|
||||
const promo = promos[Math.floor(Math.random() * promos.length)];
|
||||
|
||||
// Pick random promo
|
||||
const note = await Notes.findOne(promo.noteId).then(ensure);
|
||||
|
||||
// Join
|
||||
note.user = await Users.findOne(note.userId).then(ensure);
|
||||
|
||||
(note as any)._prInjectionId_ = rndstr('a-z0-9', 8);
|
||||
(note as any)._prId_ = rndstr('a-z0-9', 8);
|
||||
|
||||
// Inject promo
|
||||
timeline.splice(3, 0, note);
|
||||
|
@ -88,7 +88,6 @@ export async function signup(username: User['username'], password: UserProfile['
|
||||
await transactionalEntityManager.save(new UserProfile({
|
||||
userId: account.id,
|
||||
autoAcceptFollowed: true,
|
||||
autoWatch: false,
|
||||
password: hash,
|
||||
}));
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { ApiError } from './error';
|
||||
import { App } from '../../models/entities/app';
|
||||
import { SchemaType } from '../../misc/schema';
|
||||
|
||||
// TODO: defaultが設定されている場合はその型も考慮する
|
||||
type Params<T extends IEndpointMeta> = {
|
||||
[P in keyof T['params']]: NonNullable<T['params']>[P]['transform'] extends Function
|
||||
? ReturnType<NonNullable<T['params']>[P]['transform']>
|
||||
|
@ -126,6 +126,10 @@ export const meta = {
|
||||
}
|
||||
},
|
||||
|
||||
injectFeaturedNote: {
|
||||
validator: $.optional.bool,
|
||||
},
|
||||
|
||||
alwaysMarkNsfw: {
|
||||
validator: $.optional.bool,
|
||||
desc: {
|
||||
@ -195,6 +199,7 @@ export default define(meta, async (ps, user, app) => {
|
||||
if (typeof ps.autoAcceptFollowed == 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed;
|
||||
if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
|
||||
if (typeof ps.autoWatch == 'boolean') profileUpdates.autoWatch = ps.autoWatch;
|
||||
if (typeof ps.injectFeaturedNote == 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
|
||||
if (typeof ps.alwaysMarkNsfw == 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
|
||||
|
||||
if (ps.avatarId) {
|
||||
|
@ -46,6 +46,7 @@ export default define(meta, async (ps, user) => {
|
||||
const query = Notes.createQueryBuilder('note')
|
||||
.addSelect('note.score')
|
||||
.where('note.userHost IS NULL')
|
||||
.andWhere(`note.score > 0`)
|
||||
.andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) })
|
||||
.andWhere(`note.visibility = 'public'`)
|
||||
.leftJoinAndSelect('note.user', 'user');
|
||||
|
@ -9,6 +9,7 @@ import { generateMuteQuery } from '../../common/generate-mute-query';
|
||||
import { activeUsersChart } from '../../../../services/chart';
|
||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||
import { injectPromo } from '../../common/inject-promo';
|
||||
import { injectFeatured } from '../../common/inject-featured';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
@ -90,7 +91,8 @@ export default define(meta, async (ps, user) => {
|
||||
|
||||
const timeline = await query.take(ps.limit!).getMany();
|
||||
|
||||
await injectPromo(user, timeline);
|
||||
await injectPromo(timeline, user);
|
||||
await injectFeatured(timeline, user);
|
||||
|
||||
process.nextTick(() => {
|
||||
if (user) {
|
||||
|
@ -11,6 +11,7 @@ import { generateMuteQuery } from '../../common/generate-mute-query';
|
||||
import { activeUsersChart } from '../../../../services/chart';
|
||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||
import { injectPromo } from '../../common/inject-promo';
|
||||
import { injectFeatured } from '../../common/inject-featured';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
@ -170,7 +171,8 @@ export default define(meta, async (ps, user) => {
|
||||
|
||||
const timeline = await query.take(ps.limit!).getMany();
|
||||
|
||||
await injectPromo(user, timeline);
|
||||
await injectPromo(timeline, user);
|
||||
await injectFeatured(timeline, user);
|
||||
|
||||
process.nextTick(() => {
|
||||
if (user) {
|
||||
|
@ -11,6 +11,7 @@ import { activeUsersChart } from '../../../../services/chart';
|
||||
import { Brackets } from 'typeorm';
|
||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||
import { injectPromo } from '../../common/inject-promo';
|
||||
import { injectFeatured } from '../../common/inject-featured';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
@ -123,7 +124,8 @@ export default define(meta, async (ps, user) => {
|
||||
|
||||
const timeline = await query.take(ps.limit!).getMany();
|
||||
|
||||
await injectPromo(user, timeline);
|
||||
await injectPromo(timeline, user);
|
||||
await injectFeatured(timeline, user);
|
||||
|
||||
process.nextTick(() => {
|
||||
if (user) {
|
||||
|
@ -9,6 +9,7 @@ import { activeUsersChart } from '../../../../services/chart';
|
||||
import { Brackets } from 'typeorm';
|
||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||
import { injectPromo } from '../../common/inject-promo';
|
||||
import { injectFeatured } from '../../common/inject-featured';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
@ -156,7 +157,8 @@ export default define(meta, async (ps, user) => {
|
||||
|
||||
const timeline = await query.take(ps.limit!).getMany();
|
||||
|
||||
await injectPromo(user, timeline);
|
||||
await injectPromo(timeline, user);
|
||||
await injectFeatured(timeline, user);
|
||||
|
||||
process.nextTick(() => {
|
||||
if (user) {
|
||||
|
Reference in New Issue
Block a user