diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts index f39badfa3..f33cc2485 100644 --- a/packages/backend/src/server/api/endpoints/notes/edit.ts +++ b/packages/backend/src/server/api/endpoints/notes/edit.ts @@ -159,7 +159,7 @@ export const paramDef = { type: "object", properties: { editId: { type: "string", format: "misskey:id" }, - visibility: { type: "string", enum: noteVisibilities, default: "public" }, + visibility: { type: "string", enum: noteVisibilities, nullable: true }, visibleUserIds: { type: "array", uniqueItems: true, @@ -334,6 +334,11 @@ export default define(meta, paramDef, async (ps, user) => { } } + // keep visibility on edit if not specified + if (ps.visibility == null) { + ps.visibility = note.visibility; + } + // enforce silent clients on server if (user.isSilenced && ps.visibility === "public" && ps.channelId == null) { ps.visibility = "home"; diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index 61014827d..44c9f1936 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -96,6 +96,56 @@ export function apiStatusMastodon(router: Router): void { ctx.body = e.response.data; } }); + router.put("/v1/statuses/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + ctx.params.id = convertId(ctx.params.id, IdType.IceshrimpId); + let body: any = ctx.request.body; + if ( + (!body.poll && body["poll[options][]"]) || + (!body.media_ids && body["media_ids[]"]) + ) { + body = normalizeQuery(body); + } + if (!body.media_ids) body.media_ids = undefined; + if (body.media_ids && !body.media_ids.length) body.media_ids = undefined; + if (body.media_ids) { + body.media_ids = (body.media_ids as string[]).map((p) => + convertId(p, IdType.IceshrimpId), + ); + } + const { sensitive } = body; + body.sensitive = + typeof sensitive === "string" ? sensitive === "true" : sensitive; + + if (body.poll) { + if ( + body.poll.expires_in != null && + typeof body.poll.expires_in === "string" + ) + body.poll.expires_in = parseInt(body.poll.expires_in); + if ( + body.poll.multiple != null && + typeof body.poll.multiple === "string" + ) + body.poll.multiple = body.poll.multiple == "true"; + if ( + body.poll.hide_totals != null && + typeof body.poll.hide_totals === "string" + ) + body.poll.hide_totals = body.poll.hide_totals == "true"; + } + + const data = await client.editStatus(ctx.params.id, body); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = ctx.status == 404 ? 404 : 401; + ctx.body = e.response.data; + } + }); router.get<{ Params: { id: string } }>("/v1/statuses/:id", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; diff --git a/packages/megalodon/src/misskey.ts b/packages/megalodon/src/misskey.ts index c48815bfc..1d99fe6e8 100644 --- a/packages/megalodon/src/misskey.ts +++ b/packages/megalodon/src/misskey.ts @@ -1632,24 +1632,68 @@ export default class Misskey implements MegalodonInterface { } public async editStatus( - _id: string, - _options: { + id: string, + options: { status?: string; spoiler_text?: string; sensitive?: boolean; media_ids?: Array; poll?: { - options?: Array; - expires_in?: number; + options: Array; + expires_in: number; multiple?: boolean; hide_totals?: boolean; }; }, ): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError("misskey does not support"); - reject(err); - }); + let params = { + editId: id, + }; + if (options) { + params = Object.assign(params, { + text: options.status, + }); + if (options.media_ids) { + params = Object.assign(params, { + fileIds: options.media_ids, + }); + } + if (options.poll) { + let pollParam = { + choices: options.poll.options, + expiresAt: null, + expiredAfter: options.poll.expires_in * 1000, + }; + if (options.poll.multiple !== undefined) { + pollParam = Object.assign(pollParam, { + multiple: options.poll.multiple, + }); + } + params = Object.assign(params, { + poll: pollParam, + }); + } + if (options.sensitive) { + params = Object.assign(params, { + cw: "", + }); + } + if (options.spoiler_text) { + params = Object.assign(params, { + cw: options.spoiler_text, + }); + } + } + return this.client + .post("/api/notes/edit", params) + .then(async (res) => ({ + ...res, + data: await this.noteWithDetails( + res.data.createdNote, + this.baseUrlToHost(this.baseUrl), + this.getFreshAccountCache(), + ), + })); } /**