diff --git a/src/activitypub/feps.js b/src/activitypub/feps.js index f8169cf621..8dca8d6904 100644 --- a/src/activitypub/feps.js +++ b/src/activitypub/feps.js @@ -3,7 +3,6 @@ const nconf = require('nconf'); const posts = require('../posts'); -const utils = require('../utils'); const activitypub = module.parent.exports; const Feps = module.exports; @@ -53,36 +52,3 @@ Feps.announce = async function announce(id, activity) { object: activity, }); }; - -Feps.announceObject = async function announceObject(id) { - let localId; - if (String(id).startsWith(nconf.get('url'))) { - ({ id: localId } = await activitypub.helpers.resolveLocalId(id)); - } - const cid = await posts.getCidByPid(localId || id); - if (cid === -1) { - return; - } - - const followers = await activitypub.notes.getCategoryFollowers(cid); - if (!followers.length) { - return; - } - - let author = await posts.getPostField(id, 'uid'); - if (utils.isNumber(author)) { - author = `${nconf.get('url')}/uid/${author}`; - } else if (!author.startsWith(nconf.get('url'))) { - followers.unshift(author); - } - - activitypub.helpers.log(`[activitypub/inbox.announce(1b12)] Announcing object (${id}) to followers of cid ${cid}`); - await activitypub.send('cid', cid, followers, { - id: `${nconf.get('url')}/post/${encodeURIComponent(id)}#activity/announce/${Date.now()}`, - type: 'Announce', - actor: `${nconf.get('url')}/category/${cid}`, - to: [`${nconf.get('url')}/category/${cid}/followers`], - cc: [author, activitypub._constants.publicAddress], - object: utils.isNumber(id) ? `${nconf.get('url')}/post/${id}` : id, - }); -}; diff --git a/src/activitypub/mocks.js b/src/activitypub/mocks.js index 9ca0968b68..b503887f1b 100644 --- a/src/activitypub/mocks.js +++ b/src/activitypub/mocks.js @@ -754,6 +754,38 @@ Mocks.notes.private = async ({ messageObj }) => { return object; }; +Mocks.activities = {}; + +Mocks.activities.create = async (pid, uid, post) => { + // Local objects only, post optional + if (!utils.isNumber(pid)) { + throw new Error('[[error:invalid-pid]]'); + } + + if (!post) { + post = (await posts.getPostSummaryByPids([pid], uid, { stripTags: false })).pop(); + if (!post) { + throw new Error('[[error:invalid-pid]]'); + } + } + + const object = await activitypub.mocks.notes.public(post); + const { to, cc, targets } = await activitypub.buildRecipients(object, { pid, uid: post.user.uid }); + object.to = to; + object.cc = cc; + + const activity = { + id: `${object.id}#activity/create/${Date.now()}`, + type: 'Create', + actor: object.attributedTo, + to, + cc, + object, + }; + + return { activity, targets }; +}; + Mocks.tombstone = async properties => ({ '@context': 'https://www.w3.org/ns/activitystreams', type: 'Tombstone', diff --git a/src/api/activitypub.js b/src/api/activitypub.js index 0632219e66..e91e27f712 100644 --- a/src/api/activitypub.js +++ b/src/api/activitypub.js @@ -124,23 +124,11 @@ activitypubApi.create.note = enabledCheck(async (caller, { pid, post }) => { return; } - const object = await activitypub.mocks.notes.public(post); - const { to, cc, targets } = await activitypub.buildRecipients(object, { pid, uid: post.user.uid }); - object.to = to; - object.cc = cc; - - const payload = { - id: `${object.id}#activity/create/${Date.now()}`, - type: 'Create', - actor: object.attributedTo, - to, - cc, - object, - }; + const { payload: activity, targets } = await activitypub.mocks.activities.create(pid, caller.uid, post); await Promise.all([ - activitypub.send('uid', caller.uid, Array.from(targets), payload), - activitypub.feps.announce(pid, payload), + activitypub.send('uid', caller.uid, Array.from(targets), activity), + activitypub.feps.announce(pid, activity), activitypubApi.add(caller, { pid }), ]); }); diff --git a/src/api/topics.js b/src/api/topics.js index f1e9f9408a..572183a195 100644 --- a/src/api/topics.js +++ b/src/api/topics.js @@ -342,7 +342,8 @@ topicsAPI.move = async (caller, { tid, cid }) => { if (!topicData.deleted) { socketHelpers.sendNotificationToTopicOwner(tid, caller.uid, 'move', 'notifications:moved-your-topic'); activitypubApi.announce.note(caller, { tid }); - activitypub.feps.announceObject(topicData.mainPid); + const { activity } = await activitypub.mocks.activities.create(topicData.mainPid, caller.uid); + await activitypub.feps.announce(topicData.mainPid, activity); } await events.log({ diff --git a/src/topics/fork.js b/src/topics/fork.js index b12c54ecc9..f300961143 100644 --- a/src/topics/fork.js +++ b/src/topics/fork.js @@ -88,9 +88,11 @@ module.exports = function (Topics) { }), db.sortedSetsAdd(['topics:votes', `cid:${cid}:tids:votes`], mainPost.votes, tid), Topics.events.log(fromTid, { type: 'fork', uid, href: `/topic/${tid}` }), - activitypub.feps.announceObject(pids[0]), ]); + const { activity } = await activitypub.mocks.activities.create(pids[0], uid); + await activitypub.feps.announce(pids[0], activity); + plugins.hooks.fire('action:topic.fork', { tid, fromTid, uid }); return await Topics.getTopicData(tid); diff --git a/test/activitypub/feps.js b/test/activitypub/feps.js index 1ec838b557..ff9738a80b 100644 --- a/test/activitypub/feps.js +++ b/test/activitypub/feps.js @@ -22,8 +22,8 @@ describe('FEPs', () => { await install.giveWorldPrivileges(); }); - describe.only('1b12', () => { - describe('announceObject()', () => { + describe('1b12', () => { + describe('announce()', () => { let cid; let uid; let adminUid; @@ -49,7 +49,7 @@ describe('FEPs', () => { }); it('should be called when a topic is moved from uncategorized to another category', async () => { - const { topicData } = await topics.post({ + const { topicData, postData } = await topics.post({ uid, cid: -1, title: utils.generateUUID(), @@ -63,7 +63,13 @@ describe('FEPs', () => { cid, }); - assert.strictEqual(activitypub._sent.size, 1); + assert.strictEqual(activitypub._sent.size, 2); + + const key = Array.from(activitypub._sent.keys())[0]; + const activity = activitypub._sent.get(key); + + assert(activity && activity.object && typeof activity.object === 'object'); + assert.strictEqual(activity.object.id, `${nconf.get('url')}/post/${postData.pid}`); }); it('should be called for a newly forked topic', async () => { @@ -78,17 +84,47 @@ describe('FEPs', () => { topics.reply({ uid, tid, content: utils.generateUUID() }), topics.reply({ uid, tid, content: utils.generateUUID() }), ]); - const forked = await topics.createTopicFromPosts( + await topics.createTopicFromPosts( adminUid, utils.generateUUID(), [reply1Pid, reply2Pid], tid, cid ); - assert.strictEqual(activitypub._sent.size, 1); + assert.strictEqual(activitypub._sent.size, 2); const key = Array.from(activitypub._sent.keys())[0]; const activity = activitypub._sent.get(key); - assert(activity); - assert.strictEqual(activity.object, `${nconf.get('url')}/post/${reply1Pid}`); + assert(activity && activity.object && typeof activity.object === 'object'); + assert.strictEqual(activity.object.id, `${nconf.get('url')}/post/${reply1Pid}`); + }); + + // it('should be called when a post is moved to another topic', async () => { + // + // }); + }); + + describe('announceObject()', () => { + let cid; + let uid; + let adminUid; + + before(async () => { + const name = utils.generateUUID(); + const description = utils.generateUUID(); + ({ cid } = await categories.create({ name, description })); + + adminUid = await user.create({ username: utils.generateUUID() }); + await groups.join('administrators', adminUid); + uid = await user.create({ username: utils.generateUUID() }); + + const { id: followerId, actor } = helpers.mocks.actor(); + activitypub._cache.set(`0;${followerId}`, actor); + user.setCategoryWatchState(followerId, [cid], categories.watchStates.tracking); + + activitypub._sent.clear(); + }); + + afterEach(() => { + activitypub._sent.clear(); }); }); });