kopia lustrzana https://github.com/cloudflare/wildebeest
MOW-140: wip (needs more testing for actual problem)
rodzic
e7bf54985c
commit
2e97b33583
|
@ -4,6 +4,7 @@ import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
|||
import { addObjectInOutbox } from 'wildebeest/backend/src/activitypub/actors/outbox'
|
||||
import { actorURL } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import * as objects from 'wildebeest/backend/src/activitypub/objects'
|
||||
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import * as accept from 'wildebeest/backend/src/activitypub/activities/accept'
|
||||
import { addObjectInInbox } from 'wildebeest/backend/src/activitypub/actors/inbox'
|
||||
import {
|
||||
|
@ -31,6 +32,54 @@ function extractID(domain: string, s: string | URL): string {
|
|||
return s.toString().replace(`https://${domain}/ap/users/`, '')
|
||||
}
|
||||
|
||||
// Find the actor for this recipient
|
||||
async function findActorFromReceipient(db: D1Database, domain: string, recipient: URL): Promise<Actor | null> {
|
||||
if (recipient.hostname !== domain) {
|
||||
// Actor isn't in our instance
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const handle = parseHandle(extractID(domain, recipient))
|
||||
|
||||
const actor = await actors.getActorById(db, actorURL(domain, handle.localPart))
|
||||
if (actor === null) {
|
||||
console.warn(`local actor ${recipient} not found`)
|
||||
return null
|
||||
}
|
||||
|
||||
return actor
|
||||
} catch (err: any) {
|
||||
console.warn('failed to parse handle: ' + recipient)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function findActorsFromCollection(db: D1Database, domain: string, collection: URL): Promise<Array<Actor | null>> {
|
||||
if (collection.hostname !== domain) {
|
||||
console.warn('findActorsFromCollection not implemented yet for collection: ' + collection)
|
||||
return []
|
||||
}
|
||||
|
||||
// Try to resolve the collection assumin it's a followers collection
|
||||
const query = `
|
||||
SELECT actor_id
|
||||
FROM actor_following
|
||||
WHERE target_actor_id=(SELECT id FROM actors WHERE json_extract(properties, '$.followers') = ?1)
|
||||
`
|
||||
|
||||
const { results, success, error } = await db.prepare(query).bind(collection.toString()).all<{ actor_id: string }>()
|
||||
console.log({ results });
|
||||
if (!success) {
|
||||
throw new Error('SQL error: ' + error)
|
||||
}
|
||||
if (!results || results.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
// TODO: suboptimal implementation, resulting in many D1 queries for a large collection
|
||||
return Promise.all(results.map(({ actor_id }) => actors.getActorById(db, new URL(actor_id))))
|
||||
}
|
||||
|
||||
export function makeGetObjectAsId(activity: Activity) {
|
||||
return () => {
|
||||
let url: any = null
|
||||
|
@ -188,30 +237,28 @@ export async function handle(
|
|||
await addObjectInOutbox(db, fromActor, obj, activity.published, target)
|
||||
|
||||
for (let i = 0, len = recipients.length; i < len; i++) {
|
||||
const url = new URL(recipients[i])
|
||||
if (url.hostname !== domain) {
|
||||
console.warn('recipients is not for this instance')
|
||||
continue
|
||||
const recipient = new URL(recipients[i])
|
||||
|
||||
// Recipient is a local actor
|
||||
{
|
||||
const actor = await findActorFromReceipient(db, domain, recipient)
|
||||
|
||||
if (actor !== null) {
|
||||
// FIXME: check if the actor is in the mentions/tags
|
||||
const notifId = await createNotification(db, 'mention', actor, fromActor, obj)
|
||||
await Promise.all([
|
||||
await addObjectInInbox(db, actor, obj),
|
||||
await sendMentionNotification(db, fromActor, actor, notifId, adminEmail, vapidKeys),
|
||||
])
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
const handle = parseHandle(extractID(domain, recipients[i]))
|
||||
if (handle.domain !== null && handle.domain !== domain) {
|
||||
console.warn('activity not for current instance')
|
||||
continue
|
||||
// Recipient is a collection
|
||||
{
|
||||
const actors = await findActorsFromCollection(db, domain, recipient)
|
||||
console.log({ actors, recipient })
|
||||
}
|
||||
|
||||
const person = await actors.getActorById(db, actorURL(domain, handle.localPart))
|
||||
if (person === null) {
|
||||
console.warn(`person ${recipients[i]} not found`)
|
||||
continue
|
||||
}
|
||||
|
||||
// FIXME: check if the actor mentions the person
|
||||
const notifId = await createNotification(db, 'mention', person, fromActor, obj)
|
||||
await Promise.all([
|
||||
await addObjectInInbox(db, person, obj),
|
||||
await sendMentionNotification(db, fromActor, person, notifId, adminEmail, vapidKeys),
|
||||
])
|
||||
}
|
||||
|
||||
break
|
||||
|
|
|
@ -3,11 +3,12 @@ import { createPublicNote } from 'wildebeest/backend/src/activitypub/objects/not
|
|||
import type { JWK } from 'wildebeest/backend/src/webpush/jwk'
|
||||
import { strict as assert } from 'node:assert/strict'
|
||||
import { cacheObject, getObjectById } from 'wildebeest/backend/src/activitypub/objects/'
|
||||
import { addFollowing } from 'wildebeest/backend/src/mastodon/follow'
|
||||
import { addFollowing, acceptFollowing } from 'wildebeest/backend/src/mastodon/follow'
|
||||
import * as activityHandler from 'wildebeest/backend/src/activitypub/activities/handle'
|
||||
import { createPerson } from 'wildebeest/backend/src/activitypub/actors'
|
||||
import { ObjectsRow } from 'wildebeest/backend/src/types/objects'
|
||||
import { originalObjectIdSymbol } from 'wildebeest/backend/src/activitypub/objects'
|
||||
import { getHomeTimeline } from 'wildebeest/backend/src/mastodon/timeline'
|
||||
|
||||
const adminEmail = 'admin@example.com'
|
||||
const domain = 'cloudflare.com'
|
||||
|
@ -393,6 +394,44 @@ describe('ActivityPub', () => {
|
|||
)
|
||||
assert.equal(name, 'Dr Evil')
|
||||
})
|
||||
|
||||
test.only('Note sent to follower collection adds to Actor timelines', async () => {
|
||||
const db = await makeDB()
|
||||
const remoteActor = "https://example.com/users/remote-actor"
|
||||
const noteId = 'https://example.com/note/1'
|
||||
|
||||
globalThis.fetch = async (input: RequestInfo) => {
|
||||
throw new Error('unexpected request to ' + input)
|
||||
}
|
||||
|
||||
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
||||
|
||||
// actor2 follows remoteActor
|
||||
await db
|
||||
.prepare(
|
||||
'INSERT INTO actor_following (id, actor_id, target_actor_id, target_actor_acct) VALUES (?1, ?2, ?3, \'accepted\')'
|
||||
)
|
||||
.bind(
|
||||
'id1',
|
||||
actor2.id.toString(),
|
||||
remoteActor,
|
||||
'not needed',
|
||||
)
|
||||
.run()
|
||||
|
||||
// actor send the notes to its followers
|
||||
const activity = {
|
||||
type: 'Create',
|
||||
actor: remoteActor,
|
||||
to: ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
cc: [remoteActor + '/followers'],
|
||||
object: noteId,
|
||||
}
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
const timeline = await getHomeTimeline(domain, db, actor2)
|
||||
console.log({ timeline });
|
||||
})
|
||||
})
|
||||
|
||||
describe('Update', () => {
|
||||
|
|
Ładowanie…
Reference in New Issue