import { Not, In, IsNull } from "typeorm"; import { maximum } from "../../../../prelude/array.js"; import { Notes, Users } from "../../../../models/index.js"; import define from "../../define.js"; import { ApiError } from "../../error.js"; import { getUser } from "../../common/getters.js"; export const meta = { tags: [ "users" ], requireCredential: false, requireCredentialPrivateMode: true, description: "Get a list of other users that the specified user frequently replies to.", res: { type: "array", optional: false, nullable: false, items: { type: "object", optional: false, nullable: false, properties: { user: { type: "object", optional: false, nullable: false, ref: "UserDetailed" }, weight: { type: "number", optional: false, nullable: false } } } }, errors: { noSuchUser: { message: "No such user.", code: "NO_SUCH_USER", id: "e6965129-7b2a-40a4-bae2-cd84cd434822" } } }; export const paramDef = { type: "object", properties: { userId: { type: "string", format: "misskey:id" }, limit: { type: "integer", minimum: 1, maximum: 100, default: 10 } }, required: [ "userId" ] }; export default define(meta, paramDef, async (ps, me)=>{ // Lookup user const user = await getUser(ps.userId).catch((e)=>{ if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") throw new ApiError(meta.errors.noSuchUser); throw e; }); // Fetch recent notes const recentNotes = await Notes.find({ where: { userId: user.id, replyId: Not(IsNull()) }, order: { id: -1 }, take: 1000, select: [ "replyId" ] }); // 投稿が少なかったら中断 if (recentNotes.length === 0) { return []; } // TODO ミュートを考慮 const replyTargetNotes = await Notes.find({ where: { id: In(recentNotes.map((p)=>p.replyId)) }, select: [ "userId" ] }); const repliedUsers = {}; // Extract replies from recent notes for (const userId of replyTargetNotes.map((x)=>x.userId.toString())){ if (repliedUsers[userId]) { repliedUsers[userId]++; } else { repliedUsers[userId] = 1; } } // Calc peak const peak = maximum(Object.values(repliedUsers)); // Sort replies by frequency const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b)=>repliedUsers[b] - repliedUsers[a]); // Extract top replied users const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedUsers.map(async (user)=>({ user: await Users.pack(user, me, { detail: true }), weight: repliedUsers[user] / peak }))); return repliesObj; });