Fixed 267U.pre2

This commit is contained in:
2026-07-26 18:25:37 +09:00
parent 50bfaeafdf
commit 317d00a284
1286 changed files with 80222 additions and 1 deletions
@@ -0,0 +1,21 @@
import accept from "../../../../services/following/requests/accept.js";
import DbResolver from "../../db-resolver.js";
import { relayAccepted } from "../../../../services/relay.js";
export default (async (actor, activity)=>{
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
const dbResolver = new DbResolver();
const follower = await dbResolver.getUserFromApId(activity.actor);
if (follower == null) {
return "skip: follower not found";
}
if (follower.host != null) {
return "skip: follower is not a local user";
}
// relay
const match = activity.id?.match(/follow-relay\/(\w+)/);
if (match) {
return await relayAccepted(match[1]);
}
await accept(actor, follower);
return "ok";
});
@@ -0,0 +1,18 @@
import Resolver from "../../resolver.js";
import acceptFollow from "./follow.js";
import { isFollow, getApType, isQuoteRequest } from "../../type.js";
import { apLogger } from "../../logger.js";
import { acceptQuoteRequest } from "./quote-request.js";
const logger = apLogger;
export default (async (actor, activity)=>{
const uri = activity.id || activity;
logger.info(`Accept: ${uri}`);
const resolver = new Resolver();
const object = await resolver.resolve(activity.object).catch((e)=>{
logger.error(`Resolution failed: ${e}`);
throw e;
});
if (isFollow(object)) return await acceptFollow(actor, object);
else if (isQuoteRequest(object)) return await acceptQuoteRequest(actor, object, activity.result);
return `skip: Unknown Accept type: ${getApType(object)}`;
});
@@ -0,0 +1,44 @@
import { resolveNote } from "../../models/note.js";
import { Notes } from "../../../../models/index.js";
import { parseUri } from "../../db-resolver.js";
import edit from "../../../../services/note/edit.js";
import { toPuny } from "../../../../misc/convert-host.js";
export async function acceptQuoteRequest(actor, activity, result) {
if (!result) return "skip: missing result";
const localParsed = parseUri(activity.instrument);
if (!localParsed.local) return "skip: note not local";
const resultUrl = new URL(result);
if (toPuny(resultUrl.host) !== actor.host) {
return "skip: result not on same host as actor";
}
const [note, targetNote] = await Promise.all([
Notes.findOne({
where: {
id: localParsed.id
},
relations: [
"user"
]
}),
resolveNote(activity.object)
]);
if (note === null) return "skip: note not found";
if (targetNote === null) return "skip: target note not found";
if (targetNote.userId !== actor.id) return "skip: tried to authorize note without ownership";
if (note.renoteId === null) return "skip: note not renote";
if (note.renoteId !== targetNote.id) return "skip: note not renoting target";
if (note.quoteAuthorization !== null) return "skip: quote already authorizated";
if (note.text == null && note.cw == null && !note.hasPoll && note.fileIds.length === 0) return "skip: note is plain renote";
note.quoteAuthorization = result;
await Notes.update({
id: note.id
}, {
quoteAuthorization: note.quoteAuthorization
});
await edit(note.user, note, {
text: note.text,
cw: note.cw,
quoteAuthorization: result
}, true);
return "ok";
}
@@ -0,0 +1,22 @@
import { resolveNote } from "../../models/note.js";
import { addPinned } from "../../../../services/i/pin.js";
import Resolver from "../../resolver.js";
import { Users } from "../../../../models/index.js";
export default (async (actor, activity)=>{
if ("actor" in activity && actor.uri !== activity.actor) {
throw new Error("invalid actor");
}
if (activity.target == null) {
throw new Error("target is null");
}
if (activity.target === actor.featured) {
const resolver = new Resolver();
const follower = await Users.getRandomFollower(actor.id);
if (follower) resolver.setUser(follower);
const note = await resolveNote(activity.object, resolver);
if (note == null) throw new Error("note not found");
await addPinned(actor, note.id);
return "ok";
}
throw new Error(`unknown target: ${activity.target}`);
});
@@ -0,0 +1,12 @@
import Resolver from "../../resolver.js";
import announceNote from "./note.js";
import { getApId } from "../../type.js";
import { apLogger } from "../../logger.js";
const logger = apLogger;
export default (async (actor, activity)=>{
const uri = getApId(activity);
logger.info(`Announce: ${uri}`);
const resolver = new Resolver();
const targetUri = getApId(activity.object);
return announceNote(resolver, actor, activity, targetUri);
});
@@ -0,0 +1,57 @@
import post from "../../../../services/note/create.js";
import { getApId } from "../../type.js";
import { fetchNote, resolveNote } from "../../models/note.js";
import { apLogger } from "../../logger.js";
import { extractDbHost } from "../../../../misc/convert-host.js";
import { getApLock } from "../../../../misc/app-lock.js";
import { parseAudience } from "../../audience.js";
import { StatusError } from "../../../../misc/fetch.js";
import { Notes } from "../../../../models/index.js";
import { shouldBlockInstance } from "../../../../misc/should-block-instance.js";
const logger = apLogger;
/**
* Handle announcement activities
*/ export default async function(resolver, actor, activity, targetUri) {
const uri = getApId(activity);
if (actor.isSuspended) {
return "skip: actor is suspended";
}
// Interrupt if you block the announcement destination
if (await shouldBlockInstance(extractDbHost(uri))) return "skip: instance is blocked";
const unlock = await getApLock(uri);
try {
// Check if something with the same URI is already registered
const exist = await fetchNote(uri);
if (exist) {
return "skip: note exists";
}
// Resolve Announce target
let renote;
try {
renote = await resolveNote(targetUri);
} catch (e) {
// Skip if target is 4xx
if (e instanceof StatusError) {
if (!e.isRetryable) {
logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`);
return "skip: failed fetching note";
}
logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`);
}
throw e;
}
if (!await Notes.isVisibleForMe(renote, actor.id)) return "skip: invalid actor for this activity";
logger.info(`Creating the (Re)Note: ${uri}`);
const activityAudience = await parseAudience(actor, activity.to, activity.cc);
await post(actor, {
createdAt: activity.published ? new Date(activity.published) : null,
renote,
visibility: activityAudience.visibility,
visibleUsers: activityAudience.visibleUsers,
uri
});
return "ok";
} finally{
unlock();
}
}
@@ -0,0 +1,61 @@
import Resolver from "../resolver.js";
import { fetchPerson } from "../models/person.js";
import { createBite } from "../../../services/create-bite.js";
import { tickBiteIncoming } from "../../../metrics.js";
import { getNote } from "../../../server/api/common/getters.js";
import { parseUri } from "../db-resolver.js";
export default (async (actor, bite)=>{
if (actor.uri !== bite.actor) {
return "skip: actor uri mismatch";
}
if (bite.id === null) {
return "skip: bite id not specified";
}
const resolver = new Resolver();
const biteActor = await fetchPerson(bite.actor, resolver);
if (biteActor === null) {
return "skip: biteActor is null";
}
const targetParsed = parseUri(bite.target);
if (!targetParsed.local) {
return "skip: target is not local";
}
const targetDbId = targetParsed.id;
const targetPathType = targetParsed.type;
let targetType;
let targetId;
let fallback = false;
if (targetPathType === "users") {
targetType = "user";
targetId = targetDbId;
} else if (targetPathType === "bites") {
targetType = "bite";
targetId = targetDbId;
} else if (targetPathType === "notes") {
targetType = "note";
targetId = targetDbId;
try {
await getNote(targetDbId, actor);
} catch (err) {
if (err.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") {
// note either doesn't exist or the remote user shouldn't be able to access it
fallback = true;
}
}
} else {
fallback = true;
}
if (fallback) {
// fallback for unknown object types
targetType = "user";
if (bite.to !== undefined) {
const to = Array.isArray(bite.to) ? bite.to[0] : bite.to;
targetId = to.split("/").pop();
} else {
return "skip: unknown type missing to field";
}
}
await createBite(biteActor, targetType, targetId, bite.id, bite.published ? new Date(bite.published) : null);
tickBiteIncoming();
return "ok";
});
@@ -0,0 +1,20 @@
import block from "../../../../services/blocking/create.js";
import DbResolver from "../../db-resolver.js";
import { Users } from "../../../../models/index.js";
export default (async (actor, activity)=>{
// ※ There is a block target in activity.object, which should be a local user that exists.
const dbResolver = new DbResolver();
const blockee = await dbResolver.getUserFromApId(activity.object);
if (blockee == null) {
return "skip: blockee not found";
}
if (blockee.host != null) {
return "skip: The user you are trying to block is not a local user";
}
await block(await Users.findOneByOrFail({
id: actor.id
}), await Users.findOneByOrFail({
id: blockee.id
}));
return "ok";
});
@@ -0,0 +1,40 @@
import Resolver from "../../resolver.js";
import createNote from "./note.js";
import { getApId, isPost, getApType } from "../../type.js";
import { apLogger } from "../../logger.js";
import { toArray, concat, unique } from "../../../../prelude/array.js";
const logger = apLogger;
export default (async (actor, activity)=>{
const uri = getApId(activity);
logger.info(`Create: ${uri}`);
// copy audiences between activity <=> object.
if (typeof activity.object === "object") {
const to = unique(concat([
toArray(activity.to),
toArray(activity.object.to)
]));
const cc = unique(concat([
toArray(activity.cc),
toArray(activity.object.cc)
]));
activity.to = to;
activity.cc = cc;
activity.object.to = to;
activity.object.cc = cc;
}
// If there is no attributedTo, use Activity actor.
if (typeof activity.object === "object" && !activity.object.attributedTo) {
activity.object.attributedTo = activity.actor;
}
const resolver = new Resolver();
const object = await resolver.resolve(activity.object).catch((e)=>{
logger.error(`Resolution failed: ${e}`);
throw e;
});
if (isPost(object)) {
return createNote(resolver, actor, object, false, activity);
} else {
logger.warn(`Unknown type: ${getApType(object)}`);
return "skip: unknown create type";
}
});
@@ -0,0 +1,37 @@
import { createNote, fetchNote } from "../../models/note.js";
import { getApId } from "../../type.js";
import { getApLock } from "../../../../misc/app-lock.js";
import { extractDbHost } from "../../../../misc/convert-host.js";
import { StatusError } from "../../../../misc/fetch.js";
/**
* Handle post creation activity
*/ export default async function(resolver, actor, note, silent = false, activity) {
const uri = getApId(note);
if (typeof note === "object") {
if (actor.uri !== note.attributedTo) {
return "skip: actor.uri !== note.attributedTo";
}
if (typeof note.id === "string") {
if (extractDbHost(actor.uri) !== extractDbHost(note.id)) {
return "skip: host in actor.uri !== note.id";
}
} else {
return "skip: note.id is not a string";
}
}
const unlock = await getApLock(uri);
try {
const exist = await fetchNote(note);
if (exist) return "skip: note exists";
await createNote(note, resolver, silent);
return "ok";
} catch (e) {
if (e instanceof StatusError && !e.isRetryable) {
return `skip ${e.statusCode}`;
} else {
throw e;
}
} finally{
unlock();
}
}
@@ -0,0 +1,23 @@
import { apLogger } from "../../logger.js";
import { createDeleteAccountJob } from "../../../../queue/index.js";
import { Users } from "../../../../models/index.js";
const logger = apLogger;
export async function deleteActor(actor, uri) {
logger.info(`Deleting the Actor: ${uri}`);
if (actor.uri !== uri) {
return `skip: delete actor ${actor.uri} !== ${uri}`;
}
const user = await Users.findOneBy({
id: actor.id
});
if (!user) {
return `skip: actor ${actor.id} not found in the local database`;
} else if (user.isDeleted) {
return `skip: user ${user.id} already deleted`;
}
const job = await createDeleteAccountJob(actor);
await Users.update(actor.id, {
isDeleted: true
});
return `ok: queued ${job.name} ${job.id}`;
}
@@ -0,0 +1,42 @@
import { toSingle } from "../../../../prelude/array.js";
import { getApId, isTombstone, validPost, validActor } from "../../type.js";
import deleteNote from "./note.js";
import { deleteActor } from "./actor.js";
/**
* Handle delete activity
*/ export default (async (actor, activity)=>{
if ("actor" in activity && actor.uri !== activity.actor) {
throw new Error("invalid actor");
}
// Type of object to be deleted
let formerType;
if (typeof activity.object === "string") {
// The type is unknown, but it has disappeared
// anyway, so it does not remote resolve
formerType = undefined;
} else {
const object = activity.object;
if (isTombstone(object)) {
formerType = toSingle(object.formerType);
} else {
formerType = toSingle(object.type);
}
}
const uri = getApId(activity.object);
// Even if type is unknown, if actor and object are the same,
// it must be `Person`.
if (!formerType && actor.uri === uri) {
formerType = "Person";
}
// If not, fallback to `Note`.
if (!formerType) {
formerType = "Note";
}
if (validPost.includes(formerType)) {
return await deleteNote(actor, uri);
} else if (validActor.includes(formerType)) {
return await deleteActor(actor, uri);
} else {
return `Unknown type ${formerType}`;
}
});
@@ -0,0 +1,30 @@
import deleteNode from "../../../../services/note/delete.js";
import { apLogger } from "../../logger.js";
import DbResolver from "../../db-resolver.js";
import { getApLock } from "../../../../misc/app-lock.js";
import { deleteMessage } from "../../../../services/messages/delete.js";
const logger = apLogger;
export default async function(actor, uri) {
logger.info(`Deleting the Note: ${uri}`);
const unlock = await getApLock(uri);
try {
const dbResolver = new DbResolver();
const note = await dbResolver.getNoteFromApId(uri);
if (note == null) {
const message = await dbResolver.getMessageFromApId(uri);
if (message == null) return "message not found";
if (message.userId !== actor.id) {
return "The user trying to delete the post is not the post author";
}
await deleteMessage(message);
return "ok: message deleted";
}
if (note.userId !== actor.id) {
return "The user trying to delete the post is not the post author";
}
await deleteNode(actor, note);
return "ok: note deleted";
} finally{
unlock();
}
}
@@ -0,0 +1,26 @@
import config from "../../../../config/index.js";
import { getApIds } from "../../type.js";
import { AbuseUserReports, Users } from "../../../../models/index.js";
import { In } from "typeorm";
import { genId } from "../../../../misc/gen-id.js";
export default (async (actor, activity)=>{
// The object is `(User | Note) | (User | Note) []`, but it cannot be
// matched with all patterns of the DB schema, so the target user is the first
// user and it is stored as a comment.
const uris = getApIds(activity.object);
const userIds = uris.filter((uri)=>uri.startsWith(`${config.url}/users/`)).map((uri)=>uri.split("/").pop());
const users = await Users.findBy({
id: In(userIds)
});
if (users.length < 1) return "skip";
await AbuseUserReports.insert({
id: genId(),
createdAt: new Date(),
targetUserId: users[0].id,
targetUserHost: users[0].host,
reporterId: actor.id,
reporterHost: actor.host,
comment: `${activity.content}\n${JSON.stringify(uris, null, 2)}`
});
return "ok";
});
@@ -0,0 +1,14 @@
import follow from "../../../services/following/create.js";
import DbResolver from "../db-resolver.js";
export default (async (actor, activity)=>{
const dbResolver = new DbResolver();
const followee = await dbResolver.getUserFromApId(activity.object);
if (followee == null) {
return "skip: followee not found";
}
if (followee.host != null) {
return "skip: user you are trying to follow is not a local user";
}
await follow(actor, followee, activity.id);
return "ok";
});
@@ -0,0 +1,80 @@
import { isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isFlag, isMove, getApId, isBite, isQuoteRequest } from "../type.js";
import { apLogger } from "../logger.js";
import create from "./create/index.js";
import performDeleteActivity from "./delete/index.js";
import performUpdateActivity from "./update/index.js";
import { performReadActivity } from "./read.js";
import follow from "./follow.js";
import undo from "./undo/index.js";
import like from "./like.js";
import announce from "./announce/index.js";
import accept from "./accept/index.js";
import reject from "./reject/index.js";
import add from "./add/index.js";
import remove from "./remove/index.js";
import block from "./block/index.js";
import flag from "./flag/index.js";
import move from "./move/index.js";
import bite from "./bite.js";
import quoteRequest from "./quote-request.js";
import { extractDbHost } from "../../../misc/convert-host.js";
import { shouldBlockInstance } from "../../../misc/should-block-instance.js";
export async function performActivity(actor, activity) {
if (isCollectionOrOrderedCollection(activity)) {
apLogger.debug('Refusing to ingest collection as activity');
return "skip: activity is collection";
} else {
return await performOneActivity(actor, activity);
}
}
async function performOneActivity(actor, activity) {
if (actor.isSuspended) return "skip: actor suspended";
if (typeof activity.id !== "undefined") {
const host = extractDbHost(getApId(activity));
if (await shouldBlockInstance(host)) return "skip: instance blocked";
}
if (isCreate(activity)) {
return await create(actor, activity);
} else if (isDelete(activity)) {
return await performDeleteActivity(actor, activity);
} else if (isUpdate(activity)) {
return await performUpdateActivity(actor, activity);
} else if (isRead(activity)) {
return await performReadActivity(actor, activity);
} else if (isFollow(activity)) {
return await follow(actor, activity);
} else if (isAccept(activity)) {
return await accept(actor, activity);
} else if (isReject(activity)) {
return await reject(actor, activity);
} else if (isAdd(activity)) {
return await add(actor, activity).catch((err)=>{
apLogger.error(err);
return `skip: ${err}`;
});
} else if (isRemove(activity)) {
return await remove(actor, activity).catch((err)=>{
apLogger.error(err);
return `skip: ${err}`;
});
} else if (isAnnounce(activity)) {
return await announce(actor, activity);
} else if (isLike(activity)) {
return await like(actor, activity);
} else if (isUndo(activity)) {
return await undo(actor, activity);
} else if (isBlock(activity)) {
return await block(actor, activity);
} else if (isFlag(activity)) {
return await flag(actor, activity);
} else if (isMove(activity)) {
return await move(actor, activity);
} else if (isBite(activity)) {
return await bite(actor, activity);
} else if (isQuoteRequest(activity)) {
return await quoteRequest(actor, activity);
} else {
apLogger.warn(`unrecognized activity type: ${activity.type}`);
return `skip: unrecognized activity type: ${activity.type}`;
}
}
@@ -0,0 +1,16 @@
import { getApId } from "../type.js";
import create from "../../../services/note/reaction/create.js";
import { fetchNote, extractEmojis } from "../models/note.js";
export default (async (actor, activity)=>{
const targetUri = getApId(activity.object);
const note = await fetchNote(targetUri);
if (!note) return `skip: target note not found ${targetUri}`;
await extractEmojis(activity.tag || [], actor.host).catch(()=>null);
return await create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch((e)=>{
if (e.id === "51c42bb4-931a-456b-bff7-e5a8a70dd298") {
return "skip: already reacted";
} else {
throw e;
}
}).then(()=>"ok");
});
@@ -0,0 +1,55 @@
import { Followings, Users } from "../../../../models/index.js";
import { resolvePerson, updatePerson } from "../../models/person.js";
import create from "../../../../services/following/create.js";
import deleteFollowing from "../../../../services/following/delete.js";
import { getApHrefNullable } from "../../type.js";
export default (async (actor, activity)=>{
// ※ There is a block target in activity.object, which should be a local user that exists.
// fetch the new and old accounts
const targetUri = getApHrefNullable(activity.target);
if (!targetUri) return "move: target uri is null";
let new_acc = await resolvePerson(targetUri);
if (!actor.uri) return "move: actor uri is null";
let old_acc = await resolvePerson(actor.uri);
// update them if they're remote
if (new_acc.uri) await updatePerson(new_acc.uri);
if (old_acc.uri) await updatePerson(old_acc.uri);
// retrieve updated users
new_acc = await resolvePerson(targetUri);
old_acc = await resolvePerson(actor.uri);
// check if alsoKnownAs of the new account is valid
let isValidMove = true;
if (old_acc.uri) {
if (!new_acc.alsoKnownAs?.includes(old_acc.uri)) {
isValidMove = false;
}
} else if (!new_acc.alsoKnownAs?.includes(old_acc.id)) {
isValidMove = false;
}
if (!isValidMove) {
return "skip: accounts invalid";
}
// add target uri to movedToUri in order to indicate that the user has moved
await Users.update(old_acc.id, {
movedToUri: targetUri
});
// follow the new account and unfollow the old one
const followings = await Followings.findBy({
followeeId: old_acc.id
});
followings.forEach(async (following)=>{
// If follower is local
if (!following.followerHost) {
try {
const follower = await Users.findOneBy({
id: following.followerId
});
if (!follower) return;
await create(follower, new_acc);
await deleteFollowing(follower, old_acc);
} catch {
/* empty */ }
}
});
return "ok";
});
@@ -0,0 +1,41 @@
import { resolveNote } from "../models/note.js";
import renderAcceptQuoteRequest from "../renderer/accept-quote-request.js";
import { deliverToUser } from "../deliver-manager.js";
import { renderActivity } from "../renderer/index.js";
import { parseUri } from "../db-resolver.js";
import { InteractionStamps, Notes } from "../../../models/index.js";
import { genId } from "../../../misc/gen-id.js";
export default (async (actor, activity)=>{
const localParsed = parseUri(activity.object);
if (!localParsed.local) return "skip: local note not local";
const [note, targetNote] = await Promise.all([
resolveNote(activity.instrument),
Notes.findOneBy({
id: localParsed.id
})
]);
if (note === null) return "skip: note not found";
if (note.userId !== actor.id) return "skip: actor is requesting authorization for a quote they didn't make";
if (targetNote === null) return "skip: target note not found";
if (!await Notes.isVisibleForMe(targetNote, actor.id)) return "skip: target note is not visible for remote user";
let stamp = await InteractionStamps.findOneBy({
noteId: note.id,
targetNoteId: targetNote.id
});
if (stamp === null) {
stamp = {
id: genId(),
type: "quote",
noteId: note.id,
targetNoteId: targetNote.id
};
await InteractionStamps.insert(stamp);
}
stamp.note = note;
stamp.targetNote = targetNote;
await deliverToUser({
id: targetNote.userId,
host: null
}, renderActivity(await renderAcceptQuoteRequest(activity, stamp)), actor);
return "ok";
});
@@ -0,0 +1,24 @@
import { getApId } from "../type.js";
import { isSelfHost, extractDbHost } from "../../../misc/convert-host.js";
import { MessagingMessages } from "../../../models/index.js";
import { readUserMessagingMessage } from "../../../server/api/common/read-messaging-message.js";
export const performReadActivity = async (actor, activity)=>{
const id = await getApId(activity.object);
if (!isSelfHost(extractDbHost(id))) {
return `skip: Read to foreign host (${id})`;
}
const messageId = id.split("/").pop();
const message = await MessagingMessages.findOneBy({
id: messageId
});
if (message == null) {
return "skip: message not found";
}
if (actor.id !== message.recipientId) {
return "skip: actor is not a message recipient";
}
await readUserMessagingMessage(message.recipientId, message.userId, [
message.id
]);
return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`;
};
@@ -0,0 +1,22 @@
import { remoteReject } from "../../../../services/following/reject.js";
import DbResolver from "../../db-resolver.js";
import { relayRejected } from "../../../../services/relay.js";
import { Users } from "../../../../models/index.js";
export default (async (actor, activity)=>{
// ※ `activity.actor` must be an existing local user, since `activity` is a follow request thrown from us.
const dbResolver = new DbResolver();
const follower = await dbResolver.getUserFromApId(activity.actor);
if (follower == null) {
return "skip: follower not found";
}
if (!Users.isLocalUser(follower)) {
return "skip: follower is not a local user";
}
// relay
const match = activity.id?.match(/follow-relay\/(\w+)/);
if (match) {
return await relayRejected(match[1]);
}
await remoteReject(actor, follower);
return "ok";
});
@@ -0,0 +1,16 @@
import Resolver from "../../resolver.js";
import rejectFollow from "./follow.js";
import { isFollow, getApType } from "../../type.js";
import { apLogger } from "../../logger.js";
const logger = apLogger;
export default (async (actor, activity)=>{
const uri = activity.id || activity;
logger.info(`Reject: ${uri}`);
const resolver = new Resolver();
const object = await resolver.resolve(activity.object).catch((e)=>{
logger.error(`Resolution failed: ${e}`);
throw e;
});
if (isFollow(object)) return await rejectFollow(actor, object);
return `skip: Unknown Reject type: ${getApType(object)}`;
});
@@ -0,0 +1,17 @@
import { fetchNote } from "../../models/note.js";
import { removePinned } from "../../../../services/i/pin.js";
export default (async (actor, activity)=>{
if ("actor" in activity && actor.uri !== activity.actor) {
throw new Error("invalid actor");
}
if (activity.target == null) {
throw new Error("target is null");
}
if (activity.target === actor.featured) {
const note = await fetchNote(activity.object);
if (note == null) return "skip: note not found"; // not pinned either way
await removePinned(actor, note.id);
return "ok";
}
throw new Error(`unknown target: ${activity.target}`);
});
@@ -0,0 +1,19 @@
import unfollow from "../../../../services/following/delete.js";
import { Followings } from "../../../../models/index.js";
import DbResolver from "../../db-resolver.js";
export default (async (actor, activity)=>{
const dbResolver = new DbResolver();
const follower = await dbResolver.getUserFromApId(activity.object);
if (follower == null) {
return "skip: follower not found";
}
const following = await Followings.findOneBy({
followerId: follower.id,
followeeId: actor.id
});
if (following) {
await unfollow(follower, actor);
return "ok: unfollowed";
}
return "skip: skip: not followed";
});
@@ -0,0 +1,13 @@
import { Notes } from "../../../../models/index.js";
import { getApId } from "../../type.js";
import deleteNote from "../../../../services/note/delete.js";
export const undoAnnounce = async (actor, activity)=>{
const uri = getApId(activity);
const note = await Notes.findOneBy({
uri,
userId: actor.id
});
if (!note) return "skip: no such Announce";
await deleteNote(actor, note);
return "ok: deleted";
};
@@ -0,0 +1,17 @@
import unblock from "../../../../services/blocking/delete.js";
import DbResolver from "../../db-resolver.js";
import { Users } from "../../../../models/index.js";
export default (async (actor, activity)=>{
const dbResolver = new DbResolver();
const blockee = await dbResolver.getUserFromApId(activity.object);
if (blockee == null) {
return "skip: blockee not found";
}
if (blockee.host != null) {
return "skip: The user you are trying to unblock is not a local user";
}
await unblock(await Users.findOneByOrFail({
id: actor.id
}), blockee);
return "ok";
});
@@ -0,0 +1,31 @@
import unfollow from "../../../../services/following/delete.js";
import cancelRequest from "../../../../services/following/requests/cancel.js";
import { FollowRequests, Followings } from "../../../../models/index.js";
import DbResolver from "../../db-resolver.js";
export default (async (actor, activity)=>{
const dbResolver = new DbResolver();
const followee = await dbResolver.getUserFromApId(activity.object);
if (followee == null) {
return "skip: followee not found";
}
if (followee.host != null) {
return "skip: The user you are trying to unfollow is not a local user";
}
const req = await FollowRequests.findOneBy({
followerId: actor.id,
followeeId: followee.id
});
const following = await Followings.findOneBy({
followerId: actor.id,
followeeId: followee.id
});
if (req) {
await cancelRequest(followee, actor);
return "ok: follow request canceled";
}
if (following) {
await unfollow(actor, followee);
return "ok: unfollowed";
}
return "skip: Not requested or followed";
});
@@ -0,0 +1,27 @@
import { isFollow, isBlock, isLike, isAnnounce, getApType, isAccept } from "../../type.js";
import unfollow from "./follow.js";
import unblock from "./block.js";
import undoLike from "./like.js";
import undoAccept from "./accept.js";
import { undoAnnounce } from "./announce.js";
import Resolver from "../../resolver.js";
import { apLogger } from "../../logger.js";
const logger = apLogger;
export default (async (actor, activity)=>{
if ("actor" in activity && actor.uri !== activity.actor) {
throw new Error("invalid actor");
}
const uri = activity.id || activity;
logger.info(`Undo: ${uri}`);
const resolver = new Resolver();
const object = await resolver.resolve(activity.object).catch((e)=>{
logger.error(`Resolution failed: ${e}`);
throw e;
});
if (isFollow(object)) return await unfollow(actor, object);
if (isBlock(object)) return await unblock(actor, object);
if (isLike(object)) return await undoLike(actor, object);
if (isAnnounce(object)) return await undoAnnounce(actor, object);
if (isAccept(object)) return await undoAccept(actor, object);
return `skip: unknown object type ${getApType(object)}`;
});
@@ -0,0 +1,15 @@
import { getApId } from "../../type.js";
import deleteReaction from "../../../../services/note/reaction/delete.js";
import { fetchNote } from "../../models/note.js";
/**
* Process Undo.Like activity
*/ export default (async (actor, activity)=>{
const targetUri = getApId(activity.object);
const note = await fetchNote(targetUri);
if (!note) return `skip: target note not found ${targetUri}`;
await deleteReaction(actor, note).catch((e)=>{
if (e.id === "60527ec9-b4cb-4a88-a6bd-32d3ad26817d") return;
throw e;
});
return "ok";
});
@@ -0,0 +1,41 @@
import { getApId } from "../../type.js";
import { getApType, isActor } from "../../type.js";
import { apLogger } from "../../logger.js";
import { updateNote } from "../../models/note.js";
import Resolver from "../../resolver.js";
import { updatePerson } from "../../models/person.js";
/**
* Handler for the Update activity
*/ export default (async (actor, activity)=>{
if (actor.uri == null || actor.uri !== getApId(activity.actor)) {
return "skip: invalid actor";
}
apLogger.debug("Update");
const resolver = new Resolver();
const object = await resolver.resolve(activity.object).catch((e)=>{
apLogger.error(`Resolution failed: ${e}`);
throw e;
});
if (isActor(object)) {
if (actor.uri !== object.id) {
return "skip: actor id mismatch";
}
await updatePerson(actor.uri, resolver, object);
return "ok: Person updated";
}
const objectType = getApType(object);
switch(objectType){
case "Question":
case "Note":
case "Article":
case "Document":
case "Page":
let failed = false;
await updateNote(object, actor, resolver).catch((e)=>{
failed = true;
});
return failed ? "skip: Note update failed" : "ok: Note updated";
default:
return `skip: Unknown type: ${objectType}`;
}
});