fix(backend): RBTの修正 (#14621)
* fix(backend): 絵文字の変換処理が不十分なのを修正 * enhance: リアクションバッファリングが無効になったら即bakeするように * attempt to fix test * fix
This commit is contained in:
parent
1d8bfe4f1c
commit
6a1a2bef43
|
@ -126,8 +126,8 @@ const $meta: Provider = {
|
||||||
const { type, body } = obj.message as GlobalEvents['internal']['payload'];
|
const { type, body } = obj.message as GlobalEvents['internal']['payload'];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'metaUpdated': {
|
case 'metaUpdated': {
|
||||||
for (const key in body) {
|
for (const key in body.after) {
|
||||||
(meta as any)[key] = (body as any)[key];
|
(meta as any)[key] = (body.after as any)[key];
|
||||||
}
|
}
|
||||||
meta.proxyAccount = null; // joinなカラムは通常取ってこないので
|
meta.proxyAccount = null; // joinなカラムは通常取ってこないので
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -241,7 +241,7 @@ export interface InternalEventTypes {
|
||||||
avatarDecorationCreated: MiAvatarDecoration;
|
avatarDecorationCreated: MiAvatarDecoration;
|
||||||
avatarDecorationDeleted: MiAvatarDecoration;
|
avatarDecorationDeleted: MiAvatarDecoration;
|
||||||
avatarDecorationUpdated: MiAvatarDecoration;
|
avatarDecorationUpdated: MiAvatarDecoration;
|
||||||
metaUpdated: MiMeta;
|
metaUpdated: { before?: MiMeta; after: MiMeta; };
|
||||||
followChannel: { userId: MiUser['id']; channelId: MiChannel['id']; };
|
followChannel: { userId: MiUser['id']; channelId: MiChannel['id']; };
|
||||||
unfollowChannel: { userId: MiUser['id']; channelId: MiChannel['id']; };
|
unfollowChannel: { userId: MiUser['id']; channelId: MiChannel['id']; };
|
||||||
updateUserProfile: MiUserProfile;
|
updateUserProfile: MiUserProfile;
|
||||||
|
|
|
@ -52,7 +52,7 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'metaUpdated': {
|
case 'metaUpdated': {
|
||||||
this.cache = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
|
this.cache = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
|
||||||
...body,
|
...(body.after),
|
||||||
proxyAccount: null, // joinなカラムは通常取ってこないので
|
proxyAccount: null, // joinなカラムは通常取ってこないので
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
@ -141,7 +141,7 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.globalEventService.publishInternalEvent('metaUpdated', updated);
|
this.globalEventService.publishInternalEvent('metaUpdated', { before, after: updated });
|
||||||
|
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,10 +337,22 @@ export class ReactionService {
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* - 文字列タイプのレガシーな形式のリアクションを現在の形式に変換する
|
||||||
|
* - ローカルのリアクションのホストを `@.` にする(`decodeReaction()`の効果)
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public convertLegacyReaction(reaction: string): string {
|
||||||
|
reaction = this.decodeReaction(reaction).reaction;
|
||||||
|
if (Object.keys(legacies).includes(reaction)) return legacies[reaction];
|
||||||
|
return reaction;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: 廃止
|
// TODO: 廃止
|
||||||
/**
|
/**
|
||||||
* 文字列タイプのレガシーな形式のリアクションを現在の形式に変換しつつ、
|
* - 文字列タイプのレガシーな形式のリアクションを現在の形式に変換する
|
||||||
* データベース上には存在する「0個のリアクションがついている」という情報を削除する。
|
* - ローカルのリアクションのホストを `@.` にする(`decodeReaction()`の効果)
|
||||||
|
* - データベース上には存在する「0個のリアクションがついている」という情報を削除する
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public convertLegacyReactions(reactions: MiNote['reactions']): MiNote['reactions'] {
|
public convertLegacyReactions(reactions: MiNote['reactions']): MiNote['reactions'] {
|
||||||
|
@ -353,10 +365,7 @@ export class ReactionService {
|
||||||
return count > 0;
|
return count > 0;
|
||||||
})
|
})
|
||||||
.map(([reaction, count]) => {
|
.map(([reaction, count]) => {
|
||||||
// unchecked indexed access
|
const key = this.convertLegacyReaction(reaction);
|
||||||
const convertedReaction = legacies[reaction] as string | undefined;
|
|
||||||
|
|
||||||
const key = this.decodeReaction(convertedReaction ?? reaction).reaction;
|
|
||||||
|
|
||||||
return [key, count] as const;
|
return [key, count] as const;
|
||||||
})
|
})
|
||||||
|
@ -411,11 +420,4 @@ export class ReactionService {
|
||||||
host: undefined,
|
host: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public convertLegacyReaction(reaction: string): string {
|
|
||||||
reaction = this.decodeReaction(reaction).reaction;
|
|
||||||
if (Object.keys(legacies).includes(reaction)) return legacies[reaction];
|
|
||||||
return reaction;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,22 +11,48 @@ import { bindThis } from '@/decorators.js';
|
||||||
import type { MiUser, NotesRepository } from '@/models/_.js';
|
import type { MiUser, NotesRepository } from '@/models/_.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { PER_NOTE_REACTION_USER_PAIR_CACHE_MAX } from '@/const.js';
|
import { PER_NOTE_REACTION_USER_PAIR_CACHE_MAX } from '@/const.js';
|
||||||
|
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
const REDIS_DELTA_PREFIX = 'reactionsBufferDeltas';
|
const REDIS_DELTA_PREFIX = 'reactionsBufferDeltas';
|
||||||
const REDIS_PAIR_PREFIX = 'reactionsBufferPairs';
|
const REDIS_PAIR_PREFIX = 'reactionsBufferPairs';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ReactionsBufferingService {
|
export class ReactionsBufferingService implements OnApplicationShutdown {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.config)
|
@Inject(DI.config)
|
||||||
private config: Config,
|
private config: Config,
|
||||||
|
|
||||||
|
@Inject(DI.redisForSub)
|
||||||
|
private redisForSub: Redis.Redis,
|
||||||
|
|
||||||
@Inject(DI.redisForReactions)
|
@Inject(DI.redisForReactions)
|
||||||
private redisForReactions: Redis.Redis, // TODO: 専用のRedisインスタンスにする
|
private redisForReactions: Redis.Redis, // TODO: 専用のRedisインスタンスにする
|
||||||
|
|
||||||
@Inject(DI.notesRepository)
|
@Inject(DI.notesRepository)
|
||||||
private notesRepository: NotesRepository,
|
private notesRepository: NotesRepository,
|
||||||
) {
|
) {
|
||||||
|
this.redisForSub.on('message', this.onMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async onMessage(_: string, data: string) {
|
||||||
|
const obj = JSON.parse(data);
|
||||||
|
|
||||||
|
if (obj.channel === 'internal') {
|
||||||
|
const { type, body } = obj.message as GlobalEvents['internal']['payload'];
|
||||||
|
switch (type) {
|
||||||
|
case 'metaUpdated': {
|
||||||
|
// リアクションバッファリングが有効→無効になったら即bake
|
||||||
|
if (body.before != null && body.before.enableReactionsBuffering && !body.after.enableReactionsBuffering) {
|
||||||
|
this.bake();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -159,4 +185,27 @@ export class ReactionsBufferingService {
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public mergeReactions(src: MiNote['reactions'], delta: Record<string, number>): MiNote['reactions'] {
|
||||||
|
const reactions = { ...src };
|
||||||
|
for (const [name, count] of Object.entries(delta)) {
|
||||||
|
if (reactions[name] != null) {
|
||||||
|
reactions[name] += count;
|
||||||
|
} else {
|
||||||
|
reactions[name] = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public dispose(): void {
|
||||||
|
this.redisForSub.off('message', this.onMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public onApplicationShutdown(signal?: string | undefined): void {
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,25 +16,12 @@ import { bindThis } from '@/decorators.js';
|
||||||
import { DebounceLoader } from '@/misc/loader.js';
|
import { DebounceLoader } from '@/misc/loader.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
|
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
|
||||||
import type { OnModuleInit } from '@nestjs/common';
|
import type { OnModuleInit } from '@nestjs/common';
|
||||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
||||||
import type { ReactionService } from '../ReactionService.js';
|
import type { ReactionService } from '../ReactionService.js';
|
||||||
import type { UserEntityService } from './UserEntityService.js';
|
import type { UserEntityService } from './UserEntityService.js';
|
||||||
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
||||||
|
|
||||||
function mergeReactions(src: Record<string, number>, delta: Record<string, number>) {
|
|
||||||
const reactions = { ...src };
|
|
||||||
for (const [name, count] of Object.entries(delta)) {
|
|
||||||
if (reactions[name] != null) {
|
|
||||||
reactions[name] += count;
|
|
||||||
} else {
|
|
||||||
reactions[name] = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NoteEntityService implements OnModuleInit {
|
export class NoteEntityService implements OnModuleInit {
|
||||||
private userEntityService: UserEntityService;
|
private userEntityService: UserEntityService;
|
||||||
|
@ -329,12 +316,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
: this.meta.enableReactionsBuffering
|
: this.meta.enableReactionsBuffering
|
||||||
? await this.reactionsBufferingService.get(note.id)
|
? await this.reactionsBufferingService.get(note.id)
|
||||||
: { deltas: {}, pairs: [] };
|
: { deltas: {}, pairs: [] };
|
||||||
const reactions = mergeReactions(this.reactionService.convertLegacyReactions(note.reactions), bufferedReactions.deltas ?? {});
|
const reactions = this.reactionService.convertLegacyReactions(this.reactionsBufferingService.mergeReactions(note.reactions, bufferedReactions.deltas ?? {}));
|
||||||
for (const [name, count] of Object.entries(reactions)) {
|
|
||||||
if (count <= 0) {
|
|
||||||
delete reactions[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const reactionAndUserPairCache = note.reactionAndUserPairCache.concat(bufferedReactions.pairs.map(x => x.join('/')));
|
const reactionAndUserPairCache = note.reactionAndUserPairCache.concat(bufferedReactions.pairs.map(x => x.join('/')));
|
||||||
|
|
||||||
|
@ -451,7 +433,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
if (note.renote && (note.text == null && note.fileIds.length === 0)) { // pure renote
|
if (note.renote && (note.text == null && note.fileIds.length === 0)) { // pure renote
|
||||||
const reactionsCount = Object.values(mergeReactions(note.renote.reactions, bufferedReactions?.get(note.renote.id)?.deltas ?? {})).reduce((a, b) => a + b, 0);
|
const reactionsCount = Object.values(this.reactionsBufferingService.mergeReactions(note.renote.reactions, bufferedReactions?.get(note.renote.id)?.deltas ?? {})).reduce((a, b) => a + b, 0);
|
||||||
if (reactionsCount === 0) {
|
if (reactionsCount === 0) {
|
||||||
myReactionsMap.set(note.renote.id, null);
|
myReactionsMap.set(note.renote.id, null);
|
||||||
} else if (reactionsCount <= note.renote.reactionAndUserPairCache.length + (bufferedReactions?.get(note.renote.id)?.pairs.length ?? 0)) {
|
} else if (reactionsCount <= note.renote.reactionAndUserPairCache.length + (bufferedReactions?.get(note.renote.id)?.pairs.length ?? 0)) {
|
||||||
|
@ -467,7 +449,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (note.id < oldId) {
|
if (note.id < oldId) {
|
||||||
const reactionsCount = Object.values(mergeReactions(note.reactions, bufferedReactions?.get(note.id)?.deltas ?? {})).reduce((a, b) => a + b, 0);
|
const reactionsCount = Object.values(this.reactionsBufferingService.mergeReactions(note.reactions, bufferedReactions?.get(note.id)?.deltas ?? {})).reduce((a, b) => a + b, 0);
|
||||||
if (reactionsCount === 0) {
|
if (reactionsCount === 0) {
|
||||||
myReactionsMap.set(note.id, null);
|
myReactionsMap.set(note.id, null);
|
||||||
} else if (reactionsCount <= note.reactionAndUserPairCache.length + (bufferedReactions?.get(note.id)?.pairs.length ?? 0)) {
|
} else if (reactionsCount <= note.reactionAndUserPairCache.length + (bufferedReactions?.get(note.id)?.pairs.length ?? 0)) {
|
||||||
|
|
Loading…
Reference in New Issue