mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-03 23:14:13 +00:00 
			
		
		
		
	リストタイムラインでミュートが貫通してしまう問題に対処 (#12534)
* ユーザリストTL系の各種動作を修正・統一 * fix * fix CHANGELOG.md * テスト追加
This commit is contained in:
		
							parent
							
								
									4de4a2e143
								
							
						
					
					
						commit
						c68d87538a
					
				
					 7 changed files with 130 additions and 6 deletions
				
			
		| 
						 | 
					@ -49,6 +49,7 @@
 | 
				
			||||||
- Fix: 招待コードが使い回せる問題を修正
 | 
					- Fix: 招待コードが使い回せる問題を修正
 | 
				
			||||||
- Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
 | 
					- Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
 | 
				
			||||||
- Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正
 | 
					- Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正
 | 
				
			||||||
 | 
					- Fix: リストタイムラインにてミュートが機能しないケースがある問題と、チャンネル投稿がストリーミングで流れてきてしまう問題を修正 #10443
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2023.11.1
 | 
					## 2023.11.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,12 +3,13 @@
 | 
				
			||||||
 * SPDX-License-Identifier: AGPL-3.0-only
 | 
					 * SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { MiNote } from '@/models/Note.js';
 | 
				
			||||||
import type { Packed } from './json-schema.js';
 | 
					import type { Packed } from './json-schema.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function isInstanceMuted(note: Packed<'Note'>, mutedInstances: Set<string>): boolean {
 | 
					export function isInstanceMuted(note: Packed<'Note'> | MiNote, mutedInstances: Set<string>): boolean {
 | 
				
			||||||
	if (mutedInstances.has(note.user.host ?? '')) return true;
 | 
						if (mutedInstances.has(note.user?.host ?? '')) return true;
 | 
				
			||||||
	if (mutedInstances.has(note.reply?.user.host ?? '')) return true;
 | 
						if (mutedInstances.has(note.reply?.user?.host ?? '')) return true;
 | 
				
			||||||
	if (mutedInstances.has(note.renote?.user.host ?? '')) return true;
 | 
						if (mutedInstances.has(note.renote?.user?.host ?? '')) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ import { QueryService } from '@/core/QueryService.js';
 | 
				
			||||||
import { MiLocalUser } from '@/models/User.js';
 | 
					import { MiLocalUser } from '@/models/User.js';
 | 
				
			||||||
import { MetaService } from '@/core/MetaService.js';
 | 
					import { MetaService } from '@/core/MetaService.js';
 | 
				
			||||||
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 | 
					import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 | 
				
			||||||
 | 
					import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 | 
				
			||||||
import { ApiError } from '../../error.js';
 | 
					import { ApiError } from '../../error.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
| 
						 | 
					@ -124,10 +125,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
				userIdsWhoMeMuting,
 | 
									userIdsWhoMeMuting,
 | 
				
			||||||
				userIdsWhoMeMutingRenotes,
 | 
									userIdsWhoMeMutingRenotes,
 | 
				
			||||||
				userIdsWhoBlockingMe,
 | 
									userIdsWhoBlockingMe,
 | 
				
			||||||
 | 
									userMutedInstances,
 | 
				
			||||||
			] = await Promise.all([
 | 
								] = await Promise.all([
 | 
				
			||||||
				this.cacheService.userMutingsCache.fetch(me.id),
 | 
									this.cacheService.userMutingsCache.fetch(me.id),
 | 
				
			||||||
				this.cacheService.renoteMutingsCache.fetch(me.id),
 | 
									this.cacheService.renoteMutingsCache.fetch(me.id),
 | 
				
			||||||
				this.cacheService.userBlockedCache.fetch(me.id),
 | 
									this.cacheService.userBlockedCache.fetch(me.id),
 | 
				
			||||||
 | 
									this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)),
 | 
				
			||||||
			]);
 | 
								]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			const timeline = await this.fanoutTimelineEndpointService.timeline({
 | 
								const timeline = await this.fanoutTimelineEndpointService.timeline({
 | 
				
			||||||
| 
						 | 
					@ -150,6 +153,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
							if (ps.withRenotes === false) return false;
 | 
												if (ps.withRenotes === false) return false;
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
										if (isInstanceMuted(note, userMutedInstances)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					return true;
 | 
										return true;
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,7 @@ export default class Connection {
 | 
				
			||||||
	public userIdsWhoMeMuting: Set<string> = new Set();
 | 
						public userIdsWhoMeMuting: Set<string> = new Set();
 | 
				
			||||||
	public userIdsWhoBlockingMe: Set<string> = new Set();
 | 
						public userIdsWhoBlockingMe: Set<string> = new Set();
 | 
				
			||||||
	public userIdsWhoMeMutingRenotes: Set<string> = new Set();
 | 
						public userIdsWhoMeMutingRenotes: Set<string> = new Set();
 | 
				
			||||||
 | 
						public userMutedInstances: Set<string> = new Set();
 | 
				
			||||||
	private fetchIntervalId: NodeJS.Timeout | null = null;
 | 
						private fetchIntervalId: NodeJS.Timeout | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(
 | 
						constructor(
 | 
				
			||||||
| 
						 | 
					@ -69,6 +70,7 @@ export default class Connection {
 | 
				
			||||||
		this.userIdsWhoMeMuting = userIdsWhoMeMuting;
 | 
							this.userIdsWhoMeMuting = userIdsWhoMeMuting;
 | 
				
			||||||
		this.userIdsWhoBlockingMe = userIdsWhoBlockingMe;
 | 
							this.userIdsWhoBlockingMe = userIdsWhoBlockingMe;
 | 
				
			||||||
		this.userIdsWhoMeMutingRenotes = userIdsWhoMeMutingRenotes;
 | 
							this.userIdsWhoMeMutingRenotes = userIdsWhoMeMutingRenotes;
 | 
				
			||||||
 | 
							this.userMutedInstances = new Set(userProfile.mutedInstances);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,6 +41,10 @@ export default abstract class Channel {
 | 
				
			||||||
		return this.connection.userIdsWhoBlockingMe;
 | 
							return this.connection.userIdsWhoBlockingMe;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected get userMutedInstances() {
 | 
				
			||||||
 | 
							return this.connection.userMutedInstances;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected get followingChannels() {
 | 
						protected get followingChannels() {
 | 
				
			||||||
		return this.connection.followingChannels;
 | 
							return this.connection.followingChannels;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,12 +5,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Inject, Injectable } from '@nestjs/common';
 | 
					import { Inject, Injectable } from '@nestjs/common';
 | 
				
			||||||
import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
 | 
					import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
 | 
				
			||||||
import type { MiUser } from '@/models/User.js';
 | 
					 | 
				
			||||||
import { isUserRelated } from '@/misc/is-user-related.js';
 | 
					import { isUserRelated } from '@/misc/is-user-related.js';
 | 
				
			||||||
import type { Packed } from '@/misc/json-schema.js';
 | 
					import type { Packed } from '@/misc/json-schema.js';
 | 
				
			||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 | 
					import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 | 
				
			||||||
import { DI } from '@/di-symbols.js';
 | 
					import { DI } from '@/di-symbols.js';
 | 
				
			||||||
import { bindThis } from '@/decorators.js';
 | 
					import { bindThis } from '@/decorators.js';
 | 
				
			||||||
 | 
					import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 | 
				
			||||||
import Channel from '../channel.js';
 | 
					import Channel from '../channel.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserListChannel extends Channel {
 | 
					class UserListChannel extends Channel {
 | 
				
			||||||
| 
						 | 
					@ -80,6 +80,9 @@ class UserListChannel extends Channel {
 | 
				
			||||||
	private async onNote(note: Packed<'Note'>) {
 | 
						private async onNote(note: Packed<'Note'>) {
 | 
				
			||||||
		const isMe = this.user!.id === note.userId;
 | 
							const isMe = this.user!.id === note.userId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// チャンネル投稿は無視する
 | 
				
			||||||
 | 
							if (note.channelId) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
 | 
							if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!Object.hasOwn(this.membershipsMap, note.userId)) return;
 | 
							if (!Object.hasOwn(this.membershipsMap, note.userId)) return;
 | 
				
			||||||
| 
						 | 
					@ -115,6 +118,9 @@ class UserListChannel extends Channel {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 流れてきたNoteがミュートしているインスタンスに関わるものだったら無視する
 | 
				
			||||||
 | 
							if (isInstanceMuted(note, this.userMutedInstances)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.connection.cacheNote(note);
 | 
							this.connection.cacheNote(note);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.send('note', note);
 | 
							this.send('note', note);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as assert from 'assert';
 | 
					import * as assert from 'assert';
 | 
				
			||||||
import { MiFollowing } from '@/models/Following.js';
 | 
					import { MiFollowing } from '@/models/Following.js';
 | 
				
			||||||
import { connectStream, signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
 | 
					import { signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
 | 
				
			||||||
import type { INestApplicationContext } from '@nestjs/common';
 | 
					import type { INestApplicationContext } from '@nestjs/common';
 | 
				
			||||||
import type * as misskey from 'misskey-js';
 | 
					import type * as misskey from 'misskey-js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,12 +34,16 @@ describe('Streaming', () => {
 | 
				
			||||||
		let ayano: misskey.entities.MeSignup;
 | 
							let ayano: misskey.entities.MeSignup;
 | 
				
			||||||
		let kyoko: misskey.entities.MeSignup;
 | 
							let kyoko: misskey.entities.MeSignup;
 | 
				
			||||||
		let chitose: misskey.entities.MeSignup;
 | 
							let chitose: misskey.entities.MeSignup;
 | 
				
			||||||
 | 
							let kanako: misskey.entities.MeSignup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Remote users
 | 
							// Remote users
 | 
				
			||||||
		let akari: misskey.entities.MeSignup;
 | 
							let akari: misskey.entities.MeSignup;
 | 
				
			||||||
		let chinatsu: misskey.entities.MeSignup;
 | 
							let chinatsu: misskey.entities.MeSignup;
 | 
				
			||||||
 | 
							let takumi: misskey.entities.MeSignup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let kyokoNote: any;
 | 
							let kyokoNote: any;
 | 
				
			||||||
 | 
							let kanakoNote: any;
 | 
				
			||||||
 | 
							let takumiNote: any;
 | 
				
			||||||
		let list: any;
 | 
							let list: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		beforeAll(async () => {
 | 
							beforeAll(async () => {
 | 
				
			||||||
| 
						 | 
					@ -50,11 +54,15 @@ describe('Streaming', () => {
 | 
				
			||||||
			ayano = await signup({ username: 'ayano' });
 | 
								ayano = await signup({ username: 'ayano' });
 | 
				
			||||||
			kyoko = await signup({ username: 'kyoko' });
 | 
								kyoko = await signup({ username: 'kyoko' });
 | 
				
			||||||
			chitose = await signup({ username: 'chitose' });
 | 
								chitose = await signup({ username: 'chitose' });
 | 
				
			||||||
 | 
								kanako = await signup({ username: 'kanako' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			akari = await signup({ username: 'akari', host: 'example.com' });
 | 
								akari = await signup({ username: 'akari', host: 'example.com' });
 | 
				
			||||||
			chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
 | 
								chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
 | 
				
			||||||
 | 
								takumi = await signup({ username: 'takumi', host: 'example.com' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			kyokoNote = await post(kyoko, { text: 'foo' });
 | 
								kyokoNote = await post(kyoko, { text: 'foo' });
 | 
				
			||||||
 | 
								kanakoNote = await post(kanako, { text: 'hoge' });
 | 
				
			||||||
 | 
								takumiNote = await post(takumi, { text: 'piyo' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Follow: ayano => kyoko
 | 
								// Follow: ayano => kyoko
 | 
				
			||||||
			await api('following/create', { userId: kyoko.id }, ayano);
 | 
								await api('following/create', { userId: kyoko.id }, ayano);
 | 
				
			||||||
| 
						 | 
					@ -62,6 +70,9 @@ describe('Streaming', () => {
 | 
				
			||||||
			// Follow: ayano => akari
 | 
								// Follow: ayano => akari
 | 
				
			||||||
			await follow(ayano, akari);
 | 
								await follow(ayano, akari);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Mute: chitose => kanako
 | 
				
			||||||
 | 
								await api('mute/create', { userId: kanako.id }, chitose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// List: chitose => ayano, kyoko
 | 
								// List: chitose => ayano, kyoko
 | 
				
			||||||
			list = await api('users/lists/create', {
 | 
								list = await api('users/lists/create', {
 | 
				
			||||||
				name: 'my list',
 | 
									name: 'my list',
 | 
				
			||||||
| 
						 | 
					@ -76,6 +87,11 @@ describe('Streaming', () => {
 | 
				
			||||||
				listId: list.id,
 | 
									listId: list.id,
 | 
				
			||||||
				userId: kyoko.id,
 | 
									userId: kyoko.id,
 | 
				
			||||||
			}, chitose);
 | 
								}, chitose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								await api('users/lists/push', {
 | 
				
			||||||
 | 
									listId: list.id,
 | 
				
			||||||
 | 
									userId: takumi.id,
 | 
				
			||||||
 | 
								}, chitose);
 | 
				
			||||||
		}, 1000 * 60 * 2);
 | 
							}, 1000 * 60 * 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		afterAll(async () => {
 | 
							afterAll(async () => {
 | 
				
			||||||
| 
						 | 
					@ -452,6 +468,96 @@ describe('Streaming', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				assert.strictEqual(fired, false);
 | 
									assert.strictEqual(fired, false);
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// #10443
 | 
				
			||||||
 | 
								test('チャンネル投稿は流れない', async () => {
 | 
				
			||||||
 | 
									// リスインしている kyoko が 任意のチャンネルに投降した時の動きを見たい
 | 
				
			||||||
 | 
									const fired = await waitFire(
 | 
				
			||||||
 | 
										chitose, 'userList',
 | 
				
			||||||
 | 
										() => api('notes/create', { text: 'foo', channelId: 'dummy' }, kyoko),
 | 
				
			||||||
 | 
										msg => msg.type === 'note' && msg.body.userId === kyoko.id,
 | 
				
			||||||
 | 
										{ listId: list.id },
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.strictEqual(fired, false);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// #10443
 | 
				
			||||||
 | 
								test('ミュートしているユーザへのリプライがリストTLに流れない', async () => {
 | 
				
			||||||
 | 
									// chitose が kanako をミュートしている状態で、リスインしている kyoko が kanako にリプライした時の動きを見たい
 | 
				
			||||||
 | 
									const fired = await waitFire(
 | 
				
			||||||
 | 
										chitose, 'userList',
 | 
				
			||||||
 | 
										() => api('notes/create', { text: 'foo', replyId: kanakoNote.id }, kyoko),
 | 
				
			||||||
 | 
										msg => msg.type === 'note' && msg.body.userId === kyoko.id,
 | 
				
			||||||
 | 
										{ listId: list.id },
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.strictEqual(fired, false);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// #10443
 | 
				
			||||||
 | 
								test('ミュートしているユーザの投稿をリノートしたときリストTLに流れない', async () => {
 | 
				
			||||||
 | 
									// chitose が kanako をミュートしている状態で、リスインしている kyoko が kanako のノートをリノートした時の動きを見たい
 | 
				
			||||||
 | 
									const fired = await waitFire(
 | 
				
			||||||
 | 
										chitose, 'userList',
 | 
				
			||||||
 | 
										() => api('notes/create', { renoteId: kanakoNote.id }, kyoko),
 | 
				
			||||||
 | 
										msg => msg.type === 'note' && msg.body.userId === kyoko.id,
 | 
				
			||||||
 | 
										{ listId: list.id },
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.strictEqual(fired, false);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// #10443
 | 
				
			||||||
 | 
								test('ミュートしているサーバのノートがリストTLに流れない', async () => {
 | 
				
			||||||
 | 
									await api('/i/update', {
 | 
				
			||||||
 | 
										mutedInstances: ['example.com'],
 | 
				
			||||||
 | 
									}, chitose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// chitose が example.com をミュートしている状態で、リスインしている takumi が ノートした時の動きを見たい
 | 
				
			||||||
 | 
									const fired = await waitFire(
 | 
				
			||||||
 | 
										chitose, 'userList',
 | 
				
			||||||
 | 
										() => api('notes/create', { text: 'foo' }, takumi),
 | 
				
			||||||
 | 
										msg => msg.type === 'note' && msg.body.userId === kyoko.id,
 | 
				
			||||||
 | 
										{ listId: list.id },
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.strictEqual(fired, false);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// #10443
 | 
				
			||||||
 | 
								test('ミュートしているサーバのノートに対するリプライがリストTLに流れない', async () => {
 | 
				
			||||||
 | 
									await api('/i/update', {
 | 
				
			||||||
 | 
										mutedInstances: ['example.com'],
 | 
				
			||||||
 | 
									}, chitose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// chitose が example.com をミュートしている状態で、リスインしている kyoko が takumi のノートにリプライした時の動きを見たい
 | 
				
			||||||
 | 
									const fired = await waitFire(
 | 
				
			||||||
 | 
										chitose, 'userList',
 | 
				
			||||||
 | 
										() => api('notes/create', { text: 'foo', replyId: takumiNote.id }, kyoko),
 | 
				
			||||||
 | 
										msg => msg.type === 'note' && msg.body.userId === kyoko.id,
 | 
				
			||||||
 | 
										{ listId: list.id },
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.strictEqual(fired, false);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// #10443
 | 
				
			||||||
 | 
								test('ミュートしているサーバのノートに対するリノートがリストTLに流れない', async () => {
 | 
				
			||||||
 | 
									await api('/i/update', {
 | 
				
			||||||
 | 
										mutedInstances: ['example.com'],
 | 
				
			||||||
 | 
									}, chitose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// chitose が example.com をミュートしている状態で、リスインしている kyoko が takumi のノートをリノートした時の動きを見たい
 | 
				
			||||||
 | 
									const fired = await waitFire(
 | 
				
			||||||
 | 
										chitose, 'userList',
 | 
				
			||||||
 | 
										() => api('notes/create', { renoteId: takumiNote.id }, kyoko),
 | 
				
			||||||
 | 
										msg => msg.type === 'note' && msg.body.userId === kyoko.id,
 | 
				
			||||||
 | 
										{ listId: list.id },
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.strictEqual(fired, false);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// XXX: QueryFailedError: duplicate key value violates unique constraint "IDX_347fec870eafea7b26c8a73bac"
 | 
							// XXX: QueryFailedError: duplicate key value violates unique constraint "IDX_347fec870eafea7b26c8a73bac"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue