Skip to content

Commit

Permalink
Merge pull request #201 from anatawa12/vmimi-relay-timeline/forks/nirila
Browse files Browse the repository at this point in the history
Upgrade vmimi relay timeline to 2024.5.0-vrtl.2
  • Loading branch information
anatawa12 committed Jun 16, 2024
2 parents f2bef73 + 90d4b00 commit 79d01cc
Show file tree
Hide file tree
Showing 21 changed files with 134 additions and 16 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG-VRTL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# CHANGELOG about VRTL

VRTLのブランチで行われた変更点をまとめています

<!-- VV Please add changelog here VV -->
- chore(backend): VRTL参加サーバーの取得に失敗したときのリトライの間隔を短く
- feat: VRTL/VSTLに連合なし投稿を含めるかを選択可能に
- もともとのVRTL/VSTLでは連合なし投稿が常に含まれていましたが、正しくVRTL/VSTLのノートを表現するために含めないようにできるようになりました
- VSTLの場合、連合なし投稿を含めないようにしてもフォローしている人の連合なし投稿は表示されます
- fix(frontend): ウィジェットでVRTL/VSTLが使用できない問題を修正
- fix(backend): 自分自身に対するリプライがwithReplies = falseなVRTL/VSTLにて含まれていない問題を修正
- feat(backend): `vmimiRelayTimelineImplemented``disableVmimiRelayTimeline` nodeinfo に追加しました
- これによりサードパーティクライアントがVRTLの有無を認知できるようになりました。
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
## Unreleased

### General
- Feat: VRTL/VSTLに連合なし投稿を含めるかを選択可能に
- もともとのVRTL/VSTLでは連合なし投稿が常に含まれていましたが、正しくVRTL/VSTLのノートを表現するために含めないようにできるようになりました
- VSTLの場合、連合なし投稿を含めないようにしてもフォローしている人の連合なし投稿は表示されます

### Client
- Fix: ウィジェットでVRTL/VSTLが使用できない問題を修正

### Server
- Enhance: `vmimiRelayTimelineImplemented``disableVmimiRelayTimeline` nodeinfo に追加しました
- これによりサードパーティクライアントがVRTLの有無を認知できるようになりました。
- Enhance: VRTL参加サーバーの取得に失敗したときのリトライの間隔を短く
- Fix: 自分自身に対するリプライがwithReplies = falseなVRTL/VSTLにて含まれていない問題を修正

## 2024.5.0-kinel.1

### General
Expand Down
4 changes: 4 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4732,6 +4732,10 @@ export interface Locale extends ILocale {
* TLに現在フォロー中の人全員の返信を含めないようにする
*/
"hideRepliesToOthersInTimelineAll": string;
/**
* TLに連合なし投稿を含める
*/
"showLocalOnlyInTimeline": string;
/**
* この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めるようにしますか?
*/
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,7 @@ showRepliesToOthersInTimeline: "TLに他の人への返信を含める"
hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない"
showRepliesToOthersInTimelineAll: "TLに現在フォロー中の人全員の返信を含めるようにする"
hideRepliesToOthersInTimelineAll: "TLに現在フォロー中の人全員の返信を含めないようにする"
showLocalOnlyInTimeline: "TLに連合なし投稿を含める"
confirmShowRepliesAll: "この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めるようにしますか?"
confirmHideRepliesAll: "この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めないようにしますか?"
externalServices: "外部サービス"
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/core/FanoutTimelineService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export type FanoutTimelineName =
| 'vmimiRelayTimeline' // replies are not included
| 'vmimiRelayTimelineWithFiles' // only non-reply notes with files are included
| 'vmimiRelayTimelineWithReplies' // only replies are included
| `vmimiRelayTimelineWithReplyTo:${string}` // Only replies to specific local user are included. Parameter is reply user id.

@Injectable()
export class FanoutTimelineService {
Expand Down
7 changes: 5 additions & 2 deletions packages/backend/src/core/NoteCreateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -972,8 +972,11 @@ export class NoteCreateService implements OnApplicationShutdown {
this.fanoutTimelineService.push(`localTimelineWithReplyTo:${note.replyUserId}`, note.id, 300 / 10, r);
}
}
if (note.visibility === 'public' && this.vmimiRelayTimelineService.isRelayedInstance(note.userHost)) {
if (note.visibility === 'public' && this.vmimiRelayTimelineService.isRelayedInstance(note.userHost) && !note.localOnly) {
this.fanoutTimelineService.push('vmimiRelayTimelineWithReplies', note.id, meta.vmimiRelayTimelineCacheMax, r);
if (note.replyUserHost == null) {
this.fanoutTimelineService.push(`vmimiRelayTimelineWithReplyTo:${note.replyUserId}`, note.id, meta.vmimiRelayTimelineCacheMax / 10, r);
}
}
} else {
this.fanoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
Expand All @@ -987,7 +990,7 @@ export class NoteCreateService implements OnApplicationShutdown {
this.fanoutTimelineService.push('localTimelineWithFiles', note.id, 500, r);
}
}
if (note.visibility === 'public' && this.vmimiRelayTimelineService.isRelayedInstance(note.userHost)) {
if (note.visibility === 'public' && this.vmimiRelayTimelineService.isRelayedInstance(note.userHost) && !note.localOnly) {
this.fanoutTimelineService.push('vmimiRelayTimeline', note.id, meta.vmimiRelayTimelineCacheMax, r);
if (note.fileIds.length > 0) {
this.fanoutTimelineService.push('vmimiRelayTimelineWithFiles', note.id, meta.vmimiRelayTimelineCacheMax / 2, r);
Expand Down
13 changes: 9 additions & 4 deletions packages/backend/src/core/VmimiRelayTimelineService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import type Logger from '@/logger.js';
type VmimiInstanceList = { Url: string; }[];

// one day
const UpdateInterval = 1000 * 60 * 60 * 24;
const RetryInterval = 1000 * 60 * 60 * 6;
const UpdateInterval = 1000 * 60 * 60 * 24; // 24 hours = 1 day
const MinRetryInterval = 1000 * 60; // one minutes
const MaxRetryInterval = 1000 * 60 * 60 * 6; // 6 hours

@Injectable()
export class VmimiRelayTimelineService {
instanceHosts: Set<string>;
instanceHostsArray: string[];
nextUpdate: number;
nextRetryInterval: number;
updatePromise: Promise<void> | null;
private logger: Logger;

Expand All @@ -32,6 +34,7 @@ export class VmimiRelayTimelineService {
this.instanceHosts = new Set<string>([]);
this.instanceHostsArray = [];
this.nextUpdate = 0;
this.nextRetryInterval = MinRetryInterval;
this.updatePromise = null;

this.logger = this.loggerService.getLogger('vmimi');
Expand All @@ -55,10 +58,12 @@ export class VmimiRelayTimelineService {
this.instanceHosts = new Set<string>(this.instanceHostsArray);
this.nextUpdate = Date.now() + UpdateInterval;
this.logger.info(`Got instance list: ${this.instanceHostsArray}`);
this.nextRetryInterval = MinRetryInterval;
} catch (e) {
this.logger.error('Failed to update instance list', e as any);
this.nextUpdate = Date.now() + RetryInterval;
setTimeout(() => this.checkForUpdateInstanceList(), RetryInterval + 5);
this.nextUpdate = Date.now() + this.nextRetryInterval;
setTimeout(() => this.checkForUpdateInstanceList(), this.nextRetryInterval + 5);
this.nextRetryInterval = Math.min(this.nextRetryInterval * 2, MaxRetryInterval);
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/server/NodeinfoServerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ export class NodeinfoServerService {
disableRegistration: meta.disableRegistration,
disableLocalTimeline: !basePolicies.ltlAvailable,
disableGlobalTimeline: !basePolicies.gtlAvailable,
vmimiRelayTimelineImplemented: true,
disableVmimiRelayTimeline: !basePolicies.vrtlAvailable,
emailRequiredForSignup: meta.emailRequiredForSignup,
enableHcaptcha: meta.enableHcaptcha,
enableRecaptcha: meta.enableRecaptcha,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const paramDef = {
withFiles: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withLocalOnly: { type: 'boolean', default: true },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
allowPartial: { type: 'boolean', default: true }, // this timeline is new so true by default
sinceId: { type: 'string', format: 'misskey:id' },
Expand Down Expand Up @@ -108,6 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
limit: ps.limit,
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withLocalOnly: ps.withLocalOnly,
}, me);

process.nextTick(() => {
Expand All @@ -124,17 +126,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
`homeTimelineWithFiles:${me.id}`,
'vmimiRelayTimelineWithFiles',
];
if (ps.withLocalOnly) timelineConfig = [...timelineConfig, 'localTimelineWithFiles'];
} else if (ps.withReplies) {
timelineConfig = [
`homeTimeline:${me.id}`,
'vmimiRelayTimeline',
'vmimiRelayTimelineWithReplies',
];
if (ps.withLocalOnly) timelineConfig = [...timelineConfig, 'localTimeline', 'localTimelineWithReplies'];
} else {
timelineConfig = [
`homeTimeline:${me.id}`,
'vmimiRelayTimeline',
`vmimiRelayTimelineWithReplyTo:${me.id}`,
];
if (ps.withLocalOnly) timelineConfig = [...timelineConfig, 'localTimeline', `localTimelineWithReplyTo:${me.id}`];
}

const [
Expand Down Expand Up @@ -166,6 +172,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
limit,
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withLocalOnly: ps.withLocalOnly,
}, me),
});

Expand All @@ -183,6 +190,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
limit: number,
withFiles: boolean,
withReplies: boolean,
withLocalOnly: boolean,
}, me: MiLocalUser) {
const followees = await this.userFollowingService.getFollowees(me.id);
const followingChannels = await this.channelFollowingsRepository.find({
Expand All @@ -199,6 +207,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
qb.where('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds });
qb.orWhere(new Brackets(qb => {
qb.where('note.visibility = \'public\'');
if (!ps.withLocalOnly) qb.andWhere('note.localOnly = FALSE');
qb.andWhere(new Brackets(qb => {
qb.where('note.userHost IS NULL');
if (vmimiRelayInstances.length !== 0) {
Expand All @@ -210,6 +219,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
qb.where('note.userId = :meId', { meId: me.id });
qb.orWhere(new Brackets(qb => {
qb.where('note.visibility = \'public\'');
if (!ps.withLocalOnly) qb.andWhere('note.localOnly = FALSE');
qb.andWhere(new Brackets(qb => {
qb.where('note.userHost IS NULL');
if (vmimiRelayInstances.length !== 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const paramDef = {
withFiles: { type: 'boolean', default: false },
withRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withLocalOnly: { type: 'boolean', default: true },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
allowPartial: { type: 'boolean', default: true }, // this timeline is new so true by default
sinceId: { type: 'string', format: 'misskey:id' },
Expand Down Expand Up @@ -98,6 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withReplies: ps.withReplies,
withLocalOnly: ps.withLocalOnly,
}, me);

process.nextTick(() => {
Expand All @@ -117,9 +119,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
me,
useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
redisTimelines:
ps.withFiles ? ['vmimiRelayTimelineWithFiles']
: ps.withReplies ? ['vmimiRelayTimeline', 'vmimiRelayTimelineWithReplies']
: ['vmimiRelayTimeline'],
ps.withFiles ? ['vmimiRelayTimelineWithFiles', ...(ps.withLocalOnly ? ['localTimelineWithFiles'] as const : [])]
: ps.withReplies ? ['vmimiRelayTimeline', 'vmimiRelayTimelineWithReplies', ...(ps.withLocalOnly ? ['localTimeline', 'localTimelineWithReplies'] as const : [])]
: me ? ['vmimiRelayTimeline', `vmimiRelayTimelineWithReplyTo:${me.id}`, ...(ps.withLocalOnly ? ['localTimeline', `localTimelineWithReplyTo:${me.id}`] as const : [])]
: ['vmimiRelayTimeline', ...(ps.withLocalOnly ? ['localTimeline'] as const : [])],
alwaysIncludeMyNotes: true,
excludePureRenotes: !ps.withRenotes,
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
Expand All @@ -129,6 +132,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withReplies: ps.withReplies,
withLocalOnly: ps.withLocalOnly,
}, me),
});

Expand All @@ -149,6 +153,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: boolean,
withRenotes: boolean,
withReplies: boolean,
withLocalOnly: boolean,
}, me: MiLocalUser | null) {
//#region Construct query
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
Expand All @@ -168,6 +173,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
}));

if (!ps.withLocalOnly) {
query.andWhere('note.localOnly = FALSE');
}

if (!ps.withReplies) {
query.andWhere(new Brackets(qb => {
qb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class VmimiRelayHybridTimelineChannel extends Channel {
private withRenotes: boolean;
private withReplies: boolean;
private withFiles: boolean;
private withLocalOnly: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -44,6 +45,7 @@ class VmimiRelayHybridTimelineChannel extends Channel {
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
this.withLocalOnly = params.withLocalOnly ?? true;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand All @@ -62,7 +64,7 @@ class VmimiRelayHybridTimelineChannel extends Channel {
if (!(
(note.channelId == null && isMe) ||
(note.channelId == null && Object.hasOwn(this.following, note.userId)) ||
(note.channelId == null && (this.vmimiRelayTimelineService.isRelayedInstance(note.user.host) && note.visibility === 'public')) ||
(note.channelId == null && (this.vmimiRelayTimelineService.isRelayedInstance(note.user.host) && note.visibility === 'public') && (this.withLocalOnly || !note.localOnly)) ||
(note.channelId != null && this.followingChannels.has(note.channelId))
)) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class VmimiRelayTimelineChannel extends Channel {
private withRenotes: boolean;
private withReplies: boolean;
private withFiles: boolean;
private withLocalOnly: boolean;

constructor(
private metaService: MetaService,
Expand All @@ -41,6 +42,7 @@ class VmimiRelayTimelineChannel extends Channel {
this.withRenotes = params.withRenotes ?? true;
this.withReplies = params.withReplies ?? false;
this.withFiles = params.withFiles ?? false;
this.withLocalOnly = params.withLocalOnly ?? true;

// Subscribe events
this.subscriber.on('notesStream', this.onNote);
Expand All @@ -51,6 +53,7 @@ class VmimiRelayTimelineChannel extends Channel {
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;

if (!this.vmimiRelayTimelineService.isRelayedInstance(note.user.host ?? null)) return;
if (!this.withLocalOnly && note.localOnly) return;
if (note.visibility !== 'public') return;
if (note.channelId != null) return;

Expand Down
7 changes: 7 additions & 0 deletions packages/frontend/src/components/MkTimeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ const props = withDefaults(defineProps<{
withRenotes?: boolean;
withReplies?: boolean;
onlyFiles?: boolean;
withLocalOnly?: boolean;
}>(), {
withRenotes: true,
withReplies: true,
onlyFiles: false,
withLocalOnly: true,
});
const emit = defineEmits<{
Expand All @@ -57,6 +59,7 @@ type TimelineQueryType = {
withRenotes?: boolean,
withReplies?: boolean,
withFiles?: boolean,
withLocalOnly?: boolean,
visibility?: string,
listId?: string,
channelId?: string,
Expand Down Expand Up @@ -126,12 +129,14 @@ function connectChannel() {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
withReplies: props.withReplies,
withLocalOnly: props.withLocalOnly,
});
} else if (props.src === 'vmimi-relay-social') {
connection = stream.useChannel('vmimiRelayHybridTimeline', {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
withReplies: props.withReplies,
withLocalOnly: props.withLocalOnly,
});
} else if (props.src === 'mentions') {
connection = stream.useChannel('main');
Expand Down Expand Up @@ -211,13 +216,15 @@ function updatePaginationQuery() {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
withReplies: props.withReplies,
withLocalOnly: props.withLocalOnly,
};
} else if (props.src === 'vmimi-relay-social') {
endpoint = 'notes/vmimi-relay-hybrid-timeline';
query = {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
withReplies: props.withReplies,
withLocalOnly: props.withLocalOnly,
};
} else if (props.src === 'mentions') {
endpoint = 'notes/mentions';
Expand Down
Loading

0 comments on commit 79d01cc

Please sign in to comment.