mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-30 04:23:08 +02:00
fix(backend): リノート時のHTLへのストリーミングの意図しない挙動を修正 (#13425)
* fix(backend): リノート時のストリーミングの意図しない挙動を修正 * Update CHANGELOG.md * fix: 不要な返り値 * fix: 不適切な条件分岐を修正 * test(backend): add htl tests --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
parent
b7d9d16201
commit
664aeb3ced
4 changed files with 75 additions and 8 deletions
|
@ -30,6 +30,8 @@
|
||||||
- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
|
- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
|
||||||
- エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
|
- エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
|
||||||
- Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
|
- Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
|
||||||
|
- Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
|
||||||
|
- Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正
|
||||||
- エンドポイント`admin/emoji/update`の各種修正
|
- エンドポイント`admin/emoji/update`の各種修正
|
||||||
- 必須パラメータを`id`または`name`のいずれかのみに
|
- 必須パラメータを`id`または`name`のいずれかのみに
|
||||||
- `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
|
- `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
|
||||||
|
|
|
@ -71,7 +71,15 @@ class HomeTimelineChannel extends Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
|
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||||
|
if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && note.poll == null) {
|
||||||
|
if (!this.withRenotes) return;
|
||||||
|
if (note.renote.reply) {
|
||||||
|
const reply = note.renote.reply;
|
||||||
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||||
|
if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||||
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
|
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
|
||||||
|
|
|
@ -40,9 +40,9 @@ describe('Streaming', () => {
|
||||||
let chinatsu: misskey.entities.SignupResponse;
|
let chinatsu: misskey.entities.SignupResponse;
|
||||||
let takumi: misskey.entities.SignupResponse;
|
let takumi: misskey.entities.SignupResponse;
|
||||||
|
|
||||||
let kyokoNote: any;
|
let kyokoNote: misskey.entities.Note;
|
||||||
let kanakoNote: any;
|
let kanakoNote: misskey.entities.Note;
|
||||||
let takumiNote: any;
|
let takumiNote: misskey.entities.Note;
|
||||||
let list: any;
|
let list: any;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
@ -68,6 +68,9 @@ describe('Streaming', () => {
|
||||||
// Follow: ayano => akari
|
// Follow: ayano => akari
|
||||||
await follow(ayano, akari);
|
await follow(ayano, akari);
|
||||||
|
|
||||||
|
// Follow: kyoko => chitose
|
||||||
|
await api('following/create', { userId: chitose.id }, kyoko);
|
||||||
|
|
||||||
// Mute: chitose => kanako
|
// Mute: chitose => kanako
|
||||||
await api('mute/create', { userId: kanako.id }, chitose);
|
await api('mute/create', { userId: kanako.id }, chitose);
|
||||||
|
|
||||||
|
@ -170,7 +173,28 @@ describe('Streaming', () => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
test('フォローしているユーザーのフォローしていないユーザーの visibility: followers な投稿への返信が流れない', async () => {
|
test('フォローしているユーザーのフォローしていないユーザーの visibility: followers な投稿への返信が流れない', async () => {
|
||||||
// TODO
|
const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' });
|
||||||
|
|
||||||
|
const fired = await waitFire(
|
||||||
|
ayano, 'homeTimeline', // ayano:home
|
||||||
|
() => api('notes/create', { text: 'reply to chitose\'s followers-only post', replyId: chitoseNote.id }, kyoko), // kyoko's reply to chitose's followers-only post
|
||||||
|
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(fired, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('フォローしているユーザーのフォローしていないユーザーの visibility: followers な投稿への返信のリノートが流れない', async () => {
|
||||||
|
const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' });
|
||||||
|
const kyokoReply = await post(kyoko, { text: 'reply to followers-only post', replyId: chitoseNote.id });
|
||||||
|
|
||||||
|
const fired = await waitFire(
|
||||||
|
ayano, 'homeTimeline', // ayano:home
|
||||||
|
() => api('notes/create', { renoteId: kyokoReply.id }, kyoko), // kyoko's renote of kyoko's reply to chitose's followers-only post
|
||||||
|
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(fired, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('フォローしていないユーザーの投稿は流れない', async () => {
|
test('フォローしていないユーザーの投稿は流れない', async () => {
|
||||||
|
@ -202,6 +226,39 @@ describe('Streaming', () => {
|
||||||
|
|
||||||
assert.strictEqual(fired, false);
|
assert.strictEqual(fired, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('withRenotes: false のときリノートが流れない', async () => {
|
||||||
|
const fired = await waitFire(
|
||||||
|
ayano, 'homeTimeline', // ayano:home
|
||||||
|
() => api('notes/create', { renoteId: kyokoNote.id }, kyoko), // kyoko renote
|
||||||
|
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
|
||||||
|
{ withRenotes: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(fired, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('withRenotes: false のとき引用リノートが流れる', async () => {
|
||||||
|
const fired = await waitFire(
|
||||||
|
ayano, 'homeTimeline', // ayano:home
|
||||||
|
() => api('notes/create', { text: 'quote', renoteId: kyokoNote.id }, kyoko), // kyoko quote
|
||||||
|
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
|
||||||
|
{ withRenotes: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(fired, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('withRenotes: false のとき投票のみのリノートが流れる', async () => {
|
||||||
|
const fired = await waitFire(
|
||||||
|
ayano, 'homeTimeline', // ayano:home
|
||||||
|
() => api('notes/create', { poll: { choices: ['kinoko', 'takenoko'] }, renoteId: kyokoNote.id }, kyoko), // kyoko renote with poll
|
||||||
|
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
|
||||||
|
{ withRenotes: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(fired, true);
|
||||||
|
});
|
||||||
}); // Home
|
}); // Home
|
||||||
|
|
||||||
describe('Local Timeline', () => {
|
describe('Local Timeline', () => {
|
||||||
|
|
|
@ -355,7 +355,7 @@ export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'D
|
||||||
return catcher;
|
return catcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function connectStream(user: UserToken, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> {
|
export function connectStream<C extends keyof misskey.Channels>(user: UserToken, channel: C, listener: (message: Record<string, any>) => any, params?: misskey.Channels[C]['params']): Promise<WebSocket> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
const url = new URL(`ws://127.0.0.1:${port}/streaming`);
|
const url = new URL(`ws://127.0.0.1:${port}/streaming`);
|
||||||
const options: ClientOptions = {};
|
const options: ClientOptions = {};
|
||||||
|
@ -390,7 +390,7 @@ export function connectStream(user: UserToken, channel: string, listener: (messa
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const waitFire = async (user: UserToken, channel: string, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: any) => {
|
export const waitFire = async <C extends keyof misskey.Channels>(user: UserToken, channel: C, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: misskey.Channels[C]['params']) => {
|
||||||
return new Promise<boolean>(async (res, rej) => {
|
return new Promise<boolean>(async (res, rej) => {
|
||||||
let timer: NodeJS.Timeout | null = null;
|
let timer: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
@ -435,7 +435,7 @@ export const waitFire = async (user: UserToken, channel: string, trgr: () => any
|
||||||
*/
|
*/
|
||||||
export function makeStreamCatcher<T>(
|
export function makeStreamCatcher<T>(
|
||||||
user: UserToken,
|
user: UserToken,
|
||||||
channel: string,
|
channel: keyof misskey.Channels,
|
||||||
cond: (message: Record<string, any>) => boolean,
|
cond: (message: Record<string, any>) => boolean,
|
||||||
extractor: (message: Record<string, any>) => T,
|
extractor: (message: Record<string, any>) => T,
|
||||||
timeout = 60 * 1000): Promise<T> {
|
timeout = 60 * 1000): Promise<T> {
|
||||||
|
|
Loading…
Reference in a new issue