Fixed 267U.pre2
This commit is contained in:
@@ -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}`;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user