ActivityPub Social Manager, and many updates to Search functionality
* Manage Following/Followers (WIP, bugs, some missing functionality) * Search for Actors (WIP, some bugs)
This commit is contained in:
parent
2aec375bee
commit
e8c42a9b2e
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -484,16 +484,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
activityPubFollowingManager: {
|
||||
config: {
|
||||
selectedActorInfoFormat: "|00|15{preferredUsername}\n|02{name}\n{summary}"
|
||||
statusIndicatorEnabled: "|00|10√"
|
||||
staticIndicatorDisabled: "|00|12X"
|
||||
}
|
||||
0: {
|
||||
mci: {
|
||||
VM1: {
|
||||
height: 15
|
||||
width: 30
|
||||
itemFormat: "|00|03{subject}|00 {statusIndicator}"
|
||||
focusItemFormat: "|00|19|15{subject}|00 {statusIndicator}"
|
||||
}
|
||||
MT2: {
|
||||
height: 15
|
||||
width: 32
|
||||
}
|
||||
TM3: {
|
||||
focusTextStyle: first upper
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// :TODO: move this to the right area, rename, etc.
|
||||
mainMenuActivityPubActorSearch: {
|
||||
config: {
|
||||
followingIndicator: "|00|14> |10Following|08!|14 <"
|
||||
notFollowingIndicator: "|00|14> |02Not Following|14 <"
|
||||
viewInfoFormat10: "{actorFollowingIndicator}"
|
||||
}
|
||||
0: {
|
||||
mci: {
|
||||
TL1: {
|
||||
width: 70
|
||||
}
|
||||
BT2: {
|
||||
width: 20
|
||||
focusTextStyle: upper
|
||||
submit: true
|
||||
}
|
||||
}
|
||||
|
@ -524,14 +550,9 @@
|
|||
height: 3
|
||||
mode: preview
|
||||
}
|
||||
BT8: {
|
||||
focusTextStyle: upper
|
||||
submit: true
|
||||
}
|
||||
BT9: {
|
||||
text: back
|
||||
focusTextStyle: upper
|
||||
submit: true
|
||||
TL10: {
|
||||
width: 24
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ const { queryWebFinger } = require('../webfinger');
|
|||
const EnigAssert = require('../enigma_assert');
|
||||
const ActivityPubSettings = require('./settings');
|
||||
const ActivityPubObject = require('./object');
|
||||
const { ActivityStreamMediaType } = require('./const');
|
||||
const { ActivityStreamMediaType, Collections } = require('./const');
|
||||
const apDb = require('../database').dbs.activitypub;
|
||||
const Config = require('../config').get;
|
||||
|
||||
|
@ -73,7 +73,12 @@ module.exports = class Actor extends ActivityPubObject {
|
|||
}
|
||||
|
||||
static get WellKnownLinkTypes() {
|
||||
return ['inbox', 'outbox', 'following', 'followers'];
|
||||
return [
|
||||
Collections.Inbox,
|
||||
Collections.Outbox,
|
||||
Collections.Following,
|
||||
Collections.Followers,
|
||||
];
|
||||
}
|
||||
|
||||
static fromLocalUser(user, webServer, cb) {
|
||||
|
|
|
@ -3,11 +3,15 @@ const { Errors } = require('../enig_error');
|
|||
const Actor = require('../activitypub/actor');
|
||||
const moment = require('moment');
|
||||
const { htmlToMessageBody } = require('./util');
|
||||
const { Collections } = require('./const');
|
||||
const Collection = require('./collection');
|
||||
const EnigAssert = require('../enigma_assert');
|
||||
const { sendFollowRequest, sendUnfollowRequest } = require('./follow_util');
|
||||
const { getServer } = require('../listening_server');
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
const { get, truncate, isEmpty } = require('lodash');
|
||||
const { get, isEmpty, isObject, cloneDeep } = require('lodash');
|
||||
|
||||
exports.moduleInfo = {
|
||||
name: 'ActivityPub Actor Search',
|
||||
|
@ -22,8 +26,7 @@ const FormIds = {
|
|||
|
||||
const MciViewIds = {
|
||||
main: {
|
||||
searchUrl: 1,
|
||||
searchButton: 2,
|
||||
searchQuery: 1,
|
||||
},
|
||||
view: {
|
||||
userName: 1,
|
||||
|
@ -33,8 +36,8 @@ const MciViewIds = {
|
|||
numberFollowers: 5,
|
||||
numberFollowing: 6,
|
||||
summary: 7,
|
||||
followButton: 8,
|
||||
cancelButton: 9,
|
||||
|
||||
customRangeStart: 10,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -47,27 +50,34 @@ exports.getModule = class ActivityPubActorSearch extends MenuModule {
|
|||
});
|
||||
|
||||
this.menuMethods = {
|
||||
submit: (formData, extraArgs, cb) => {
|
||||
switch (formData.submitId) {
|
||||
case MciViewIds.main.searchUrl: {
|
||||
search: (formData, extraArgs, cb) => {
|
||||
return this._search(formData.value, cb);
|
||||
}
|
||||
case MciViewIds.main.searchButton: {
|
||||
return this._search(formData.value, cb);
|
||||
}
|
||||
|
||||
default:
|
||||
cb(
|
||||
Errors.UnexpectedState(
|
||||
`Unexpected submitId: ${formData.submitId}`
|
||||
)
|
||||
},
|
||||
toggleFollowKeyPressed: (formData, extraArgs, cb) => {
|
||||
return this._toggleFollowStatus(err => {
|
||||
if (err) {
|
||||
this.client.log.error(
|
||||
{ error: err.message },
|
||||
'Failed to toggle follow status'
|
||||
);
|
||||
}
|
||||
return cb(err);
|
||||
});
|
||||
},
|
||||
backKeyPressed: (formData, extraArgs, cb) => {
|
||||
return this._displayMainPage(true, cb);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
initSequence() {
|
||||
this.webServer = getServer('codes.l33t.enigma.web.server');
|
||||
if (!this.webServer) {
|
||||
this.client.log('Could not get Web server');
|
||||
return this.prevMenu();
|
||||
}
|
||||
this.webServer = this.webServer.instance;
|
||||
|
||||
async.series(
|
||||
[
|
||||
callback => {
|
||||
|
@ -84,7 +94,7 @@ exports.getModule = class ActivityPubActorSearch extends MenuModule {
|
|||
}
|
||||
|
||||
_search(values, cb) {
|
||||
const searchString = values['searchUrl'].trim();
|
||||
const searchString = values.searchQuery.trim();
|
||||
//TODO: Handle empty searchString
|
||||
Actor.fromId(searchString, (err, remoteActor) => {
|
||||
if (err) {
|
||||
|
@ -95,11 +105,15 @@ exports.getModule = class ActivityPubActorSearch extends MenuModule {
|
|||
// TODO: Add error to page for failure to find actor
|
||||
return this._displayMainPage(true, cb);
|
||||
}
|
||||
return this._displayListScreen(remoteActor, cb);
|
||||
|
||||
this.selectedActorInfo = remoteActor;
|
||||
return this._displayViewPage(cb);
|
||||
});
|
||||
}
|
||||
|
||||
_displayListScreen(remoteActor, cb) {
|
||||
_displayViewPage(cb) {
|
||||
EnigAssert(isObject(this.selectedActorInfo), 'No Actor selected!');
|
||||
|
||||
async.series(
|
||||
[
|
||||
callback => {
|
||||
|
@ -122,59 +136,65 @@ exports.getModule = class ActivityPubActorSearch extends MenuModule {
|
|||
callback => {
|
||||
return this.validateMCIByViewIds(
|
||||
'view',
|
||||
Object.values(MciViewIds.view),
|
||||
Object.values(MciViewIds.view).filter(
|
||||
id => id !== MciViewIds.view.customRangeStart
|
||||
),
|
||||
callback
|
||||
);
|
||||
},
|
||||
callback => {
|
||||
this._updateCollectionItemCount(Collections.Following, () => {
|
||||
return this._updateCollectionItemCount(
|
||||
Collections.Followers,
|
||||
callback
|
||||
);
|
||||
});
|
||||
},
|
||||
callback => {
|
||||
const v = id => this.getView('view', id);
|
||||
|
||||
const nameView = v(MciViewIds.view.userName);
|
||||
nameView.setText(
|
||||
truncate(remoteActor.preferredUsername, {
|
||||
length: nameView.getWidth(),
|
||||
})
|
||||
);
|
||||
nameView.setText(this.selectedActorInfo.preferredUsername);
|
||||
|
||||
const fullNameView = v(MciViewIds.view.fullName);
|
||||
fullNameView.setText(
|
||||
truncate(remoteActor.name, { length: fullNameView.getWidth() })
|
||||
);
|
||||
fullNameView.setText(this.selectedActorInfo.name);
|
||||
|
||||
const datePublishedView = v(MciViewIds.view.datePublished);
|
||||
if (isEmpty(remoteActor.published)) {
|
||||
if (isEmpty(this.selectedActorInfo.published)) {
|
||||
datePublishedView.setText('Not available.');
|
||||
} else {
|
||||
const publishedDate = moment(remoteActor.published);
|
||||
const publishedDate = moment(this.selectedActorInfo.published);
|
||||
datePublishedView.setText(
|
||||
publishedDate.format(this.getDateFormat())
|
||||
);
|
||||
}
|
||||
|
||||
const manualFollowersView = v(MciViewIds.view.manualFollowers);
|
||||
manualFollowersView.setText(remoteActor.manuallyApprovesFollowers);
|
||||
manualFollowersView.setText(
|
||||
this.selectedActorInfo.manuallyApprovesFollowers
|
||||
);
|
||||
|
||||
const followerCountView = v(MciViewIds.view.numberFollowers);
|
||||
this._updateViewWithCollectionItemCount(
|
||||
remoteActor.followers,
|
||||
followerCountView
|
||||
followerCountView.setText(
|
||||
this.selectedActorInfo._followersCount > -1
|
||||
? this.selectedActorInfo._followersCount
|
||||
: '--'
|
||||
);
|
||||
|
||||
const followingCountView = v(MciViewIds.view.numberFollowing);
|
||||
this._updateViewWithCollectionItemCount(
|
||||
remoteActor.following,
|
||||
followingCountView
|
||||
followingCountView.setText(
|
||||
this.selectedActorInfo._followingCount > -1
|
||||
? this.selectedActorInfo._followingCount
|
||||
: '--'
|
||||
);
|
||||
|
||||
const summaryView = v(MciViewIds.view.summary);
|
||||
summaryView.setText(htmlToMessageBody(remoteActor.summary));
|
||||
summaryView.setText(
|
||||
htmlToMessageBody(this.selectedActorInfo.summary)
|
||||
);
|
||||
summaryView.redraw();
|
||||
|
||||
const followButtonView = v(MciViewIds.view.followButton);
|
||||
// TODO: FIXME: Real status
|
||||
followButtonView.setText('follow');
|
||||
|
||||
return callback(null);
|
||||
return this._setFollowStatus(callback);
|
||||
},
|
||||
],
|
||||
err => {
|
||||
|
@ -183,6 +203,102 @@ exports.getModule = class ActivityPubActorSearch extends MenuModule {
|
|||
);
|
||||
}
|
||||
|
||||
_setFollowStatus(cb) {
|
||||
Collection.ownedObjectByNameAndId(
|
||||
Collections.Following,
|
||||
this.client.user,
|
||||
this.selectedActorInfo.id,
|
||||
(err, followingActorEntry) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
this.selectedActorInfo._isFollowing = followingActorEntry ? true : false;
|
||||
this.selectedActorInfo._followingIndicator =
|
||||
this._getFollowingIndicator();
|
||||
|
||||
this.updateCustomViewTextsWithFilter(
|
||||
'view',
|
||||
MciViewIds.view.customRangeStart,
|
||||
this._getCustomInfoFormatObject()
|
||||
);
|
||||
|
||||
return cb(null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_toggleFollowStatus(cb) {
|
||||
// catch early key presses
|
||||
if (!this.selectedActorInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedActorInfo._isFollowing = !this.selectedActorInfo._isFollowing;
|
||||
this.selectedActorInfo._followingIndicator = this._getFollowingIndicator();
|
||||
|
||||
const finish = e => {
|
||||
this.updateCustomViewTextsWithFilter(
|
||||
'view',
|
||||
MciViewIds.view.customRangeStart,
|
||||
this._getCustomInfoFormatObject()
|
||||
);
|
||||
|
||||
return cb(e);
|
||||
};
|
||||
|
||||
const actor = this._getSelectedActor(); // actor info -> actor
|
||||
return this.selectedActorInfo._isFollowing
|
||||
? sendFollowRequest(this.client.user, actor, this.webServer, finish)
|
||||
: sendUnfollowRequest(this.client.user, actor, this.webServer, finish);
|
||||
}
|
||||
|
||||
_getSelectedActor() {
|
||||
const actor = cloneDeep(this.selectedActorInfo);
|
||||
|
||||
// nuke our added properties
|
||||
delete actor._isFollowing;
|
||||
delete actor._followingIndicator;
|
||||
delete actor._followingCount;
|
||||
delete actor._followersCount;
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
_getFollowingIndicator() {
|
||||
return this.selectedActorInfo._isFollowing
|
||||
? this.config.followingIndicator || 'Following'
|
||||
: this.config.notFollowingIndicator || 'Not following';
|
||||
}
|
||||
|
||||
_getCustomInfoFormatObject() {
|
||||
const formatObj = {
|
||||
followingCount: this.selectedActorInfo._followingCount,
|
||||
followerCount: this.selectedActorInfo._followersCount,
|
||||
};
|
||||
|
||||
const v = f => {
|
||||
return this.selectedActorInfo[f] || '';
|
||||
};
|
||||
|
||||
Object.assign(formatObj, {
|
||||
actorId: v('id'),
|
||||
actorSubject: v('subject'),
|
||||
actorType: v('type'),
|
||||
actorName: v('name'),
|
||||
actorSummary: v('summary'),
|
||||
actorPreferredUsername: v('preferredUsername'),
|
||||
actorUrl: v('url'),
|
||||
actorImage: v('image'),
|
||||
actorIcon: v('icon'),
|
||||
actorFollowing: this.selectedActorInfo._isFollowing,
|
||||
actorFollowingIndicator: v('_followingIndicator'),
|
||||
text: v('name'),
|
||||
});
|
||||
|
||||
return formatObj;
|
||||
}
|
||||
|
||||
_displayMainPage(clearScreen, cb) {
|
||||
async.series(
|
||||
[
|
||||
|
@ -208,17 +324,20 @@ exports.getModule = class ActivityPubActorSearch extends MenuModule {
|
|||
);
|
||||
}
|
||||
|
||||
_updateViewWithCollectionItemCount(collectionUrl, view) {
|
||||
_updateCollectionItemCount(collectionName, cb) {
|
||||
const collectionUrl = this.selectedActorInfo[collectionName];
|
||||
this._retrieveCountFromCollectionUrl(collectionUrl, (err, count) => {
|
||||
if (err) {
|
||||
this.client.log.warn(
|
||||
{ err: err },
|
||||
`Unable to get Collection count for ${collectionUrl}`
|
||||
);
|
||||
view.setText('--');
|
||||
this.selectedActorInfo[`_${collectionName}Count`] = -1;
|
||||
} else {
|
||||
view.setText(count);
|
||||
this.selectedActorInfo[`_${collectionName}Count`] = count;
|
||||
}
|
||||
|
||||
return cb(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -229,7 +348,7 @@ exports.getModule = class ActivityPubActorSearch extends MenuModule {
|
|||
}
|
||||
|
||||
Collection.getRemoteCollectionStats(collectionUrl, (err, stats) => {
|
||||
return cb(err, stats.totalItems);
|
||||
return cb(err, err ? null : stats.totalItems);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@ const { Errors } = require('../enig_error.js');
|
|||
const {
|
||||
PublicCollectionId: APPublicCollectionId,
|
||||
ActivityStreamMediaType,
|
||||
Collections,
|
||||
} = require('./const');
|
||||
const UserProps = require('../user_property');
|
||||
const { getJson } = require('../http_util');
|
||||
|
@ -55,7 +56,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
|
||||
static followers(collectionId, page, cb) {
|
||||
return Collection.publicOrderedById(
|
||||
'followers',
|
||||
Collections.Followers,
|
||||
collectionId,
|
||||
page,
|
||||
e => e.id,
|
||||
|
@ -65,7 +66,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
|
||||
static following(collectionId, page, cb) {
|
||||
return Collection.publicOrderedById(
|
||||
'following',
|
||||
Collections.Following,
|
||||
collectionId,
|
||||
page,
|
||||
e => e.id,
|
||||
|
@ -74,13 +75,19 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
}
|
||||
|
||||
static outbox(collectionId, page, cb) {
|
||||
return Collection.publicOrderedById('outbox', collectionId, page, null, cb);
|
||||
return Collection.publicOrderedById(
|
||||
Collections.Outbox,
|
||||
collectionId,
|
||||
page,
|
||||
null,
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
static addFollower(owningUser, followingActor, webServer, ignoreDupes, cb) {
|
||||
const collectionId = Endpoints.followers(webServer, owningUser);
|
||||
return Collection.addToCollection(
|
||||
'followers',
|
||||
Collections.Followers,
|
||||
owningUser,
|
||||
collectionId,
|
||||
followingActor.id, // Actor following owningUser
|
||||
|
@ -95,7 +102,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
const collectionId =
|
||||
Endpoints.makeUserUrl(webServer, owningUser) + 'follow-requests';
|
||||
return Collection.addToCollection(
|
||||
'follow-requests',
|
||||
Collections.FollowRequests,
|
||||
owningUser,
|
||||
collectionId,
|
||||
requestingActor.id, // Actor requesting to follow owningUser
|
||||
|
@ -109,7 +116,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
static addFollowing(owningUser, followingActor, webServer, ignoreDupes, cb) {
|
||||
const collectionId = Endpoints.following(webServer, owningUser);
|
||||
return Collection.addToCollection(
|
||||
'following',
|
||||
Collections.Following,
|
||||
owningUser,
|
||||
collectionId,
|
||||
followingActor.id, // Actor owningUser is following
|
||||
|
@ -123,7 +130,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
static addOutboxItem(owningUser, outboxItem, isPrivate, webServer, ignoreDupes, cb) {
|
||||
const collectionId = Endpoints.outbox(webServer, owningUser);
|
||||
return Collection.addToCollection(
|
||||
'outbox',
|
||||
Collections.Outbox,
|
||||
owningUser,
|
||||
collectionId,
|
||||
outboxItem.id,
|
||||
|
@ -137,7 +144,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
static addInboxItem(inboxItem, owningUser, webServer, ignoreDupes, cb) {
|
||||
const collectionId = Endpoints.inbox(webServer, owningUser);
|
||||
return Collection.addToCollection(
|
||||
'inbox',
|
||||
Collections.Inbox,
|
||||
owningUser,
|
||||
collectionId,
|
||||
inboxItem.id,
|
||||
|
@ -150,7 +157,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
|
||||
static addSharedInboxItem(inboxItem, ignoreDupes, cb) {
|
||||
return Collection.addToCollection(
|
||||
'sharedInbox',
|
||||
Collections.SharedInbox,
|
||||
null, // N/A
|
||||
Collection.PublicCollectionId,
|
||||
inboxItem.id,
|
||||
|
@ -161,13 +168,79 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
);
|
||||
}
|
||||
|
||||
static objectById(objectId, cb) {
|
||||
// Get Object(s) by ID; There may be multiples as they may be
|
||||
// e.g. Actors belonging to multiple followers collections.
|
||||
// This method also returns information about the objects
|
||||
// and any items that can't be parsed
|
||||
static objectsById(objectId, cb) {
|
||||
apDb.all(
|
||||
`SELECT name, timestamp, owner_actor_id, object_json, is_private
|
||||
FROM collection
|
||||
WHERE object_id = ?;`,
|
||||
[objectId],
|
||||
(err, rows) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
const results = (rows || []).map(r => {
|
||||
const info = {
|
||||
info: this._rowToObjectInfo(r),
|
||||
object: ActivityPubObject.fromJsonString(r.object_json),
|
||||
};
|
||||
if (!info.object) {
|
||||
info.raw = r.object_json;
|
||||
}
|
||||
return info;
|
||||
});
|
||||
|
||||
return cb(null, results);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static ownedObjectByNameAndId(collectionName, owningUser, objectId, cb) {
|
||||
const actorId = owningUser.getProperty(UserProps.ActivityPubActorId);
|
||||
if (!actorId) {
|
||||
return cb(
|
||||
Errors.MissingProperty(
|
||||
`User "${owningUser.username}" is missing property '${UserProps.ActivityPubActorId}'`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
apDb.get(
|
||||
`SELECT name, timestamp, owner_actor_id, object_json, is_private
|
||||
FROM collection
|
||||
WHERE name = ? AND owner_actor_id = ? AND object_id = ?
|
||||
LIMIT 1;`,
|
||||
[collectionName, actorId, objectId],
|
||||
(err, row) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
if (!row) {
|
||||
return cb(null, null);
|
||||
}
|
||||
|
||||
const obj = ActivityPubObject.fromJsonString(row.object_json);
|
||||
if (!obj) {
|
||||
return cb(Errors.Invalid('Failed to parse Object JSON'));
|
||||
}
|
||||
|
||||
return cb(null, obj, Collection._rowToObjectInfo(row));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static objectByNameAndId(collectionName, objectId, cb) {
|
||||
apDb.get(
|
||||
`SELECT name, timestamp, owner_actor_id, object_json, is_private
|
||||
FROM collection
|
||||
WHERE name = ? AND object_id = ?
|
||||
LIMIT 1;`,
|
||||
[objectId],
|
||||
[collectionName, objectId],
|
||||
(err, row) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
|
|
|
@ -31,3 +31,13 @@ exports.HttpSignatureSignHeaders = [
|
|||
'digest',
|
||||
'content-type',
|
||||
];
|
||||
|
||||
const Collections = {
|
||||
Following: 'following',
|
||||
Followers: 'followers',
|
||||
FollowRequests: 'followRequests',
|
||||
Outbox: 'outbox',
|
||||
Inbox: 'inbox',
|
||||
SharedInbox: 'sharedInbox',
|
||||
};
|
||||
exports.Collections = Collections;
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
const { Collections, WellKnownActivity } = require('./const');
|
||||
const ActivityPubObject = require('./object');
|
||||
const UserProps = require('../user_property');
|
||||
const { Errors } = require('../enig_error');
|
||||
const Collection = require('./collection');
|
||||
|
||||
exports.sendFollowRequest = sendFollowRequest;
|
||||
exports.sendUnfollowRequest = sendUnfollowRequest;
|
||||
|
||||
function sendFollowRequest(fromUser, toActor, webServer, cb) {
|
||||
const fromActorId = fromUser.getProperty(UserProps.ActivityPubActorId);
|
||||
if (!fromActorId) {
|
||||
return cb(
|
||||
Errors.MissingProperty(
|
||||
`User missing "${UserProps.ActivityPubActorId}" property`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// We always add to the following collection;
|
||||
// We expect an async follow up request to our server of
|
||||
// Accept or Reject but it's not guaranteed
|
||||
Collection.addFollowing(fromUser, toActor, webServer, true, err => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
const followRequest = new ActivityPubObject({
|
||||
id: ActivityPubObject.makeObjectId(webServer, 'follow'),
|
||||
type: WellKnownActivity.Follow,
|
||||
actor: fromActorId,
|
||||
object: toActor.id,
|
||||
});
|
||||
|
||||
return followRequest.sendTo(toActor.inbox, fromUser, webServer, cb);
|
||||
});
|
||||
}
|
||||
|
||||
function sendUnfollowRequest(fromUser, toActor, webServer, cb) {
|
||||
const fromActorId = fromUser.getProperty(UserProps.ActivityPubActorId);
|
||||
if (!fromActorId) {
|
||||
return cb(
|
||||
Errors.MissingProperty(
|
||||
`User missing "${UserProps.ActivityPubActorId}" property`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Always remove from the local collection, notify the remote server
|
||||
Collection.removeOwnedById(Collections.Following, fromUser, toActor.inbox, err => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
const undoRequest = new ActivityPubObject({
|
||||
id: ActivityPubObject.makeObjectId(webServer, 'undo'),
|
||||
type: WellKnownActivity.Undo,
|
||||
actor: fromActorId,
|
||||
object: toActor.id,
|
||||
});
|
||||
|
||||
return undoRequest.sendTo(toActor.inbox, fromUser, webServer, cb);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
const { MenuModule } = require('../menu_module');
|
||||
const Collection = require('./collection');
|
||||
const { getServer } = require('../listening_server');
|
||||
const Endpoints = require('./endpoint');
|
||||
const Actor = require('./actor');
|
||||
const stringFormat = require('../string_format');
|
||||
const { pipeToAnsi } = require('../color_codes');
|
||||
const MultiLineEditTextView =
|
||||
require('../multi_line_edit_text_view').MultiLineEditTextView;
|
||||
const { sendFollowRequest, sendUnfollowRequest } = require('./follow_util');
|
||||
const { Collections } = require('./const');
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
const { get, cloneDeep } = require('lodash');
|
||||
|
||||
exports.moduleInfo = {
|
||||
name: 'ActivityPub Social Manager',
|
||||
desc: 'Manages ActivityPub Actors the current user is following or being followed by.',
|
||||
author: 'NuSkooler',
|
||||
};
|
||||
|
||||
const FormIds = {
|
||||
main: 0,
|
||||
};
|
||||
|
||||
const MciViewIds = {
|
||||
main: {
|
||||
actorList: 1,
|
||||
selectedActorInfo: 2,
|
||||
navMenu: 3,
|
||||
|
||||
customRangeStart: 10,
|
||||
},
|
||||
};
|
||||
|
||||
exports.getModule = class ActivityPubFollowingManager extends MenuModule {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.setConfigWithExtraArgs(options);
|
||||
|
||||
this.followingActors = [];
|
||||
this.followerActors = [];
|
||||
this.currentCollection = Collections.Following;
|
||||
|
||||
this.menuMethods = {
|
||||
spaceKeyPressed: (formData, extraArgs, cb) => {
|
||||
return this._toggleSelectedActorStatus(cb);
|
||||
},
|
||||
listKeyPressed: (formData, extraArgs, cb) => {
|
||||
const actorListView = this.getView('main', MciViewIds.main.actorList);
|
||||
if (actorListView) {
|
||||
const keyName = get(formData, 'key.name');
|
||||
switch (keyName) {
|
||||
case 'down arrow':
|
||||
actorListView.focusNext();
|
||||
break;
|
||||
case 'up arrow':
|
||||
actorListView.focusPrevious();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cb(null);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
initSequence() {
|
||||
this.webServer = getServer('codes.l33t.enigma.web.server');
|
||||
if (!this.webServer) {
|
||||
this.client.log('Could not get Web server');
|
||||
return this.prevMenu();
|
||||
}
|
||||
this.webServer = this.webServer.instance;
|
||||
|
||||
async.series(
|
||||
[
|
||||
callback => {
|
||||
return this.beforeArt(callback);
|
||||
},
|
||||
callback => {
|
||||
return this._displayMainPage(callback);
|
||||
},
|
||||
],
|
||||
() => {
|
||||
this.finishedLoading();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_displayMainPage(cb) {
|
||||
async.series(
|
||||
[
|
||||
callback => {
|
||||
return this.displayArtAndPrepViewController(
|
||||
'main',
|
||||
FormIds.main,
|
||||
{ clearScreen: true },
|
||||
callback
|
||||
);
|
||||
},
|
||||
callback => {
|
||||
return this.validateMCIByViewIds(
|
||||
'main',
|
||||
Object.values(MciViewIds.main).filter(
|
||||
id => id !== MciViewIds.main.customRangeStart
|
||||
),
|
||||
callback
|
||||
);
|
||||
},
|
||||
callback => {
|
||||
this._fetchActorList(
|
||||
Collections.Following,
|
||||
(err, followingActors) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
return this._fetchActorList(
|
||||
Collections.Followers,
|
||||
(err, followerActors) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.followingActors = followingActors;
|
||||
this.followerActors = followerActors;
|
||||
|
||||
return callback(null);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
callback => {
|
||||
const v = id => this.getView('main', id);
|
||||
|
||||
const actorListView = v(MciViewIds.main.actorList);
|
||||
const selectedActorInfoView = v(MciViewIds.main.selectedActorInfo);
|
||||
const navMenuView = v(MciViewIds.main.navMenu);
|
||||
|
||||
// We start with following
|
||||
this._switchTo(Collections.Following);
|
||||
|
||||
actorListView.on('index update', index => {
|
||||
const selectedActor = this._getSelectedActorItem(index);
|
||||
this._updateSelectedActorInfo(
|
||||
selectedActorInfoView,
|
||||
selectedActor
|
||||
);
|
||||
});
|
||||
|
||||
navMenuView.on('index update', index => {
|
||||
if (0 === index) {
|
||||
this._switchTo(Collections.Following);
|
||||
} else {
|
||||
this._switchTo(Collections.Followers);
|
||||
}
|
||||
});
|
||||
|
||||
return callback(null);
|
||||
},
|
||||
],
|
||||
err => {
|
||||
return cb(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_switchTo(collectionName) {
|
||||
this.currentCollection = collectionName;
|
||||
const actorListView = this.getView('main', MciViewIds.main.actorList);
|
||||
if (Collections.Following === collectionName) {
|
||||
actorListView.setItems(this.followingActors);
|
||||
} else {
|
||||
actorListView.setItems(this.followerActors);
|
||||
}
|
||||
actorListView.redraw();
|
||||
|
||||
const selectedActor = this._getSelectedActorItem(
|
||||
actorListView.getFocusItemIndex()
|
||||
);
|
||||
const selectedActorInfoView = this.getView(
|
||||
'main',
|
||||
MciViewIds.main.selectedActorInfo
|
||||
);
|
||||
if (selectedActor) {
|
||||
this._updateSelectedActorInfo(selectedActorInfoView, selectedActor);
|
||||
} else {
|
||||
selectedActorInfoView.setText('');
|
||||
}
|
||||
}
|
||||
|
||||
_getSelectedActorItem(index) {
|
||||
if (this.currentCollection === Collections.Following) {
|
||||
return this.followingActors[index];
|
||||
} else {
|
||||
return this.followerActors[index];
|
||||
}
|
||||
}
|
||||
|
||||
_getCurrentActorList() {
|
||||
return this.currentCollection === Collections.Following
|
||||
? this.followingActors
|
||||
: this.followerActors;
|
||||
}
|
||||
|
||||
_updateSelectedActorInfo(view, actorInfo) {
|
||||
if (actorInfo) {
|
||||
const selectedActorInfoFormat =
|
||||
this.config.selectedActorInfoFormat || '{text}';
|
||||
|
||||
const s = stringFormat(selectedActorInfoFormat, actorInfo);
|
||||
|
||||
if (view instanceof MultiLineEditTextView) {
|
||||
view.setAnsi(pipeToAnsi(s, this.client));
|
||||
} else {
|
||||
view.setText(s);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateCustomViewTextsWithFilter(
|
||||
'main',
|
||||
MciViewIds.main.customRangeStart,
|
||||
this._getCustomInfoFormatObject(actorInfo)
|
||||
);
|
||||
}
|
||||
|
||||
_toggleSelectedActorStatus(cb) {
|
||||
const actorListView = this.getView('main', MciViewIds.main.actorList);
|
||||
const selectedActor = this._getSelectedActorItem(
|
||||
actorListView.getFocusItemIndex()
|
||||
);
|
||||
if (selectedActor) {
|
||||
selectedActor.status = !selectedActor.status;
|
||||
selectedActor.statusIndicator = this._getStatusIndicator(
|
||||
selectedActor.status
|
||||
);
|
||||
|
||||
async.series(
|
||||
[
|
||||
callback => {
|
||||
if (Collections.Following === this.currentCollection) {
|
||||
return this._followingActorToggled(selectedActor, callback);
|
||||
} else {
|
||||
return this._followerActorToggled(selectedActor, callback);
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if (err) {
|
||||
this.client.log.error(
|
||||
{ error: err.message, type: this.currentCollection },
|
||||
`Failed to toggle "${this.currentCollection}" status`
|
||||
);
|
||||
}
|
||||
|
||||
// :TODO: we really need updateItem() call on MenuView
|
||||
actorListView.setItems(this._getCurrentActorList());
|
||||
actorListView.redraw(); // oof
|
||||
|
||||
return cb(null);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_followingActorToggled(actorInfo, cb) {
|
||||
// Local user/Actor wants to follow or un-follow
|
||||
const wantsToFollow = actorInfo.status;
|
||||
const actor = this._actorInfoToActor(actorInfo);
|
||||
|
||||
return wantsToFollow
|
||||
? sendFollowRequest(this.client.user, actor, this.webServer, cb)
|
||||
: sendUnfollowRequest(this.client.user, actor, this.webServer, cb);
|
||||
}
|
||||
|
||||
_actorInfoToActor(actorInfo) {
|
||||
const actor = cloneDeep(actorInfo);
|
||||
|
||||
// nuke our added properties
|
||||
delete actor.subject;
|
||||
delete actor.text;
|
||||
delete actor.status;
|
||||
delete actor.statusIndicator;
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
_followerActorToggled(actorInfo, cb) {
|
||||
return cb(null);
|
||||
}
|
||||
|
||||
_getCustomInfoFormatObject(actorInfo) {
|
||||
const formatObj = {
|
||||
followingCount: this.followingActors.length,
|
||||
followerCount: this.followerActors.length,
|
||||
};
|
||||
|
||||
const v = f => {
|
||||
return actorInfo ? actorInfo[f] || '' : '';
|
||||
};
|
||||
|
||||
Object.assign(formatObj, {
|
||||
selectedActorId: v('id'),
|
||||
selectedActorSubject: v('subject'),
|
||||
selectedActorType: v('type'),
|
||||
selectedActorName: v('name'),
|
||||
selectedActorSummary: v('summary'),
|
||||
selectedActorPreferredUsername: v('preferredUsername'),
|
||||
selectedActorUrl: v('url'),
|
||||
selectedActorImage: v('image'),
|
||||
selectedActorIcon: v('icon'),
|
||||
selectedActorStatus: actorInfo ? actorInfo.status : false,
|
||||
selectedActorStatusIndicator: v('statusIndicator'),
|
||||
text: v('name'),
|
||||
});
|
||||
|
||||
return formatObj;
|
||||
}
|
||||
|
||||
_getStatusIndicator(enabled) {
|
||||
return enabled
|
||||
? this.config.statusIndicatorEnabled || '√'
|
||||
: this.config.statusIndicatorDisabled || 'X';
|
||||
}
|
||||
|
||||
_fetchActorList(collectionName, cb) {
|
||||
const collectionId = Endpoints[collectionName](this.webServer, this.client.user);
|
||||
Collection[collectionName](collectionId, 'all', (err, collection) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
if (!collection.orderedItems || collection.orderedItems.length < 1) {
|
||||
return cb(null, []);
|
||||
}
|
||||
|
||||
const statusIndicator = this._getStatusIndicator(true);
|
||||
|
||||
async.mapLimit(
|
||||
collection.orderedItems,
|
||||
4,
|
||||
(actorId, nextActorId) => {
|
||||
Actor.fromId(actorId, (err, actor, subject) => {
|
||||
if (err) {
|
||||
this.client.log.warn({ actorId }, 'Failed to retrieve Actor');
|
||||
return nextActorId(null, null);
|
||||
}
|
||||
|
||||
// Add some of our own properties
|
||||
Object.assign(actor, {
|
||||
subject,
|
||||
status: true,
|
||||
statusIndicator,
|
||||
text: actor.name,
|
||||
});
|
||||
|
||||
return nextActorId(null, actor);
|
||||
});
|
||||
},
|
||||
(err, actorsList) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
actorsList = actorsList.filter(f => f); // drop nulls
|
||||
return cb(null, actorsList);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
|
@ -51,10 +51,7 @@ const EnabledViewGroup = [
|
|||
exports.getModule = class ActivityPubUserConfig extends MenuModule {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.config = Object.assign({}, get(options, 'menuConfig.config'), {
|
||||
extraArgs: options.extraArgs,
|
||||
});
|
||||
this.setConfigWithExtraArgs(options);
|
||||
|
||||
this.menuMethods = {
|
||||
mainSubmit: (formData, extraArgs, cb) => {
|
||||
|
|
|
@ -3,8 +3,7 @@ const { Errors, ErrorReasons } = require('../enig_error');
|
|||
const UserProps = require('../user_property');
|
||||
const ActivityPubSettings = require('./settings');
|
||||
const { stripAnsiControlCodes } = require('../string_util');
|
||||
const { WellKnownRecipientFields, WellKnownActivity } = require('./const');
|
||||
const ActivityPubObject = require('./object');
|
||||
const { WellKnownRecipientFields } = require('./const');
|
||||
const Log = require('../logger').log;
|
||||
|
||||
// deps
|
||||
|
@ -29,7 +28,6 @@ exports.userNameFromSubject = userNameFromSubject;
|
|||
exports.userNameToSubject = userNameToSubject;
|
||||
exports.extractMessageMetadata = extractMessageMetadata;
|
||||
exports.recipientIdsFromObject = recipientIdsFromObject;
|
||||
exports.sendFollowRequest = sendFollowRequest;
|
||||
|
||||
// :TODO: more info in default
|
||||
// this profile template is the *default* for both WebFinger
|
||||
|
@ -264,23 +262,3 @@ function recipientIdsFromObject(obj) {
|
|||
|
||||
return Array.from(new Set(ids));
|
||||
}
|
||||
|
||||
function sendFollowRequest(fromUser, toActor, webServer, cb) {
|
||||
const fromActorId = fromUser.getProperty(UserProps.ActivityPubActorId);
|
||||
if (!fromActorId) {
|
||||
return cb(
|
||||
Errors.MissingProperty(
|
||||
`User missing "${UserProps.ActivityPubActorId}" property`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const followRequest = new ActivityPubObject({
|
||||
id: ActivityPubObject.makeObjectId(webServer, 'follow'),
|
||||
type: WellKnownActivity.Follow,
|
||||
actor: fromActorId,
|
||||
object: toActor.id,
|
||||
});
|
||||
|
||||
return followRequest.sendTo(toActor.inbox, fromUser, webServer, cb);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ const Endpoints = require('../../../activitypub/endpoint');
|
|||
const {
|
||||
ActivityStreamMediaType,
|
||||
WellKnownActivity,
|
||||
Collections,
|
||||
} = require('../../../activitypub/const');
|
||||
const Config = require('../../../config').get;
|
||||
const Activity = require('../../../activitypub/activity');
|
||||
|
@ -64,7 +65,12 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
req,
|
||||
resp,
|
||||
(req, resp, signature) => {
|
||||
return this._inboxPostHandler(req, resp, signature, 'inbox');
|
||||
return this._inboxPostHandler(
|
||||
req,
|
||||
resp,
|
||||
signature,
|
||||
Collections.Inbox
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
@ -82,7 +88,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
req,
|
||||
resp,
|
||||
signature,
|
||||
'sharedInbox'
|
||||
Collections.SharedInbox
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -262,7 +268,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
|
||||
switch (activity.type) {
|
||||
case WellKnownActivity.Accept:
|
||||
break;
|
||||
return this._inboxAcceptActivity(resp, activity);
|
||||
|
||||
case WellKnownActivity.Add:
|
||||
break;
|
||||
|
@ -292,17 +298,17 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
|
||||
case WellKnownActivity.Follow:
|
||||
// Follow requests are only allowed directly
|
||||
if ('inbox' === inboxType) {
|
||||
if (Collections.Inbox === inboxType) {
|
||||
return this._inboxFollowActivity(resp, remoteActor, activity);
|
||||
}
|
||||
break;
|
||||
|
||||
case WellKnownActivity.Reject:
|
||||
break;
|
||||
return this._inboxRejectActivity(resp, activity);
|
||||
|
||||
case WellKnownActivity.Undo:
|
||||
// We only Undo from private inboxes
|
||||
if ('inbox' === inboxType) {
|
||||
if (Collections.Inbox === inboxType) {
|
||||
// Only Follow Undo's currently supported
|
||||
const type = _.get(activity, 'object.type');
|
||||
if (WellKnownActivity.Follow === type) {
|
||||
|
@ -330,6 +336,21 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
});
|
||||
}
|
||||
|
||||
_inboxAcceptActivity(resp, activity) {
|
||||
const acceptWhat = _.get(activity, 'object.type');
|
||||
switch (acceptWhat) {
|
||||
case WellKnownActivity.Follow:
|
||||
return this._inboxAcceptFollowActivity(resp, activity);
|
||||
|
||||
default:
|
||||
this.log.warn(
|
||||
{ type: acceptWhat },
|
||||
'Invalid or unsupported "Accept" type'
|
||||
);
|
||||
return this.webServer.notImplemented(resp);
|
||||
}
|
||||
}
|
||||
|
||||
_inboxCreateActivity(resp, activity) {
|
||||
const createWhat = _.get(activity, 'object.type');
|
||||
switch (createWhat) {
|
||||
|
@ -341,10 +362,23 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
{ type: createWhat },
|
||||
'Invalid or unsupported "Create" type'
|
||||
);
|
||||
return this.webServer.resourceNotFound(resp);
|
||||
return this.webServer.notImplemented(resp);
|
||||
}
|
||||
}
|
||||
|
||||
_inboxAcceptFollowActivity(resp, activity) {
|
||||
// Currently Accept's to Follow's are really just a formality;
|
||||
// we'll log it, but that's about it for now
|
||||
this.log.info(
|
||||
{
|
||||
remoteActorId: activity.actor,
|
||||
localActorId: _.get(activity, 'object.actor'),
|
||||
},
|
||||
'Follow request Accepted'
|
||||
);
|
||||
return this.webServer.accepted(resp);
|
||||
}
|
||||
|
||||
_inboxCreateNoteActivity(resp, activity) {
|
||||
const note = new Note(activity.object);
|
||||
if (!note.isValid()) {
|
||||
|
@ -380,10 +414,107 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
);
|
||||
}
|
||||
|
||||
_inboxDeleteActivity(inboxType, signature, resp /*, activity*/) {
|
||||
// :TODO: Implement me!
|
||||
_inboxRejectFollowActivity(resp, activity) {
|
||||
// A user Rejected our local Actor/user's Follow request;
|
||||
// Update the local Collection to reflect this fact.
|
||||
const remoteActorId = activity.actor;
|
||||
const localActorId = _.get(activity, 'object.actor');
|
||||
|
||||
if (!remoteActorId || !localActorId) {
|
||||
return this.webServer.badRequest(resp);
|
||||
}
|
||||
|
||||
userFromActorId(localActorId, (err, localUser) => {
|
||||
if (err) {
|
||||
return this.webServer.resourceNotFound(resp);
|
||||
}
|
||||
|
||||
Collection.removeOwnedById(
|
||||
Collections.Following,
|
||||
localUser,
|
||||
remoteActorId,
|
||||
err => {
|
||||
if (err) {
|
||||
this.log.error(
|
||||
{ remoteActorId, localActorId },
|
||||
'Failed removing "following" record'
|
||||
);
|
||||
}
|
||||
return this.webServer.accepted(resp);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_inboxDeleteActivity(inboxType, signature, resp, activity) {
|
||||
const objectId = _.get(activity, 'object.id', activity.object);
|
||||
|
||||
this.log.info({ inboxType, objectId }, 'Incoming Delete request');
|
||||
|
||||
// :TODO: we need to DELETE the existing stored Message object if this is a Note, or associated if this is an Actor
|
||||
// :TODO: delete / invalidate any actor cache if actor
|
||||
Collection.objectsById(objectId, (err, objectsInfo) => {
|
||||
if (err) {
|
||||
this.log.warn({ objectId });
|
||||
// We'll respond accepted so they don't keep trying
|
||||
return this.webServer.accepted(resp);
|
||||
}
|
||||
|
||||
if (objectsInfo.length === 0) {
|
||||
return this.webServer.resourceNotFound(resp);
|
||||
}
|
||||
|
||||
// Generally we'd have a 1:1 objectId -> object here, but it's
|
||||
// possible for example, that we're being asked to delete an Actor;
|
||||
// If this is the case, they may be following multiple local Actor/users
|
||||
// and we have multiple entries.
|
||||
async.forEachSeries(
|
||||
objectsInfo,
|
||||
(objInfo, nextObjInfo) => {
|
||||
if (objInfo.object) {
|
||||
// Based on the collection we find this entry in,
|
||||
// we may have additional validation or actions
|
||||
switch (objInfo.info.name) {
|
||||
case Collections.Inbox:
|
||||
if (inboxType !== Collections.Inbox) {
|
||||
// :TODO: LOG ME
|
||||
return nextObjInfo(null);
|
||||
}
|
||||
break;
|
||||
|
||||
case Collections.SharedInbox:
|
||||
if (inboxType !== Collections.SharedInbox) {
|
||||
// :TODO: log me
|
||||
return nextObjInfo(null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nextObjInfo(null);
|
||||
} else {
|
||||
// it's unparsable, so we'll delete it
|
||||
Collection.removeById(objInfo.info.name, objectId, err => {
|
||||
if (err) {
|
||||
this.log.warn(
|
||||
{ objectId, collectionName: objInfo.info.name },
|
||||
'Failed to remove object'
|
||||
);
|
||||
}
|
||||
return nextObjInfo(null);
|
||||
});
|
||||
}
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
// :TODO: log me
|
||||
}
|
||||
return this.webServer.accepted(resp);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return this.webServer.accepted(resp);
|
||||
}
|
||||
|
||||
|
@ -402,7 +533,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
const activityPubSettings = ActivityPubSettings.fromUser(localUser);
|
||||
if (!activityPubSettings.manuallyApproveFollowers) {
|
||||
this._recordAcceptedFollowRequest(localUser, remoteActor, activity);
|
||||
return this.webServer.ok(resp);
|
||||
return this.webServer.accepted(resp);
|
||||
}
|
||||
|
||||
// User manually approves requests; add them to their requests collection
|
||||
|
@ -416,12 +547,27 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
return this.internalServerError(resp, err);
|
||||
}
|
||||
|
||||
return this.webServer.ok(resp);
|
||||
return this.webServer.accepted(resp);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_inboxRejectActivity(resp, activity) {
|
||||
const rejectWhat = _.get(activity, 'object.type');
|
||||
switch (rejectWhat) {
|
||||
case WellKnownActivity.Follow:
|
||||
return this._inboxRejectFollowActivity(resp, activity);
|
||||
|
||||
default:
|
||||
this.log.warn(
|
||||
{ type: rejectWhat },
|
||||
'Invalid or unsupported "Reject" type'
|
||||
);
|
||||
return this.webServer.notImplemented(resp);
|
||||
}
|
||||
}
|
||||
|
||||
_inboxUndoActivity(resp, remoteActor, activity) {
|
||||
const localActorId = _.get(activity, 'object.object');
|
||||
|
||||
|
@ -435,7 +581,11 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
return this.webServer.resourceNotFound(resp);
|
||||
}
|
||||
|
||||
Collection.removeOwnedById('followers', localUser, remoteActor.id, err => {
|
||||
Collection.removeOwnedById(
|
||||
Collections.Followers,
|
||||
localUser,
|
||||
remoteActor.id,
|
||||
err => {
|
||||
if (err) {
|
||||
return this.webServer.internalServerError(resp, err);
|
||||
}
|
||||
|
@ -450,7 +600,8 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
);
|
||||
|
||||
return this.webServer.accepted(resp);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -712,18 +863,18 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
|
||||
_followingGetHandler(req, resp) {
|
||||
this.log.debug({ url: req.url }, 'Request for "following"');
|
||||
return this._actorCollectionRequest('following', req, resp);
|
||||
return this._actorCollectionRequest(Collections.Following, req, resp);
|
||||
}
|
||||
|
||||
_followersGetHandler(req, resp) {
|
||||
this.log.debug({ url: req.url }, 'Request for "followers"');
|
||||
return this._actorCollectionRequest('followers', req, resp);
|
||||
return this._actorCollectionRequest(Collections.Followers, req, resp);
|
||||
}
|
||||
|
||||
// https://docs.gotosocial.org/en/latest/federation/behaviors/outbox/
|
||||
_outboxGetHandler(req, resp) {
|
||||
this.log.debug({ url: req.url }, 'Request for "outbox"');
|
||||
return this._actorCollectionRequest('outbox', req, resp);
|
||||
return this._actorCollectionRequest(Collections.Outbox, req, resp);
|
||||
}
|
||||
|
||||
_singlePublicNoteGetHandler(req, resp) {
|
||||
|
|
|
@ -11,7 +11,6 @@ const pipeToAnsi = require('./color_codes.js').pipeToAnsi;
|
|||
// deps
|
||||
const util = require('util');
|
||||
const _ = require('lodash');
|
||||
const { throws } = require('assert');
|
||||
|
||||
exports.VerticalMenuView = VerticalMenuView;
|
||||
|
||||
|
|
Loading…
Reference in New Issue