diff --git a/locales/en-US.yml b/locales/en-US.yml index 5f3c74754..a786e178c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1080,6 +1080,7 @@ noteId: "Post ID" signupsDisabled: "Signups on this server are currently disabled, but you can always sign up at another server! If you have an invitation code for this server, please enter it below." findOtherInstance: "Find another server" apps: "Apps" +sendModMail: "Send Moderation Notice" _sensitiveMediaDetection: description: "Reduces the effort of server moderation through automatically recognizing\ diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e6f8f7ee6..6a98fdfb2 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -53,6 +53,7 @@ import * as ep___admin_resetPassword from "./endpoints/admin/reset-password.js"; import * as ep___admin_resolveAbuseUserReport from "./endpoints/admin/resolve-abuse-user-report.js"; import * as ep___admin_search_indexAll from "./endpoints/admin/search/index-all.js"; import * as ep___admin_sendEmail from "./endpoints/admin/send-email.js"; +import * as ep___admin_sendModMail from "./endpoints/admin/send-mod-mail.js"; import * as ep___admin_serverInfo from "./endpoints/admin/server-info.js"; import * as ep___admin_showModerationLogs from "./endpoints/admin/show-moderation-logs.js"; import * as ep___admin_showUser from "./endpoints/admin/show-user.js"; @@ -403,6 +404,7 @@ const eps = [ ["admin/resolve-abuse-user-report", ep___admin_resolveAbuseUserReport], ["admin/search/index-all", ep___admin_search_indexAll], ["admin/send-email", ep___admin_sendEmail], + ["admin/send-mod-mail", ep___admin_sendModMail], ["admin/server-info", ep___admin_serverInfo], ["admin/show-moderation-logs", ep___admin_showModerationLogs], ["admin/show-user", ep___admin_showUser], diff --git a/packages/backend/src/server/api/endpoints/admin/send-mod-mail.ts b/packages/backend/src/server/api/endpoints/admin/send-mod-mail.ts new file mode 100644 index 000000000..b6f3111ce --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/send-mod-mail.ts @@ -0,0 +1,68 @@ +import * as sanitizeHtml from "sanitize-html"; +import define from "../../define.js"; +import { Users, UserProfiles } from "@/models/index.js"; +import { ApiError } from "../../error.js"; +import { sendEmail } from "@/services/send-email.js"; +import { createNotification } from "@/services/create-notification.js"; + +export const meta = { + tags: ["users"], + + requireCredential: true, + requireModerator: true, + + description: "Send a mod mail.", + + errors: { + noSuchUser: { + message: "No such user.", + code: "NO_SUCH_USER", + id: "1acefcb5-0959-43fd-9685-b48305736cb5", + }, + noEmail: { + message: "No email for user.", + code: "NO_EMAIL", + id: "ac9d2d22-ef73-11ed-a05b-0242ac120003", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + userId: { type: "string", format: "misskey:id" }, + comment: { type: "string", minLength: 1, maxLength: 2048 }, + }, + required: ["userId", "comment"], +} as const; + +export default define(meta, paramDef, async (ps) => { + const [user, profile] = await Promise.all([ + Users.findOneBy({ id: ps.userId }), + UserProfiles.findOneBy({ userId: ps.userId }), + ]); + + if (user == null || profile == null) { + throw new ApiError(meta.errors.noSuchUser); + } + + createNotification(user.id, "app", { + customBody: ps.comment, + customHeader: "Moderation Notice", + customIcon: "/static-assets/badges/info.png", + }); + + setImmediate(async () => { + const email = profile.email; + if (email == null) { + throw new ApiError(meta.errors.noEmail); + } + + sendEmail( + email, + "Moderation notice", + sanitizeHtml(ps.comment), + sanitizeHtml(ps.comment), + ); + }); +}); diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue index 12321c2ba..f66069a35 100644 --- a/packages/client/src/pages/user-info.vue +++ b/packages/client/src/pages/user-info.vue @@ -218,6 +218,12 @@ >{{ i18n.ts.deleteAccount }} + {{ i18n.ts.sendModMail }}