import renderUpdate from "../../remote/activitypub/renderer/update.js"; import { renderActivity } from "../../remote/activitypub/renderer/index.js"; import { UserProfiles, Users } from "../../models/index.js"; import { renderPerson } from "../../remote/activitypub/renderer/person.js"; import { deliverToFollowers } from "../../remote/activitypub/deliver-manager.js"; import { deliverToRelays } from "../relay.js"; import { extractCustomEmojisFromMfm } from "../../misc/extract-custom-emojis-from-mfm.js"; import { extractHashtags } from "../../misc/extract-hashtags.js"; import { normalizeForSearch } from "../../misc/normalize-for-search.js"; import { updateUsertags } from "../update-hashtag.js"; import { publishMainStream, publishUserEvent } from "../stream.js"; import acceptAllFollowRequests from "../following/requests/accept-all.js"; import mfm from "mfm-js"; import { promiseEarlyReturn } from "../../prelude/promise.js"; import { UserConverter } from "../../server/api/mastodon/converters/user.js"; export async function updateUserProfileData(user, profile, updates, profileUpdates, isSecure) { if (!profile) profile = await UserProfiles.findOneByOrFail({ userId: user.id }); let emojis = []; let tags = []; const newName = updates.name === undefined ? user.name : updates.name; const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description; const newFields = profileUpdates.fields === undefined ? profile.fields : profileUpdates.fields; if (newName != null) { const tokens = mfm.parseSimple(newName); emojis.push(...extractCustomEmojisFromMfm(tokens)); } if (newDescription != null) { const tokens = mfm.parse(newDescription); emojis.push(...extractCustomEmojisFromMfm(tokens)); tags = extractHashtags(tokens).map((tag)=>normalizeForSearch(tag)).splice(0, 32); } for (const field of newFields || []){ const nameTokens = mfm.parse(field.name); emojis.push(...extractCustomEmojisFromMfm(nameTokens)); const valueTokens = mfm.parse(field.value); emojis.push(...extractCustomEmojisFromMfm(valueTokens)); } updates.emojis = [ ...new Set(emojis) ]; updates.tags = tags; updates.updatedAt = new Date(); updateUsertags(user, tags); const oldProfile = await UserProfiles.findOneBy({ userId: user.id }); if (Object.keys(updates).length > 0) await Users.update(user.id, updates); if (Object.keys(profileUpdates).length > 0) { await UserProfiles.update(user.id, profileUpdates); } const iObj = await Users.pack(user.id, user, { detail: true, includeSecrets: isSecure }); publishMainStream(user.id, "meUpdated", iObj); publishUserEvent(user.id, "updateUserProfile", await UserProfiles.findOneByOrFail({ userId: user.id })); if (user.isLocked && updates.isLocked === false) { acceptAllFollowRequests(user); } await promiseEarlyReturn(UserProfiles.updateMentions(user.id).finally(()=>{ UserConverter.prewarmCacheById(user.id, oldProfile); publishToFollowers(user.id); }), 1500); return iObj; } export async function publishToFollowers(userId) { const user = await Users.findOneBy({ id: userId }); if (user == null) throw new Error("user not found"); // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 if (Users.isLocalUser(user)) { const content = renderActivity(renderUpdate(await renderPerson(user), user)); deliverToFollowers(user, content); deliverToRelays(user, content); } }