Add attachment information to messages, fix duplicate handling to respond properly
This commit is contained in:
parent
5cfacf4ff0
commit
f97d1844e3
|
@ -38,7 +38,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
);
|
||||
}
|
||||
|
||||
static addFollower(owningUser, followingActor, webServer, cb) {
|
||||
static addFollower(owningUser, followingActor, webServer, ignoreDupes, cb) {
|
||||
const collectionId =
|
||||
makeUserUrl(webServer, owningUser, '/ap/collections/') + '/followers';
|
||||
return Collection.addToCollection(
|
||||
|
@ -48,11 +48,12 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
followingActor.id,
|
||||
followingActor,
|
||||
false,
|
||||
ignoreDupes,
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
static addFollowRequest(owningUser, requestingActor, webServer, cb) {
|
||||
static addFollowRequest(owningUser, requestingActor, webServer, ignoreDupes, cb) {
|
||||
const collectionId =
|
||||
makeUserUrl(webServer, owningUser, '/ap/collections/') + '/follow-requests';
|
||||
return Collection.addToCollection(
|
||||
|
@ -62,6 +63,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
requestingActor.id,
|
||||
requestingActor,
|
||||
true,
|
||||
ignoreDupes,
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
@ -70,7 +72,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
return Collection.publicOrderedById('outbox', collectionId, page, null, cb);
|
||||
}
|
||||
|
||||
static addOutboxItem(owningUser, outboxItem, isPrivate, webServer, cb) {
|
||||
static addOutboxItem(owningUser, outboxItem, isPrivate, webServer, ignoreDupes, cb) {
|
||||
const collectionId =
|
||||
makeUserUrl(webServer, owningUser, '/ap/collections/') + '/outbox';
|
||||
return Collection.addToCollection(
|
||||
|
@ -80,11 +82,12 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
outboxItem.id,
|
||||
outboxItem,
|
||||
isPrivate,
|
||||
ignoreDupes,
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
static addInboxItem(inboxItem, owningUser, webServer, cb) {
|
||||
static addInboxItem(inboxItem, owningUser, webServer, ignoreDupes, cb) {
|
||||
const collectionId =
|
||||
makeUserUrl(webServer, owningUser, '/ap/collections/') + '/inbox';
|
||||
return Collection.addToCollection(
|
||||
|
@ -94,11 +97,12 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
inboxItem.id,
|
||||
inboxItem,
|
||||
true,
|
||||
ignoreDupes,
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
static addPublicInboxItem(inboxItem, cb) {
|
||||
static addPublicInboxItem(inboxItem, ignoreDupes, cb) {
|
||||
return Collection.addToCollection(
|
||||
'publicInbox',
|
||||
null, // N/A
|
||||
|
@ -106,6 +110,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
inboxItem.id,
|
||||
inboxItem,
|
||||
false,
|
||||
ignoreDupes,
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
@ -325,6 +330,7 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
objectId,
|
||||
obj,
|
||||
isPrivate,
|
||||
ignoreDupes,
|
||||
cb
|
||||
) {
|
||||
if (!isString(obj)) {
|
||||
|
@ -361,8 +367,8 @@ module.exports = class Collection extends ActivityPubObject {
|
|||
],
|
||||
function res(err) {
|
||||
// non-arrow for 'this' scope
|
||||
if (err) {
|
||||
if ('SQLITE_CONSTRAINT' === err.code) {
|
||||
if (err && 'SQLITE_CONSTRAINT' === err.code) {
|
||||
if (ignoreDupes) {
|
||||
err = null; // ignore
|
||||
}
|
||||
return cb(err);
|
||||
|
|
|
@ -172,17 +172,54 @@ module.exports = class Note extends ActivityPubObject {
|
|||
|
||||
// :TODO: it would be better to do some basic HTML to ANSI or pipe codes perhaps
|
||||
message.message = htmlToMessageBody(this.content);
|
||||
message.subject = this._getSubject(message);
|
||||
|
||||
// If the summary is not present, build one using the message itself;
|
||||
// finally, default to a static subject so there is *something* if
|
||||
// all else fails.
|
||||
if (this.summary) {
|
||||
message.subject = this.summary;
|
||||
} else {
|
||||
let subject = message.message.replace(`@${message.toUserName} `, '');
|
||||
subject = truncate(subject, { length: 32, omission: '...' });
|
||||
subject = subject || APDefaultSummary;
|
||||
message.subject = subject;
|
||||
// List all attachments
|
||||
if (Array.isArray(this.attachment) && this.attachment.length > 0) {
|
||||
let attachmentInfoLines = ['--[Attachments]--'];
|
||||
// https://socialhub.activitypub.rocks/t/representing-images/624
|
||||
this.attachment.forEach(att => {
|
||||
switch (att.mediaType) {
|
||||
case 'image/avif':
|
||||
case 'image/apng':
|
||||
case 'image/png':
|
||||
case 'image/x-png':
|
||||
case 'image/jpeg':
|
||||
case 'image/gif':
|
||||
case 'image/svg+xml':
|
||||
case 'image/webp':
|
||||
{
|
||||
let imgInfo;
|
||||
if (att.height && att.width) {
|
||||
imgInfo = `Image (${att.width} x ${att.height})`;
|
||||
} else {
|
||||
imgInfo = 'Image';
|
||||
}
|
||||
attachmentInfoLines.push(imgInfo);
|
||||
}
|
||||
break;
|
||||
|
||||
// :TODO: video
|
||||
|
||||
default:
|
||||
attachmentInfoLines.push(att.mediaType);
|
||||
}
|
||||
|
||||
if (att.name) {
|
||||
attachmentInfoLines.push(att.name);
|
||||
}
|
||||
|
||||
attachmentInfoLines.push(att.url);
|
||||
attachmentInfoLines.push('');
|
||||
attachmentInfoLines.push('');
|
||||
});
|
||||
|
||||
message.message += '\r\n\r\n' + attachmentInfoLines.join('\r\n');
|
||||
}
|
||||
|
||||
// If the Note is marked sensitive, prefix the subject
|
||||
if (this.sensitive) {
|
||||
message.subject = `[NSFW] ${message.subject}`;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -233,4 +270,28 @@ module.exports = class Note extends ActivityPubObject {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
_getSubject(message) {
|
||||
if (this.summary) {
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
//
|
||||
// Build a subject from the message itself:
|
||||
// - First few characters of the message, removing the @username
|
||||
// prefix, if any
|
||||
// - Truncate at the first line feed, the end of the message,
|
||||
// or 32 characters in length, whichever comes first
|
||||
// - If not end of string, we'll sub in '...'
|
||||
//
|
||||
let subject = message.message.replace(`@${message.toUserName} `, '').trim();
|
||||
const m = /^(.+)\r?\n/.exec(subject);
|
||||
if (m && m[1]) {
|
||||
subject = m[1];
|
||||
}
|
||||
|
||||
subject = truncate(subject, { length: 32, omission: '...' });
|
||||
subject = subject || APDefaultSummary;
|
||||
return subject;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -211,8 +211,9 @@ function messageBodyToHtml(body) {
|
|||
}
|
||||
|
||||
function htmlToMessageBody(html) {
|
||||
// <br>, </br>, and <br/> -> \r\n
|
||||
html = html.replace(/<\/?br?\/?>/g, '\r\n');
|
||||
// <br>, </br>, and <br />, <br/> -> \r\n
|
||||
// </p> -> \r\n
|
||||
html = html.replace(/(?:<\/?br ?\/?>)|(?:<\/p>)/g, '\r\n');
|
||||
return striptags(decode(html));
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ exports.Errors = {
|
|||
Expired: (reason, reasonCode) => new EnigError('Expired', -32015, reason, reasonCode),
|
||||
BadFormData: (reason, reasonCode) =>
|
||||
new EnigError('Bad or missing form data', -32016, reason, reasonCode),
|
||||
Duplicate: (reason, reasonCode) =>
|
||||
new EnigError('Duplicate', -32017, reason, reasonCode),
|
||||
};
|
||||
|
||||
exports.ErrorReasons = {
|
||||
|
|
|
@ -100,6 +100,7 @@ exports.getModule = class ActivityPubScannerTosser extends MessageScanTossModule
|
|||
activity,
|
||||
message.isPrivate(),
|
||||
this._webServer(),
|
||||
false, // do not ignore dupes
|
||||
(err, localId) => {
|
||||
if (!err) {
|
||||
this.log.debug(
|
||||
|
@ -145,11 +146,16 @@ exports.getModule = class ActivityPubScannerTosser extends MessageScanTossModule
|
|||
},
|
||||
],
|
||||
(err, activity) => {
|
||||
// dupes aren't considered failure
|
||||
if (err) {
|
||||
this.log.error(
|
||||
{ error: err.message, messageId: message.messageId },
|
||||
'Failed to export message to ActivityPub'
|
||||
);
|
||||
if (err.code === 'SQLITE_CONSTRAINT') {
|
||||
this.log.debug({ id: activity.id }, 'Ignoring duplicate');
|
||||
} else {
|
||||
this.log.error(
|
||||
{ error: err.message, messageId: message.messageId },
|
||||
'Failed to export message to ActivityPub'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.log.info(
|
||||
{ id: activity.id },
|
||||
|
|
|
@ -281,14 +281,6 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
|
||||
_sharedInboxCreateActivity(req, resp, activity) {
|
||||
const deliverTo = activity.recipientIds();
|
||||
|
||||
//Create a method to gather all to, cc, bcc, etc. dests (see spec) -> single array
|
||||
// loop through, and attempt to fetch user-by-actor id for each; if found, deliver
|
||||
// --we may need to add properties for ActivityPubFollowersId, ActivityPubFollowingId, etc.
|
||||
// to user props for quick lookup -> user
|
||||
// special handling of bcc (remove others before delivery), etc.
|
||||
// const toActorIds = activity.recipientActorIds()
|
||||
|
||||
const createWhat = _.get(activity, 'object.type');
|
||||
switch (createWhat) {
|
||||
case 'Note':
|
||||
|
@ -322,7 +314,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
switch (actorId) {
|
||||
case Collection.PublicCollectionId:
|
||||
// :TODO: we should probably land this in a public areaTag as well for AP; allowing Message objects to be used/etc.
|
||||
Collection.addPublicInboxItem(note, err => {
|
||||
Collection.addPublicInboxItem(note, true, err => {
|
||||
return nextActor(err);
|
||||
});
|
||||
break;
|
||||
|
@ -342,7 +334,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
}
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
if (err && err.code !== 'SQLITE_CONSTRAINT') {
|
||||
return this.webServer.internalServerError(resp, err);
|
||||
}
|
||||
|
||||
|
@ -357,7 +349,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
return cb(null); // not found/etc., just bail
|
||||
}
|
||||
|
||||
Collection.addInboxItem(note, localUser, this.webServer, err => {
|
||||
Collection.addInboxItem(note, localUser, this.webServer, false, err => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
@ -388,6 +380,8 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
},
|
||||
'Note delivered as message to private mailbox'
|
||||
);
|
||||
} else if (err.code === 'SQLITE_CONSTRAINT') {
|
||||
return cb(null);
|
||||
}
|
||||
return cb(err);
|
||||
});
|
||||
|
@ -517,13 +511,19 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
this._recordAcceptedFollowRequest(localUser, remoteActor, activity);
|
||||
return ok();
|
||||
} else {
|
||||
Collection.addFollowRequest(localUser, remoteActor, this.webServer, err => {
|
||||
if (err) {
|
||||
return this.internalServerError(resp, err);
|
||||
}
|
||||
Collection.addFollowRequest(
|
||||
localUser,
|
||||
remoteActor,
|
||||
this.webServer,
|
||||
true, // ignore dupes
|
||||
err => {
|
||||
if (err) {
|
||||
return this.internalServerError(resp, err);
|
||||
}
|
||||
|
||||
return ok();
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -631,6 +631,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
localUser,
|
||||
remoteActor,
|
||||
this.webServer,
|
||||
true, // ignore dupes
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
@ -706,7 +707,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
}
|
||||
|
||||
_selfAsActorHandler(localUser, localActor, req, resp) {
|
||||
this.log.trace(
|
||||
this.log.info(
|
||||
{ username: localUser.username },
|
||||
`Serving ActivityPub Actor for "${localUser.username}"`
|
||||
);
|
||||
|
@ -744,6 +745,11 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
|
|||
return this.webServer.resourceNotFound(resp);
|
||||
}
|
||||
|
||||
this.log.info(
|
||||
{ username: localUser.username },
|
||||
`Serving ActivityPub Profile for "${localUser.username}"`
|
||||
);
|
||||
|
||||
const headers = {
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': Buffer(body).length,
|
||||
|
|
Loading…
Reference in New Issue