Merge branch 'develop' into mahjong
This commit is contained in:
		| @@ -385,7 +385,7 @@ export class CustomEmojiService implements OnApplicationShutdown { | ||||
| 	 */ | ||||
| 	@bindThis | ||||
| 	public checkDuplicate(name: string): Promise<boolean> { | ||||
| 		return this.emojisRepository.exist({ where: { name, host: IsNull() } }); | ||||
| 		return this.emojisRepository.exists({ where: { name, host: IsNull() } }); | ||||
| 	} | ||||
|  | ||||
| 	@bindThis | ||||
|   | ||||
| @@ -419,6 +419,10 @@ export class MfmService { | ||||
| 			}, | ||||
|  | ||||
| 			text: (node) => { | ||||
| 				if (!node.props.text.match(/[\r\n]/)) { | ||||
| 					return doc.createTextNode(node.props.text); | ||||
| 				} | ||||
|  | ||||
| 				const el = doc.createElement('span'); | ||||
| 				const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x)); | ||||
|  | ||||
|   | ||||
| @@ -603,7 +603,7 @@ export class NoteCreateService implements OnApplicationShutdown { | ||||
| 			if (data.reply) { | ||||
| 				// 通知 | ||||
| 				if (data.reply.userHost === null) { | ||||
| 					const isThreadMuted = await this.noteThreadMutingsRepository.exist({ | ||||
| 					const isThreadMuted = await this.noteThreadMutingsRepository.exists({ | ||||
| 						where: { | ||||
| 							userId: data.reply.userId, | ||||
| 							threadId: data.reply.threadId ?? data.reply.id, | ||||
| @@ -741,7 +741,7 @@ export class NoteCreateService implements OnApplicationShutdown { | ||||
| 	@bindThis | ||||
| 	private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) { | ||||
| 		for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { | ||||
| 			const isThreadMuted = await this.noteThreadMutingsRepository.exist({ | ||||
| 			const isThreadMuted = await this.noteThreadMutingsRepository.exists({ | ||||
| 				where: { | ||||
| 					userId: u.id, | ||||
| 					threadId: note.threadId ?? note.id, | ||||
|   | ||||
| @@ -49,7 +49,7 @@ export class NoteReadService implements OnApplicationShutdown { | ||||
| 		//#endregion | ||||
|  | ||||
| 		// スレッドミュート | ||||
| 		const isThreadMuted = await this.noteThreadMutingsRepository.exist({ | ||||
| 		const isThreadMuted = await this.noteThreadMutingsRepository.exists({ | ||||
| 			where: { | ||||
| 				userId: userId, | ||||
| 				threadId: note.threadId ?? note.id, | ||||
| @@ -70,7 +70,7 @@ export class NoteReadService implements OnApplicationShutdown { | ||||
|  | ||||
| 		// 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する | ||||
| 		setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => { | ||||
| 			const exist = await this.noteUnreadsRepository.exist({ where: { id: unread.id } }); | ||||
| 			const exist = await this.noteUnreadsRepository.exists({ where: { id: unread.id } }); | ||||
|  | ||||
| 			if (!exist) return; | ||||
|  | ||||
|   | ||||
| @@ -74,12 +74,12 @@ export class SignupService { | ||||
| 		const secret = generateUserToken(); | ||||
|  | ||||
| 		// Check username duplication | ||||
| 		if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { | ||||
| 		if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { | ||||
| 			throw new Error('DUPLICATED_USERNAME'); | ||||
| 		} | ||||
|  | ||||
| 		// Check deleted username duplication | ||||
| 		if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) { | ||||
| 		if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) { | ||||
| 			throw new Error('USED_USERNAME'); | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -144,7 +144,7 @@ export class UserFollowingService implements OnModuleInit { | ||||
| 			let autoAccept = false; | ||||
|  | ||||
| 			// 鍵アカウントであっても、既にフォローされていた場合はスルー | ||||
| 			const isFollowing = await this.followingsRepository.exist({ | ||||
| 			const isFollowing = await this.followingsRepository.exists({ | ||||
| 				where: { | ||||
| 					followerId: follower.id, | ||||
| 					followeeId: followee.id, | ||||
| @@ -156,7 +156,7 @@ export class UserFollowingService implements OnModuleInit { | ||||
|  | ||||
| 			// フォローしているユーザーは自動承認オプション | ||||
| 			if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { | ||||
| 				const isFollowed = await this.followingsRepository.exist({ | ||||
| 				const isFollowed = await this.followingsRepository.exists({ | ||||
| 					where: { | ||||
| 						followerId: followee.id, | ||||
| 						followeeId: follower.id, | ||||
| @@ -170,7 +170,7 @@ export class UserFollowingService implements OnModuleInit { | ||||
| 			if (followee.isLocked && !autoAccept) { | ||||
| 				autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs( | ||||
| 					follower, | ||||
| 					(oldSrc, newSrc) => this.followingsRepository.exist({ | ||||
| 					(oldSrc, newSrc) => this.followingsRepository.exists({ | ||||
| 						where: { | ||||
| 							followeeId: followee.id, | ||||
| 							followerId: newSrc.id, | ||||
| @@ -233,7 +233,7 @@ export class UserFollowingService implements OnModuleInit { | ||||
|  | ||||
| 		this.cacheService.userFollowingsCache.refresh(follower.id); | ||||
|  | ||||
| 		const requestExist = await this.followRequestsRepository.exist({ | ||||
| 		const requestExist = await this.followRequestsRepository.exists({ | ||||
| 			where: { | ||||
| 				followeeId: followee.id, | ||||
| 				followerId: follower.id, | ||||
| @@ -531,7 +531,7 @@ export class UserFollowingService implements OnModuleInit { | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		const requestExist = await this.followRequestsRepository.exist({ | ||||
| 		const requestExist = await this.followRequestsRepository.exists({ | ||||
| 			where: { | ||||
| 				followeeId: followee.id, | ||||
| 				followerId: follower.id, | ||||
|   | ||||
| @@ -629,7 +629,7 @@ export class ApInboxService { | ||||
| 			return 'skip: follower not found'; | ||||
| 		} | ||||
|  | ||||
| 		const isFollowing = await this.followingsRepository.exist({ | ||||
| 		const isFollowing = await this.followingsRepository.exists({ | ||||
| 			where: { | ||||
| 				followerId: follower.id, | ||||
| 				followeeId: actor.id, | ||||
| @@ -686,14 +686,14 @@ export class ApInboxService { | ||||
| 			return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません'; | ||||
| 		} | ||||
|  | ||||
| 		const requestExist = await this.followRequestsRepository.exist({ | ||||
| 		const requestExist = await this.followRequestsRepository.exists({ | ||||
| 			where: { | ||||
| 				followerId: actor.id, | ||||
| 				followeeId: followee.id, | ||||
| 			}, | ||||
| 		}); | ||||
|  | ||||
| 		const isFollowing = await this.followingsRepository.exist({ | ||||
| 		const isFollowing = await this.followingsRepository.exists({ | ||||
| 			where: { | ||||
| 				followerId: actor.id, | ||||
| 				followeeId: followee.id, | ||||
|   | ||||
| @@ -25,8 +25,21 @@ export class ApMfmService { | ||||
| 	} | ||||
|  | ||||
| 	@bindThis | ||||
| 	public getNoteHtml(note: MiNote): string | null { | ||||
| 		if (!note.text) return ''; | ||||
| 		return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); | ||||
| 	public getNoteHtml(note: MiNote, apAppend?: string) { | ||||
| 		let noMisskeyContent = false; | ||||
| 		const srcMfm = (note.text ?? '') + (apAppend ?? ''); | ||||
|  | ||||
| 		const parsed = mfm.parse(srcMfm); | ||||
|  | ||||
| 		if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { | ||||
| 			noMisskeyContent = true; | ||||
| 		} | ||||
|  | ||||
| 		const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers)); | ||||
|  | ||||
| 		return { | ||||
| 			content, | ||||
| 			noMisskeyContent, | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -325,7 +325,7 @@ export class ApRendererService { | ||||
| 			inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); | ||||
|  | ||||
| 			if (inReplyToNote != null) { | ||||
| 				const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } }); | ||||
| 				const inReplyToUserExist = await this.usersRepository.exists({ where: { id: inReplyToNote.userId } }); | ||||
|  | ||||
| 				if (inReplyToUserExist) { | ||||
| 					if (inReplyToNote.uri) { | ||||
| @@ -389,17 +389,15 @@ export class ApRendererService { | ||||
| 			poll = await this.pollsRepository.findOneBy({ noteId: note.id }); | ||||
| 		} | ||||
|  | ||||
| 		let apText = text; | ||||
| 		let apAppend = ''; | ||||
|  | ||||
| 		if (quote) { | ||||
| 			apText += `\n\nRE: ${quote}`; | ||||
| 			apAppend += `\n\nRE: ${quote}`; | ||||
| 		} | ||||
|  | ||||
| 		const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; | ||||
|  | ||||
| 		const content = this.apMfmService.getNoteHtml(Object.assign({}, note, { | ||||
| 			text: apText, | ||||
| 		})); | ||||
| 		const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, apAppend); | ||||
|  | ||||
| 		const emojis = await this.getEmojis(note.emojis); | ||||
| 		const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji)); | ||||
| @@ -412,9 +410,6 @@ export class ApRendererService { | ||||
|  | ||||
| 		const asPoll = poll ? { | ||||
| 			type: 'Question', | ||||
| 			content: this.apMfmService.getNoteHtml(Object.assign({}, note, { | ||||
| 				text: text, | ||||
| 			})), | ||||
| 			[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt, | ||||
| 			[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ | ||||
| 				type: 'Note', | ||||
| @@ -432,11 +427,13 @@ export class ApRendererService { | ||||
| 			attributedTo, | ||||
| 			summary: summary ?? undefined, | ||||
| 			content: content ?? undefined, | ||||
| 			_misskey_content: text, | ||||
| 			source: { | ||||
| 				content: text, | ||||
| 				mediaType: 'text/x.misskeymarkdown', | ||||
| 			}, | ||||
| 			...(noMisskeyContent ? {} : { | ||||
| 				_misskey_content: text, | ||||
| 				source: { | ||||
| 					content: text, | ||||
| 					mediaType: 'text/x.misskeymarkdown', | ||||
| 				}, | ||||
| 			}), | ||||
| 			_misskey_quote: quote, | ||||
| 			quoteUrl: quote, | ||||
| 			published: this.idService.parse(note.id).date.toISOString(), | ||||
| @@ -625,6 +622,7 @@ export class ApRendererService { | ||||
| 				'https://www.w3.org/ns/activitystreams', | ||||
| 				'https://w3id.org/security/v1', | ||||
| 				{ | ||||
| 					Key: 'sec:Key', | ||||
| 					// as non-standards | ||||
| 					manuallyApprovesFollowers: 'as:manuallyApprovesFollowers', | ||||
| 					sensitive: 'as:sensitive', | ||||
|   | ||||
| @@ -51,14 +51,14 @@ export class ChannelEntityService { | ||||
|  | ||||
| 		const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null; | ||||
|  | ||||
| 		const isFollowing = meId ? await this.channelFollowingsRepository.exist({ | ||||
| 		const isFollowing = meId ? await this.channelFollowingsRepository.exists({ | ||||
| 			where: { | ||||
| 				followerId: meId, | ||||
| 				followeeId: channel.id, | ||||
| 			}, | ||||
| 		}) : false; | ||||
|  | ||||
| 		const isFavorited = meId ? await this.channelFavoritesRepository.exist({ | ||||
| 		const isFavorited = meId ? await this.channelFavoritesRepository.exists({ | ||||
| 			where: { | ||||
| 				userId: meId, | ||||
| 				channelId: channel.id, | ||||
|   | ||||
| @@ -46,7 +46,7 @@ export class ClipEntityService { | ||||
| 			description: clip.description, | ||||
| 			isPublic: clip.isPublic, | ||||
| 			favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }), | ||||
| 			isFavorited: meId ? await this.clipFavoritesRepository.exist({ where: { clipId: clip.id, userId: meId } }) : undefined, | ||||
| 			isFavorited: meId ? await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: meId } }) : undefined, | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -31,6 +31,7 @@ export class EmojiEntityService { | ||||
| 			category: emoji.category, | ||||
| 			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) | ||||
| 			url: emoji.publicUrl || emoji.originalUrl, | ||||
| 			localOnly: emoji.localOnly ? true : undefined, | ||||
| 			isSensitive: emoji.isSensitive ? true : undefined, | ||||
| 			roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, | ||||
| 		}; | ||||
|   | ||||
| @@ -47,7 +47,7 @@ export class FlashEntityService { | ||||
| 			summary: flash.summary, | ||||
| 			script: flash.script, | ||||
| 			likedCount: flash.likedCount, | ||||
| 			isLiked: meId ? await this.flashLikesRepository.exist({ where: { flashId: flash.id, userId: meId } }) : undefined, | ||||
| 			isLiked: meId ? await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } }) : undefined, | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -53,7 +53,7 @@ export class GalleryPostEntityService { | ||||
| 			tags: post.tags.length > 0 ? post.tags : undefined, | ||||
| 			isSensitive: post.isSensitive, | ||||
| 			likedCount: post.likedCount, | ||||
| 			isLiked: meId ? await this.galleryLikesRepository.exist({ where: { postId: post.id, userId: meId } }) : undefined, | ||||
| 			isLiked: meId ? await this.galleryLikesRepository.exists({ where: { postId: post.id, userId: meId } }) : undefined, | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -108,7 +108,7 @@ export class NoteEntityService implements OnModuleInit { | ||||
| 				hide = false; | ||||
| 			} else { | ||||
| 				// フォロワーかどうか | ||||
| 				const isFollowing = await this.followingsRepository.exist({ | ||||
| 				const isFollowing = await this.followingsRepository.exists({ | ||||
| 					where: { | ||||
| 						followeeId: packedNote.userId, | ||||
| 						followerId: meId, | ||||
|   | ||||
| @@ -104,7 +104,7 @@ export class PageEntityService { | ||||
| 			eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null, | ||||
| 			attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null)), | ||||
| 			likedCount: page.likedCount, | ||||
| 			isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined, | ||||
| 			isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined, | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -152,43 +152,43 @@ export class UserEntityService implements OnModuleInit { | ||||
| 				followerId: me, | ||||
| 				followeeId: target, | ||||
| 			}), | ||||
| 			this.followingsRepository.exist({ | ||||
| 			this.followingsRepository.exists({ | ||||
| 				where: { | ||||
| 					followerId: target, | ||||
| 					followeeId: me, | ||||
| 				}, | ||||
| 			}), | ||||
| 			this.followRequestsRepository.exist({ | ||||
| 			this.followRequestsRepository.exists({ | ||||
| 				where: { | ||||
| 					followerId: me, | ||||
| 					followeeId: target, | ||||
| 				}, | ||||
| 			}), | ||||
| 			this.followRequestsRepository.exist({ | ||||
| 			this.followRequestsRepository.exists({ | ||||
| 				where: { | ||||
| 					followerId: target, | ||||
| 					followeeId: me, | ||||
| 				}, | ||||
| 			}), | ||||
| 			this.blockingsRepository.exist({ | ||||
| 			this.blockingsRepository.exists({ | ||||
| 				where: { | ||||
| 					blockerId: me, | ||||
| 					blockeeId: target, | ||||
| 				}, | ||||
| 			}), | ||||
| 			this.blockingsRepository.exist({ | ||||
| 			this.blockingsRepository.exists({ | ||||
| 				where: { | ||||
| 					blockerId: target, | ||||
| 					blockeeId: me, | ||||
| 				}, | ||||
| 			}), | ||||
| 			this.mutingsRepository.exist({ | ||||
| 			this.mutingsRepository.exists({ | ||||
| 				where: { | ||||
| 					muterId: me, | ||||
| 					muteeId: target, | ||||
| 				}, | ||||
| 			}), | ||||
| 			this.renoteMutingsRepository.exist({ | ||||
| 			this.renoteMutingsRepository.exists({ | ||||
| 				where: { | ||||
| 					muterId: me, | ||||
| 					muteeId: target, | ||||
| @@ -215,7 +215,7 @@ export class UserEntityService implements OnModuleInit { | ||||
| 		/* | ||||
| 		const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); | ||||
|  | ||||
| 		const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exist({ | ||||
| 		const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exists({ | ||||
| 			where: { | ||||
| 				antennaId: In(myAntennas.map(x => x.id)), | ||||
| 				read: false, | ||||
|   | ||||
| @@ -27,6 +27,10 @@ export const packedEmojiSimpleSchema = { | ||||
| 			type: 'string', | ||||
| 			optional: false, nullable: false, | ||||
| 		}, | ||||
| 		localOnly: { | ||||
| 			type: 'boolean', | ||||
| 			optional: true, nullable: false, | ||||
| 		}, | ||||
| 		isSensitive: { | ||||
| 			type: 'boolean', | ||||
| 			optional: true, nullable: false, | ||||
|   | ||||
| @@ -163,12 +163,12 @@ export class SignupApiService { | ||||
| 		} | ||||
|  | ||||
| 		if (instance.emailRequiredForSignup) { | ||||
| 			if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { | ||||
| 			if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) { | ||||
| 				throw new FastifyReplyError(400, 'DUPLICATED_USERNAME'); | ||||
| 			} | ||||
|  | ||||
| 			// Check deleted username duplication | ||||
| 			if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) { | ||||
| 			if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) { | ||||
| 				throw new FastifyReplyError(400, 'USED_USERNAME'); | ||||
| 			} | ||||
|  | ||||
|   | ||||
| @@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 				throw e; | ||||
| 			}); | ||||
|  | ||||
| 			const exist = await this.promoNotesRepository.exist({ where: { noteId: note.id } }); | ||||
| 			const exist = await this.promoNotesRepository.exists({ where: { noteId: note.id } }); | ||||
|  | ||||
| 			if (exist) { | ||||
| 				throw new ApiError(meta.errors.alreadyPromoted); | ||||
|   | ||||
| @@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			const accessToken = secureRndstr(32); | ||||
|  | ||||
| 			// Fetch exist access token | ||||
| 			const exist = await this.accessTokensRepository.exist({ | ||||
| 			const exist = await this.accessTokensRepository.exists({ | ||||
| 				where: { | ||||
| 					appId: session.appId, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -88,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			}); | ||||
|  | ||||
| 			// Check if already blocking | ||||
| 			const exist = await this.blockingsRepository.exist({ | ||||
| 			const exist = await this.blockingsRepository.exists({ | ||||
| 				where: { | ||||
| 					blockerId: blocker.id, | ||||
| 					blockeeId: blockee.id, | ||||
|   | ||||
| @@ -88,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			}); | ||||
|  | ||||
| 			// Check not blocking | ||||
| 			const exist = await this.blockingsRepository.exist({ | ||||
| 			const exist = await this.blockingsRepository.exists({ | ||||
| 				where: { | ||||
| 					blockerId: blocker.id, | ||||
| 					blockeeId: blockee.id, | ||||
|   | ||||
| @@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 				throw new ApiError(meta.errors.noSuchClip); | ||||
| 			} | ||||
|  | ||||
| 			const exist = await this.clipFavoritesRepository.exist({ | ||||
| 			const exist = await this.clipFavoritesRepository.exists({ | ||||
| 				where: { | ||||
| 					clipId: clip.id, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -38,7 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 		private driveFilesRepository: DriveFilesRepository, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const exist = await this.driveFilesRepository.exist({ | ||||
| 			const exist = await this.driveFilesRepository.exists({ | ||||
| 				where: { | ||||
| 					md5: ps.md5, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			} | ||||
|  | ||||
| 			// if already liked | ||||
| 			const exist = await this.flashLikesRepository.exist({ | ||||
| 			const exist = await this.flashLikesRepository.exists({ | ||||
| 				where: { | ||||
| 					flashId: flash.id, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			}); | ||||
|  | ||||
| 			// Check if already following | ||||
| 			const exist = await this.followingsRepository.exist({ | ||||
| 			const exist = await this.followingsRepository.exists({ | ||||
| 				where: { | ||||
| 					followerId: follower.id, | ||||
| 					followeeId: followee.id, | ||||
|   | ||||
| @@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			}); | ||||
|  | ||||
| 			// Check not following | ||||
| 			const exist = await this.followingsRepository.exist({ | ||||
| 			const exist = await this.followingsRepository.exists({ | ||||
| 				where: { | ||||
| 					followerId: follower.id, | ||||
| 					followeeId: followee.id, | ||||
|   | ||||
| @@ -72,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			} | ||||
|  | ||||
| 			// if already liked | ||||
| 			const exist = await this.galleryLikesRepository.exist({ | ||||
| 			const exist = await this.galleryLikesRepository.exists({ | ||||
| 				where: { | ||||
| 					postId: post.id, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 		private downloadService: DownloadService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const userExist = await this.usersRepository.exist({ where: { id: me.id } }); | ||||
| 			const userExist = await this.usersRepository.exists({ where: { id: me.id } }); | ||||
| 			if (!userExist) throw new ApiError(meta.errors.noSuchUser); | ||||
| 			const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); | ||||
| 			if (file === null) throw new ApiError(meta.errors.noSuchFile); | ||||
|   | ||||
| @@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			if (ps.tokenId) { | ||||
| 				const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } }); | ||||
| 				const tokenExist = await this.accessTokensRepository.exists({ where: { id: ps.tokenId } }); | ||||
|  | ||||
| 				if (tokenExist) { | ||||
| 					await this.accessTokensRepository.delete({ | ||||
| @@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 					}); | ||||
| 				} | ||||
| 			} else if (ps.token) { | ||||
| 				const tokenExist = await this.accessTokensRepository.exist({ where: { token: ps.token } }); | ||||
| 				const tokenExist = await this.accessTokensRepository.exists({ where: { token: ps.token } }); | ||||
|  | ||||
| 				if (tokenExist) { | ||||
| 					await this.accessTokensRepository.delete({ | ||||
|   | ||||
| @@ -83,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			}); | ||||
|  | ||||
| 			// Check if already muting | ||||
| 			const exist = await this.mutingsRepository.exist({ | ||||
| 			const exist = await this.mutingsRepository.exists({ | ||||
| 				where: { | ||||
| 					muterId: muter.id, | ||||
| 					muteeId: mutee.id, | ||||
|   | ||||
| @@ -260,7 +260,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
|  | ||||
| 				// Check blocking | ||||
| 				if (renote.userId !== me.id) { | ||||
| 					const blockExist = await this.blockingsRepository.exist({ | ||||
| 					const blockExist = await this.blockingsRepository.exists({ | ||||
| 						where: { | ||||
| 							blockerId: renote.userId, | ||||
| 							blockeeId: me.id, | ||||
| @@ -308,7 +308,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
|  | ||||
| 				// Check blocking | ||||
| 				if (reply.userId !== me.id) { | ||||
| 					const blockExist = await this.blockingsRepository.exist({ | ||||
| 					const blockExist = await this.blockingsRepository.exists({ | ||||
| 						where: { | ||||
| 							blockerId: reply.userId, | ||||
| 							blockeeId: me.id, | ||||
|   | ||||
| @@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			}); | ||||
|  | ||||
| 			// if already favorited | ||||
| 			const exist = await this.noteFavoritesRepository.exist({ | ||||
| 			const exist = await this.noteFavoritesRepository.exists({ | ||||
| 				where: { | ||||
| 					noteId: note.id, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 			} | ||||
|  | ||||
| 			// if already liked | ||||
| 			const exist = await this.pageLikesRepository.exist({ | ||||
| 			const exist = await this.pageLikesRepository.exists({ | ||||
| 				where: { | ||||
| 					pageId: page.id, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 				throw err; | ||||
| 			}); | ||||
|  | ||||
| 			const exist = await this.promoReadsRepository.exist({ | ||||
| 			const exist = await this.promoReadsRepository.exists({ | ||||
| 				where: { | ||||
| 					noteId: note.id, | ||||
| 					userId: me.id, | ||||
|   | ||||
| @@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 				if (me == null) { | ||||
| 					throw new ApiError(meta.errors.forbidden); | ||||
| 				} else if (me.id !== user.id) { | ||||
| 					const isFollowing = await this.followingsRepository.exist({ | ||||
| 					const isFollowing = await this.followingsRepository.exists({ | ||||
| 						where: { | ||||
| 							followeeId: user.id, | ||||
| 							followerId: me.id, | ||||
|   | ||||
| @@ -109,7 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 				if (me == null) { | ||||
| 					throw new ApiError(meta.errors.forbidden); | ||||
| 				} else if (me.id !== user.id) { | ||||
| 					const isFollowing = await this.followingsRepository.exist({ | ||||
| 					const isFollowing = await this.followingsRepository.exists({ | ||||
| 						where: { | ||||
| 							followeeId: user.id, | ||||
| 							followerId: me.id, | ||||
|   | ||||
| @@ -90,7 +90,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 		private roleService: RoleService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const listExist = await this.userListsRepository.exist({ | ||||
| 			const listExist = await this.userListsRepository.exists({ | ||||
| 				where: { | ||||
| 					id: ps.listId, | ||||
| 					isPublic: true, | ||||
| @@ -121,7 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 				}); | ||||
|  | ||||
| 				if (currentUser.id !== me.id) { | ||||
| 					const blockExist = await this.blockingsRepository.exist({ | ||||
| 					const blockExist = await this.blockingsRepository.exists({ | ||||
| 						where: { | ||||
| 							blockerId: currentUser.id, | ||||
| 							blockeeId: me.id, | ||||
| @@ -132,7 +132,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				const exist = await this.userListMembershipsRepository.exist({ | ||||
| 				const exist = await this.userListMembershipsRepository.exists({ | ||||
| 					where: { | ||||
| 						userListId: userList.id, | ||||
| 						userId: currentUser.id, | ||||
|   | ||||
| @@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 		private idService: IdService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const userListExist = await this.userListsRepository.exist({ | ||||
| 			const userListExist = await this.userListsRepository.exists({ | ||||
| 				where: { | ||||
| 					id: ps.listId, | ||||
| 					isPublic: true, | ||||
| @@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 				throw new ApiError(meta.errors.noSuchList); | ||||
| 			} | ||||
|  | ||||
| 			const exist = await this.userListFavoritesRepository.exist({ | ||||
| 			const exist = await this.userListFavoritesRepository.exists({ | ||||
| 				where: { | ||||
| 					userId: me.id, | ||||
| 					userListId: ps.listId, | ||||
|   | ||||
| @@ -104,7 +104,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
|  | ||||
| 			// Check blocking | ||||
| 			if (user.id !== me.id) { | ||||
| 				const blockExist = await this.blockingsRepository.exist({ | ||||
| 				const blockExist = await this.blockingsRepository.exists({ | ||||
| 					where: { | ||||
| 						blockerId: user.id, | ||||
| 						blockeeId: me.id, | ||||
| @@ -115,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			const exist = await this.userListMembershipsRepository.exist({ | ||||
| 			const exist = await this.userListMembershipsRepository.exists({ | ||||
| 				where: { | ||||
| 					userListId: userList.id, | ||||
| 					userId: user.id, | ||||
|   | ||||
| @@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 					userListId: ps.listId, | ||||
| 				}); | ||||
| 				if (me !== null) { | ||||
| 					additionalProperties.isLiked = await this.userListFavoritesRepository.exist({ | ||||
| 					additionalProperties.isLiked = await this.userListFavoritesRepository.exists({ | ||||
| 						where: { | ||||
| 							userId: me.id, | ||||
| 							userListId: ps.listId, | ||||
|   | ||||
| @@ -45,7 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 		private userListFavoritesRepository: UserListFavoritesRepository, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const userListExist = await this.userListsRepository.exist({ | ||||
| 			const userListExist = await this.userListsRepository.exists({ | ||||
| 				where: { | ||||
| 					id: ps.listId, | ||||
| 					isPublic: true, | ||||
|   | ||||
| @@ -43,7 +43,7 @@ class UserListChannel extends Channel { | ||||
| 		this.withRenotes = params.withRenotes ?? true; | ||||
|  | ||||
| 		// Check existence and owner | ||||
| 		const listExist = await this.userListsRepository.exist({ | ||||
| 		const listExist = await this.userListsRepository.exists({ | ||||
| 			where: { | ||||
| 				id: this.listId, | ||||
| 				userId: this.user!.id, | ||||
|   | ||||
							
								
								
									
										44
									
								
								packages/backend/test/unit/ApMfmService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								packages/backend/test/unit/ApMfmService.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| import * as assert from 'assert'; | ||||
| import { Test } from '@nestjs/testing'; | ||||
|  | ||||
| import { CoreModule } from '@/core/CoreModule.js'; | ||||
| import { ApMfmService } from '@/core/activitypub/ApMfmService.js'; | ||||
| import { GlobalModule } from '@/GlobalModule.js'; | ||||
| import { MiNote } from '@/models/Note.js'; | ||||
|  | ||||
| describe('ApMfmService', () => { | ||||
| 	let apMfmService: ApMfmService; | ||||
|  | ||||
| 	beforeAll(async () => { | ||||
| 		const app = await Test.createTestingModule({ | ||||
| 			imports: [GlobalModule, CoreModule], | ||||
| 		}).compile(); | ||||
| 		apMfmService = app.get<ApMfmService>(ApMfmService); | ||||
| 	}); | ||||
|  | ||||
| 	describe('getNoteHtml', () => { | ||||
| 		test('Do not provide _misskey_content for simple text', () => { | ||||
| 			const note: MiNote = { | ||||
| 				text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com', | ||||
| 				mentionedRemoteUsers: '[]', | ||||
| 			} as any; | ||||
|  | ||||
| 			const { content, noMisskeyContent } = apMfmService.getNoteHtml(note); | ||||
|  | ||||
| 			assert.equal(noMisskeyContent, true, 'noMisskeyContent'); | ||||
| 			assert.equal(content, '<p>テキスト <a href="http://misskey.local/tags/タグ" rel="tag">#タグ</a> <a href="http://misskey.local/@mention" class="u-url mention">@mention</a> 🍊 :emoji: <a href="https://example.com">https://example.com</a></p>', 'content'); | ||||
| 		}); | ||||
|  | ||||
| 		test('Provide _misskey_content for MFM', () => { | ||||
| 			const note: MiNote = { | ||||
| 				text: '$[tada foo]', | ||||
| 				mentionedRemoteUsers: '[]', | ||||
| 			} as any; | ||||
|  | ||||
| 			const { content, noMisskeyContent } = apMfmService.getNoteHtml(note); | ||||
|  | ||||
| 			assert.equal(noMisskeyContent, false, 'noMisskeyContent'); | ||||
| 			assert.equal(content, '<p><i>foo</i></p>', 'content'); | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
| @@ -33,6 +33,12 @@ describe('MfmService', () => { | ||||
| 			const output = '<p><span>foo<br>bar<br>baz</span></p>'; | ||||
| 			assert.equal(mfmService.toHtml(mfm.parse(input)), output); | ||||
| 		}); | ||||
|  | ||||
| 		test('Do not generate unnecessary span', () => { | ||||
| 			const input = 'foo $[tada bar]'; | ||||
| 			const output = '<p>foo <i>bar</i></p>'; | ||||
| 			assert.equal(mfmService.toHtml(mfm.parse(input)), output); | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| 	describe('fromHtml', () => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo