mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-25 19:37:34 -07:00
[mastodon-client] GET /statuses/:id/context
This commit is contained in:
parent
2fe58b0017
commit
e1e4160a62
3 changed files with 76 additions and 37 deletions
|
@ -17,10 +17,10 @@ import { populatePoll } from "@/models/repositories/note.js";
|
||||||
import { FileConverter } from "@/server/api/mastodon/converters/file.js";
|
import { FileConverter } from "@/server/api/mastodon/converters/file.js";
|
||||||
|
|
||||||
export class NoteConverter {
|
export class NoteConverter {
|
||||||
public static async encode(note: Note, user: ILocalUser): Promise<MastodonEntity.Status> {
|
public static async encode(note: Note, user?: ILocalUser): Promise<MastodonEntity.Status> {
|
||||||
const noteUser = note.user ?? await getUser(note.userId);
|
const noteUser = note.user ?? await getUser(note.userId);
|
||||||
|
|
||||||
if (!await Notes.isVisibleForMe(note, user.id ?? null))
|
if (!await Notes.isVisibleForMe(note, user?.id ?? null))
|
||||||
throw new Error();
|
throw new Error();
|
||||||
|
|
||||||
const host = note.user?.host ?? null;
|
const host = note.user?.host ?? null;
|
||||||
|
@ -49,22 +49,22 @@ export class NoteConverter {
|
||||||
}
|
}
|
||||||
}) : null;
|
}) : null;
|
||||||
|
|
||||||
const reply = note.reply ?? (note.replyId ? await getNote(note.replyId, user) : null);
|
const reply = note.reply ?? (note.replyId ? await getNote(note.replyId, user ?? null) : null);
|
||||||
|
|
||||||
const isBookmarked = await NoteFavorites.exist({
|
const isBookmarked = user ? await NoteFavorites.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
},
|
},
|
||||||
take: 1,
|
take: 1,
|
||||||
});
|
}) : false;
|
||||||
|
|
||||||
const isMuted = await NoteThreadMutings.exist({
|
const isMuted = user ? await NoteThreadMutings.exist({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
threadId: note.threadId || note.id,
|
threadId: note.threadId || note.id,
|
||||||
}
|
}
|
||||||
});
|
}) : false;
|
||||||
|
|
||||||
const files = await DriveFiles.packMany(note.fileIds);
|
const files = await DriveFiles.packMany(note.fileIds);
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ export class NoteConverter {
|
||||||
mentions: await Promise.all(note.mentions.map(async p => MentionConverter.encode(await getUser(p)))),
|
mentions: await Promise.all(note.mentions.map(async p => MentionConverter.encode(await getUser(p)))),
|
||||||
tags: [], //FIXME
|
tags: [], //FIXME
|
||||||
card: null, //FIXME
|
card: null, //FIXME
|
||||||
poll: note.hasPoll ? PollConverter.encode(await populatePoll(note, user.id), note.id) : null,
|
poll: note.hasPoll ? PollConverter.encode(await populatePoll(note, user?.id ?? null), note.id) : null,
|
||||||
application: null, //FIXME
|
application: null, //FIXME
|
||||||
language: null, //FIXME
|
language: null, //FIXME
|
||||||
pinned: null, //FIXME
|
pinned: null, //FIXME
|
||||||
|
|
|
@ -4,18 +4,12 @@ import { emojiRegexAtStartToEnd } from "@/misc/emoji-regex.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import querystring from "node:querystring";
|
import querystring from "node:querystring";
|
||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
import { convertTimelinesArgsId, limitToInt } from "./timeline.js";
|
|
||||||
import { convertId, IdType } from "../../index.js";
|
import { convertId, IdType } from "../../index.js";
|
||||||
import {
|
import { convertAccount, convertAttachment, convertPoll, convertStatus, } from "../converters.js";
|
||||||
convertAccount,
|
|
||||||
convertAttachment,
|
|
||||||
convertPoll,
|
|
||||||
convertStatus,
|
|
||||||
} from "../converters.js";
|
|
||||||
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||||
import { getNote } from "@/server/api/common/getters.js";
|
import { getNote } from "@/server/api/common/getters.js";
|
||||||
import authenticate from "@/server/api/authenticate.js";
|
import authenticate from "@/server/api/authenticate.js";
|
||||||
import {Notes} from "@/models";
|
import { NoteHelpers } from "@/server/api/mastodon/helpers/note.js";
|
||||||
|
|
||||||
function normalizeQuery(data: any) {
|
function normalizeQuery(data: any) {
|
||||||
const str = querystring.stringify(data);
|
const str = querystring.stringify(data);
|
||||||
|
@ -153,15 +147,10 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
router.get<{ Params: { id: string } }>("/v1/statuses/:id", async (ctx) => {
|
router.get<{ Params: { id: string } }>("/v1/statuses/:id", async (ctx) => {
|
||||||
try {
|
try {
|
||||||
const auth = await authenticate(ctx.headers.authorization, null);
|
const auth = await authenticate(ctx.headers.authorization, null);
|
||||||
const user = auth[0];
|
const user = auth[0] ?? undefined;
|
||||||
|
|
||||||
if (!auth || !user) {
|
|
||||||
ctx.status = 401;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
|
const noteId = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||||
const note = await getNote(noteId, user).then(n => n).catch(() => null);
|
const note = await getNote(noteId, user ?? null).then(n => n).catch(() => null);
|
||||||
|
|
||||||
if (!note) {
|
if (!note) {
|
||||||
ctx.status = 404;
|
ctx.status = 404;
|
||||||
|
@ -200,23 +189,26 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
router.get<{ Params: { id: string } }>(
|
router.get<{ Params: { id: string } }>(
|
||||||
"/v1/statuses/:id/context",
|
"/v1/statuses/:id/context",
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
|
||||||
try {
|
try {
|
||||||
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
const auth = await authenticate(ctx.headers.authorization, null);
|
||||||
const data = await client.getStatusContext(
|
const user = auth[0] ?? undefined;
|
||||||
id,
|
|
||||||
convertTimelinesArgsId(limitToInt(ctx.query as any)),
|
|
||||||
);
|
|
||||||
|
|
||||||
data.data.ancestors = data.data.ancestors.map((status) =>
|
const id = convertId(ctx.params.id, IdType.IceshrimpId);
|
||||||
convertStatus(status),
|
const note = await getNote(id, user ?? null).then(n => n).catch(() => null);
|
||||||
);
|
if (!note) {
|
||||||
data.data.descendants = data.data.descendants.map((status) =>
|
if (!note) {
|
||||||
convertStatus(status),
|
ctx.status = 404;
|
||||||
);
|
return;
|
||||||
ctx.body = data.data;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ancestors = await NoteHelpers.getNoteAncestors(note, user, user ? 4096 : 60);
|
||||||
|
let children = await NoteHelpers.getNoteChildren(note, user, user ? 4096 : 40, user ? 4096 : 20);
|
||||||
|
ctx.body = {
|
||||||
|
ancestors: (await Promise.all(ancestors.map(n => NoteConverter.encode(n, user)))).map(s => convertStatus(s)),
|
||||||
|
descendants: (await Promise.all(children.map(n => NoteConverter.encode(n, user)))).map(s => convertStatus(s)),
|
||||||
|
};
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
|
|
47
packages/backend/src/server/api/mastodon/helpers/note.ts
Normal file
47
packages/backend/src/server/api/mastodon/helpers/note.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||||
|
import { Notes } from "@/models/index.js";
|
||||||
|
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
|
||||||
|
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
||||||
|
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
||||||
|
import { Note } from "@/models/entities/note.js";
|
||||||
|
import { ILocalUser } from "@/models/entities/user.js";
|
||||||
|
import querystring from "node:querystring";
|
||||||
|
import { getNote } from "@/server/api/common/getters.js";
|
||||||
|
|
||||||
|
export class NoteHelpers {
|
||||||
|
public static async getNoteChildren(note: Note | string, user?: ILocalUser, limit: number = 10, depth: number = 2): Promise<Note[]> {
|
||||||
|
const noteId = typeof note === "string" ? note : note.id;
|
||||||
|
const query = makePaginationQuery(Notes.createQueryBuilder("note"))
|
||||||
|
.andWhere(
|
||||||
|
"note.id IN (SELECT id FROM note_replies(:noteId, :depth, :limit))",
|
||||||
|
{noteId, depth, limit},
|
||||||
|
)
|
||||||
|
.innerJoinAndSelect("note.user", "user")
|
||||||
|
.leftJoinAndSelect("user.avatar", "avatar")
|
||||||
|
.leftJoinAndSelect("user.banner", "banner");
|
||||||
|
|
||||||
|
generateVisibilityQuery(query, user);
|
||||||
|
if (user) {
|
||||||
|
generateMutedUserQuery(query, user);
|
||||||
|
generateBlockedUserQuery(query, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getNoteAncestors(rootNote: Note, user?: ILocalUser, limit: number = 10): Promise<Note[]> {
|
||||||
|
const notes = new Array<Note>;
|
||||||
|
for (let i = 0; i < limit; i++) {
|
||||||
|
const currentNote = notes.at(-1) ?? rootNote;
|
||||||
|
if (!currentNote.replyId) break;
|
||||||
|
const nextNote = await getNote(currentNote.replyId, user ?? null).catch((e) => {
|
||||||
|
if (e.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") return null;
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
if (nextNote) notes.push(nextNote);
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return notes;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue