Split message consts to their own file, fix some HTTP responses, better subjects from ActivityPub messages, fix AP reply indicators, ...

This commit is contained in:
Bryan Ashby 2023-01-26 15:42:11 -07:00
parent 0bd2c3db1c
commit d624871a83
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
5 changed files with 150 additions and 128 deletions

View File

@ -171,10 +171,18 @@ 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.summary ||
truncate(message.message, { length: 32, omission: '...' }) ||
APDefaultSummary;
// 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;
}
try {
message.modTimestamp = moment(this.published);

View File

@ -3,12 +3,13 @@
const EnigmaAssert = require('./enigma_assert.js');
const Address = require('./ftn_address.js');
const { AddressFlavor } = require('./message.js');
const Message = require('./message.js');
const MessageConst = require('./message_const');
const { getQuotePrefix } = require('./ftn_util');
exports.getAddressedToInfo = getAddressedToInfo;
exports.setExternalAddressedToInfo = setExternalAddressedToInfo;
exports.copyExternalAddressedToInfo = copyExternalAddressedToInfo;
exports.getQuotePrefixFromName = getQuotePrefixFromName;
const EMAIL_REGEX =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[?[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}]?)|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
@ -35,29 +36,29 @@ function getAddressedToInfo(input) {
if (firstAtPos < 0) {
let addr = Address.fromString(input);
if (Address.isValidAddress(addr)) {
return { flavor: AddressFlavor.FTN, remote: input };
return { flavor: MessageConst.AddressFlavor.FTN, remote: input };
}
const lessThanPos = input.indexOf('<');
if (lessThanPos < 0) {
return { name: input, flavor: AddressFlavor.Local };
return { name: input, flavor: MessageConst.AddressFlavor.Local };
}
const greaterThanPos = input.indexOf('>');
if (greaterThanPos < lessThanPos) {
return { name: input, flavor: AddressFlavor.Local };
return { name: input, flavor: MessageConst.AddressFlavor.Local };
}
addr = Address.fromString(input.slice(lessThanPos + 1, greaterThanPos));
if (Address.isValidAddress(addr)) {
return {
name: input.slice(0, lessThanPos).trim(),
flavor: AddressFlavor.FTN,
flavor: MessageConst.AddressFlavor.FTN,
remote: addr.toString(),
};
}
return { name: input, flavor: AddressFlavor.Local };
return { name: input, flavor: MessageConst.AddressFlavor.Local };
}
if (firstAtPos === 0) {
@ -67,7 +68,7 @@ function getAddressedToInfo(input) {
if (m) {
return {
name: input.slice(1, secondAtPos),
flavor: AddressFlavor.ActivityPub,
flavor: MessageConst.AddressFlavor.ActivityPub,
remote: input.slice(firstAtPos),
};
}
@ -82,38 +83,38 @@ function getAddressedToInfo(input) {
if (m) {
return {
name: input.slice(0, lessThanPos).trim(),
flavor: AddressFlavor.Email,
flavor: MessageConst.AddressFlavor.Email,
remote: addr,
};
}
return { name: input, flavor: AddressFlavor.Local };
return { name: input, flavor: MessageConst.AddressFlavor.Local };
}
let m = input.match(EMAIL_REGEX);
if (m) {
return {
name: input.slice(0, firstAtPos),
flavor: AddressFlavor.Email,
flavor: MessageConst.AddressFlavor.Email,
remote: input,
};
}
let addr = Address.fromString(input); // 5D?
if (Address.isValidAddress(addr)) {
return { flavor: AddressFlavor.FTN, remote: addr.toString() };
return { flavor: MessageConst.AddressFlavor.FTN, remote: addr.toString() };
}
addr = Address.fromString(input.slice(firstAtPos + 1).trim());
if (Address.isValidAddress(addr)) {
return {
name: input.slice(0, firstAtPos).trim(),
flavor: AddressFlavor.FTN,
flavor: MessageConst.AddressFlavor.FTN,
remote: addr.toString(),
};
}
return { name: input, flavor: AddressFlavor.Local };
return { name: input, flavor: MessageConst.AddressFlavor.Local };
}
/// returns true if it's an external address
@ -123,11 +124,11 @@ function setExternalAddressedToInfo(addressInfo, message) {
};
switch (addressInfo.flavor) {
case AddressFlavor.FTN:
case AddressFlavor.Email:
case AddressFlavor.QWK:
case AddressFlavor.NNTP:
case AddressFlavor.ActivityPub:
case MessageConst.AddressFlavor.FTN:
case MessageConst.AddressFlavor.Email:
case MessageConst.AddressFlavor.QWK:
case MessageConst.AddressFlavor.NNTP:
case MessageConst.AddressFlavor.ActivityPub:
EnigmaAssert(isValidAddressInfo());
message.setRemoteToUser(addressInfo.remote);
@ -136,13 +137,18 @@ function setExternalAddressedToInfo(addressInfo, message) {
return true;
default:
case AddressFlavor.Local:
case MessageConst.AddressFlavor.Local:
return false;
}
}
function copyExternalAddressedToInfo(fromMessage, toMessage) {
const sm = Message.SystemMetaNames;
const sm = MessageConst.SystemMetaNames;
toMessage.setRemoteToUser(fromMessage.meta.System[sm.RemoteFromUser]);
toMessage.setExternalFlavor(fromMessage.meta.System[sm.ExternalFlavor]);
}
function getQuotePrefixFromName(name) {
const addrInfo = getAddressedToInfo(name);
return getQuotePrefix(addrInfo.name || name);
}

View File

@ -3,14 +3,14 @@
const msgDb = require('./database.js').dbs.message;
const wordWrapText = require('./word_wrap.js').wordWrapText;
const ftnUtil = require('./ftn_util.js');
const createNamedUUID = require('./uuid_util.js').createNamedUUID;
const Errors = require('./enig_error.js').Errors;
const ANSI = require('./ansi_term.js');
const { sanitizeString, getISOTimestampString } = require('./database.js');
const { isCP437Encodable } = require('./cp437util');
const { containsNonLatinCodepoints } = require('./string_util');
const MessageConst = require('./message_const');
const { getQuotePrefixFromName } = require('./mail_util');
const {
isAnsi,
@ -33,93 +33,6 @@ const ENIGMA_MESSAGE_UUID_NAMESPACE = uuidParse.parse(
'154506df-1df8-46b9-98f8-ebb5815baaf8'
);
const WELL_KNOWN_AREA_TAGS = {
Invalid: '',
Private: 'private_mail',
Bulletin: 'local_bulletin',
};
const WellKnownMetaCategories = {
System: 'System',
FtnProperty: 'FtnProperty',
FtnKludge: 'FtnKludge',
QwkProperty: 'QwkProperty',
QwkKludge: 'QwkKludge',
ActivityPub: 'ActivityPub',
};
// Category: WellKnownMetaCategories.System ("System")
const SYSTEM_META_NAMES = {
LocalToUserID: 'local_to_user_id',
LocalFromUserID: 'local_from_user_id',
StateFlags0: 'state_flags0', // See Message.StateFlags0
ExplicitEncoding: 'explicit_encoding', // Explicitly set encoding when exporting/etc.
ExternalFlavor: 'external_flavor', // "Flavor" of message - imported from or to be exported to. See Message.AddressFlavor
RemoteToUser: 'remote_to_user', // Opaque value depends on external system, e.g. FTN address
RemoteFromUser: 'remote_from_user', // Opaque value depends on external system, e.g. FTN address
};
// Types for Message.SystemMetaNames.ExternalFlavor meta
const ADDRESS_FLAVOR = {
Local: 'local', // local / non-remote addressing
FTN: 'ftn', // FTN style
Email: 'email', // From email
QWK: 'qwk', // QWK packet
NNTP: 'nntp', // NNTP article POST; often a email address
ActivityPub: 'activitypub', // ActivityPub, Mastodon, etc.
};
const STATE_FLAGS0 = {
None: 0x00000000,
Imported: 0x00000001, // imported from foreign system
Exported: 0x00000002, // exported to foreign system
};
// :TODO: these should really live elsewhere...
// Category: WellKnownMetaCategories.FtnProperty ("FtnProperty")
const FTN_PROPERTY_NAMES = {
// packet header oriented
FtnOrigNode: 'ftn_orig_node',
FtnDestNode: 'ftn_dest_node',
// :TODO: rename these to ftn_*_net vs network - ensure things won't break, may need mapping
FtnOrigNetwork: 'ftn_orig_network',
FtnDestNetwork: 'ftn_dest_network',
FtnAttrFlags: 'ftn_attr_flags',
FtnCost: 'ftn_cost',
FtnOrigZone: 'ftn_orig_zone',
FtnDestZone: 'ftn_dest_zone',
FtnOrigPoint: 'ftn_orig_point',
FtnDestPoint: 'ftn_dest_point',
// message header oriented
FtnMsgOrigNode: 'ftn_msg_orig_node',
FtnMsgDestNode: 'ftn_msg_dest_node',
FtnMsgOrigNet: 'ftn_msg_orig_net',
FtnMsgDestNet: 'ftn_msg_dest_net',
FtnAttribute: 'ftn_attribute',
FtnTearLine: 'ftn_tear_line', // http://ftsc.org/docs/fts-0004.001
FtnOrigin: 'ftn_origin', // http://ftsc.org/docs/fts-0004.001
FtnArea: 'ftn_area', // http://ftsc.org/docs/fts-0004.001
FtnSeenBy: 'ftn_seen_by', // http://ftsc.org/docs/fts-0004.001
};
// Category: WellKnownMetaCategories.QwkProperty
const QWKPropertyNames = {
MessageNumber: 'qwk_msg_num',
MessageStatus: 'qwk_msg_status', // See http://wiki.synchro.net/ref:qwk for a decent list
ConferenceNumber: 'qwk_conf_num',
InReplyToNum: 'qwk_in_reply_to_num', // note that we prefer the 'InReplyToMsgId' kludge if available
};
// Category: WellKnownMetaCategories.ActivityPub
const ActivityPubPropertyNames = {
ActivityId: 'activitypub_activity_id', // Activity ID; FK to AP table entries
InReplyTo: 'activitypub_in_reply_to', // Activity ID from 'inReplyTo' field
NoteId: 'activitypub_note_id', // Note ID specific to Note Activities
};
// :TODO: this is a ugly hack due to bad variable names - clean it up & just _.camelCase(k)!
const MESSAGE_ROW_MAP = {
reply_to_message_id: 'replyToMsgId',
@ -247,35 +160,35 @@ module.exports = class Message {
}
static get WellKnownMetaCategories() {
return WellKnownMetaCategories;
return MessageConst.WellKnownMetaCategories;
}
static get WellKnownAreaTags() {
return WELL_KNOWN_AREA_TAGS;
return MessageConst.WellKnownAreaTags;
}
static get SystemMetaNames() {
return SYSTEM_META_NAMES;
return MessageConst.SystemMetaNames;
}
static get AddressFlavor() {
return ADDRESS_FLAVOR;
return MessageConst.AddressFlavor;
}
static get StateFlags0() {
return STATE_FLAGS0;
return MessageConst.StateFlags0;
}
static get FtnPropertyNames() {
return FTN_PROPERTY_NAMES;
return MessageConst.FtnPropertyNames;
}
static get QWKPropertyNames() {
return QWKPropertyNames;
return MessageConst.QWKPropertyNames;
}
static get ActivityPubPropertyNames() {
return ActivityPubPropertyNames;
return MessageConst.ActivityPubPropertyNames;
}
setLocalToUserId(userId) {
@ -943,11 +856,13 @@ module.exports = class Message {
);
}
// :TODO: FTN stuff doesn't have any business here
getFTNQuotePrefix(source) {
_getQuotePrefix(source) {
source = source || 'fromUserName';
return ftnUtil.getQuotePrefix(this[source]);
// grab out the name member, so we don't try to build
// quote prefixes such as "@N" for "@NuSkooler@some.host", etc.
const userName = this[source];
return getQuotePrefixFromName(userName);
}
static getTearLinePosition(input) {
@ -982,7 +897,7 @@ module.exports = class Message {
*/
const quotePrefix = options.includePrefix
? this.getFTNQuotePrefix(options.prefixSource || 'fromUserName')
? this._getQuotePrefix(options.prefixSource || 'fromUserName')
: '';
function getWrapped(text, extraPrefix) {

93
core/message_const.js Normal file
View File

@ -0,0 +1,93 @@
const WellKnownAreaTags = {
Invalid: '',
Private: 'private_mail',
Bulletin: 'local_bulletin',
};
exports.WellKnownAreaTags = WellKnownAreaTags;
const WellKnownMetaCategories = {
System: 'System',
FtnProperty: 'FtnProperty',
FtnKludge: 'FtnKludge',
QwkProperty: 'QwkProperty',
QwkKludge: 'QwkKludge',
ActivityPub: 'ActivityPub',
};
exports.WellKnownMetaCategories = WellKnownMetaCategories;
// Category: WellKnownMetaCategories.System ("System")
const SystemMetaNames = {
LocalToUserID: 'local_to_user_id',
LocalFromUserID: 'local_from_user_id',
StateFlags0: 'state_flags0', // See Message.StateFlags0
ExplicitEncoding: 'explicit_encoding', // Explicitly set encoding when exporting/etc.
ExternalFlavor: 'external_flavor', // "Flavor" of message - imported from or to be exported to. See Message.AddressFlavor
RemoteToUser: 'remote_to_user', // Opaque value depends on external system, e.g. FTN address
RemoteFromUser: 'remote_from_user', // Opaque value depends on external system, e.g. FTN address
};
exports.SystemMetaNames = SystemMetaNames;
// Types for Message.SystemMetaNames.ExternalFlavor meta
const AddressFlavor = {
Local: 'local', // local / non-remote addressing
FTN: 'ftn', // FTN style
Email: 'email', // From email
QWK: 'qwk', // QWK packet
NNTP: 'nntp', // NNTP article POST; often a email address
ActivityPub: 'activitypub', // ActivityPub, Mastodon, etc.
};
exports.AddressFlavor = AddressFlavor;
const StateFlags0 = {
None: 0x00000000,
Imported: 0x00000001, // imported from foreign system
Exported: 0x00000002, // exported to foreign system
};
exports.StateFlags0 = StateFlags0;
// Category: WellKnownMetaCategories.FtnProperty ("FtnProperty")
const FtnPropertyNames = {
// packet header oriented
FtnOrigNode: 'ftn_orig_node',
FtnDestNode: 'ftn_dest_node',
// :TODO: rename these to ftn_*_net vs network - ensure things won't break, may need mapping
FtnOrigNetwork: 'ftn_orig_network',
FtnDestNetwork: 'ftn_dest_network',
FtnAttrFlags: 'ftn_attr_flags',
FtnCost: 'ftn_cost',
FtnOrigZone: 'ftn_orig_zone',
FtnDestZone: 'ftn_dest_zone',
FtnOrigPoint: 'ftn_orig_point',
FtnDestPoint: 'ftn_dest_point',
// message header oriented
FtnMsgOrigNode: 'ftn_msg_orig_node',
FtnMsgDestNode: 'ftn_msg_dest_node',
FtnMsgOrigNet: 'ftn_msg_orig_net',
FtnMsgDestNet: 'ftn_msg_dest_net',
FtnAttribute: 'ftn_attribute',
FtnTearLine: 'ftn_tear_line', // http://ftsc.org/docs/fts-0004.001
FtnOrigin: 'ftn_origin', // http://ftsc.org/docs/fts-0004.001
FtnArea: 'ftn_area', // http://ftsc.org/docs/fts-0004.001
FtnSeenBy: 'ftn_seen_by', // http://ftsc.org/docs/fts-0004.001
};
exports.FtnPropertyNames = FtnPropertyNames;
// Category: WellKnownMetaCategories.QwkProperty
const QWKPropertyNames = {
MessageNumber: 'qwk_msg_num',
MessageStatus: 'qwk_msg_status', // See http://wiki.synchro.net/ref:qwk for a decent list
ConferenceNumber: 'qwk_conf_num',
InReplyToNum: 'qwk_in_reply_to_num', // note that we prefer the 'InReplyToMsgId' kludge if available
};
exports.QWKPropertyNames = QWKPropertyNames;
// Category: WellKnownMetaCategories.ActivityPub
const ActivityPubPropertyNames = {
ActivityId: 'activitypub_activity_id', // Activity ID; FK to AP table entries
InReplyTo: 'activitypub_in_reply_to', // Activity ID from 'inReplyTo' field
NoteId: 'activitypub_note_id', // Note ID specific to Note Activities
};
exports.ActivityPubPropertyNames = ActivityPubPropertyNames;

View File

@ -258,7 +258,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
{ type: activity.type },
'Invalid or unknown Activity type'
);
return this.resourceNotFound(resp);
return this.webServer.resourceNotFound(resp);
}
});
}
@ -279,7 +279,7 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
{ type: createWhat },
'Invalid or unsupported "Create" type'
);
return this.resourceNotFound(resp);
return this.webServer.resourceNotFound(resp);
}
}