2015-07-11 22:39:42 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
2015-07-12 06:32:31 +00:00
|
|
|
var msgDb = require('./database.js').dbs.message;
|
2015-09-16 03:55:10 +00:00
|
|
|
var wordWrapText = require('./word_wrap.js').wordWrapText;
|
2015-09-21 01:10:09 +00:00
|
|
|
var ftnUtil = require('./ftn_util.js');
|
2015-07-12 06:32:31 +00:00
|
|
|
|
|
|
|
var uuid = require('node-uuid');
|
|
|
|
var async = require('async');
|
|
|
|
var _ = require('lodash');
|
2015-07-13 04:56:33 +00:00
|
|
|
var assert = require('assert');
|
|
|
|
|
2015-07-16 05:51:00 +00:00
|
|
|
module.exports = Message;
|
2015-07-12 06:32:31 +00:00
|
|
|
|
2015-07-11 22:39:42 +00:00
|
|
|
function Message(options) {
|
2015-09-01 05:18:46 +00:00
|
|
|
options = options || {};
|
2015-07-16 05:51:00 +00:00
|
|
|
|
2015-07-13 04:56:33 +00:00
|
|
|
this.messageId = options.messageId || 0; // always generated @ persist
|
2015-08-20 23:00:35 +00:00
|
|
|
this.areaName = options.areaName || Message.WellKnownAreaNames.Invalid;
|
2015-07-13 04:56:33 +00:00
|
|
|
this.uuid = uuid.v1();
|
|
|
|
this.replyToMsgId = options.replyToMsgId || 0;
|
|
|
|
this.toUserName = options.toUserName || '';
|
|
|
|
this.fromUserName = options.fromUserName || '';
|
|
|
|
this.subject = options.subject || '';
|
|
|
|
this.message = options.message || '';
|
2015-07-16 23:13:48 +00:00
|
|
|
|
|
|
|
if(_.isDate(options.modTimestamp)) {
|
|
|
|
this.modTimestamp = options.modTimestamp;
|
|
|
|
} else if(_.isString(options.modTimestamp)) {
|
|
|
|
this.modTimestamp = new Date(options.modTimestamp);
|
|
|
|
}
|
|
|
|
|
2015-07-13 04:56:33 +00:00
|
|
|
this.viewCount = options.viewCount || 0;
|
2015-07-17 04:57:08 +00:00
|
|
|
|
|
|
|
this.meta = {
|
|
|
|
system : {}, // we'll always have this one
|
|
|
|
};
|
|
|
|
|
|
|
|
if(_.isObject(options.meta)) {
|
|
|
|
_.defaultsDeep(this.meta, options.meta);
|
|
|
|
}
|
|
|
|
|
2015-08-20 04:10:18 +00:00
|
|
|
if(options.meta) {
|
|
|
|
this.meta = options.meta;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this.meta = options.meta || {};
|
2015-07-13 04:56:33 +00:00
|
|
|
this.hashTags = options.hashTags || [];
|
2015-07-12 06:32:31 +00:00
|
|
|
|
2015-07-13 04:56:33 +00:00
|
|
|
var self = this;
|
2015-07-12 06:32:31 +00:00
|
|
|
|
|
|
|
this.isValid = function() {
|
|
|
|
// :TODO: validate as much as possible
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2015-07-16 23:13:48 +00:00
|
|
|
this.getMessageTimestampString = function(ts) {
|
|
|
|
ts = ts || new Date();
|
|
|
|
return ts.toISOString();
|
2015-07-12 06:32:31 +00:00
|
|
|
};
|
2015-07-13 04:56:33 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Object.defineProperty(this, 'messageId', {
|
|
|
|
get : function() {
|
|
|
|
return messageId;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.defineProperty(this, 'areaId', {
|
|
|
|
get : function() { return areaId; },
|
|
|
|
set : function(i) {
|
|
|
|
areaId = i;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
*/
|
2015-07-12 06:32:31 +00:00
|
|
|
}
|
|
|
|
|
2015-08-20 23:00:35 +00:00
|
|
|
Message.WellKnownAreaNames = {
|
|
|
|
Invalid : '',
|
|
|
|
Private : 'private_mail'
|
2015-07-13 04:56:33 +00:00
|
|
|
};
|
|
|
|
|
2015-08-28 04:20:24 +00:00
|
|
|
Message.Status = {
|
|
|
|
New : 0,
|
|
|
|
Read : 1,
|
|
|
|
};
|
|
|
|
|
2015-07-17 04:57:08 +00:00
|
|
|
Message.MetaCategories = {
|
2015-07-17 14:07:43 +00:00
|
|
|
System : 1, // ENiGMA1/2 stuff
|
|
|
|
FtnProperty : 2, // Various FTN network properties, ftn_cost, ftn_origin, ...
|
|
|
|
FtnKludge : 3, // FTN kludges -- PATH, MSGID, ...
|
2015-07-17 04:57:08 +00:00
|
|
|
};
|
2015-07-13 04:56:33 +00:00
|
|
|
|
2015-07-17 04:57:08 +00:00
|
|
|
Message.SystemMetaNames = {
|
2015-07-13 04:56:33 +00:00
|
|
|
LocalToUserID : 'local_to_user_id',
|
|
|
|
LocalFromUserID : 'local_from_user_id',
|
2015-07-17 04:57:08 +00:00
|
|
|
};
|
2015-07-13 04:56:33 +00:00
|
|
|
|
2015-07-17 04:57:08 +00:00
|
|
|
Message.FtnPropertyNames = {
|
|
|
|
FtnCost : 'ftn_cost',
|
|
|
|
FtnOrigNode : 'ftn_orig_node',
|
|
|
|
FtnDestNode : 'ftn_dest_node',
|
|
|
|
FtnOrigNetwork : 'ftn_orig_network',
|
|
|
|
FtnDestNetwork : 'ftn_dest_network',
|
|
|
|
FtnOrigZone : 'ftn_orig_zone',
|
|
|
|
FtnDestZone : 'ftn_dest_zone',
|
|
|
|
FtnOrigPoint : 'ftn_orig_point',
|
|
|
|
FtnDestPoint : 'ftn_dest_point',
|
|
|
|
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
|
2015-07-16 23:13:48 +00:00
|
|
|
};
|
|
|
|
|
2015-07-17 14:07:43 +00:00
|
|
|
// Note: kludges are stored with their names as-is
|
2015-07-17 04:57:08 +00:00
|
|
|
|
2015-07-13 04:56:33 +00:00
|
|
|
Message.prototype.setLocalToUserId = function(userId) {
|
2015-07-17 04:57:08 +00:00
|
|
|
this.meta.system.local_to_user_id = userId;
|
2015-07-13 04:56:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Message.prototype.setLocalFromUserId = function(userId) {
|
2015-07-17 04:57:08 +00:00
|
|
|
this.meta.system.local_from_user_id = userId;
|
2015-07-13 04:56:33 +00:00
|
|
|
};
|
|
|
|
|
2015-09-01 05:18:46 +00:00
|
|
|
Message.prototype.load = function(options, cb) {
|
|
|
|
assert(_.isString(options.uuid));
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
async.series(
|
|
|
|
[
|
|
|
|
function loadMessage(callback) {
|
|
|
|
msgDb.get(
|
|
|
|
'SELECT message_id, area_name, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, ' +
|
|
|
|
'message, modified_timestamp, view_count ' +
|
|
|
|
'FROM message ' +
|
|
|
|
'WHERE message_uuid=? ' +
|
|
|
|
'LIMIT 1;',
|
|
|
|
[ options.uuid ],
|
|
|
|
function row(err, msgRow) {
|
|
|
|
self.messageId = msgRow.message_id;
|
|
|
|
self.areaName = msgRow.area_name;
|
|
|
|
self.messageUuid = msgRow.message_uuid;
|
|
|
|
self.replyToMsgId = msgRow.reply_to_message_id;
|
|
|
|
self.toUserName = msgRow.to_user_name;
|
|
|
|
self.fromUserName = msgRow.from_user_name;
|
|
|
|
self.subject = msgRow.subject;
|
|
|
|
self.message = msgRow.message;
|
|
|
|
self.modTimestamp = msgRow.modified_timestamp;
|
|
|
|
self.viewCount = msgRow.view_count;
|
|
|
|
|
|
|
|
callback(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
function loadMessageMeta(callback) {
|
|
|
|
// :TODO:
|
|
|
|
callback(null);
|
|
|
|
},
|
|
|
|
function loadHashTags(callback) {
|
|
|
|
// :TODO:
|
|
|
|
callback(null);
|
|
|
|
},
|
|
|
|
function loadMessageStatus(callback) {
|
|
|
|
if(options.user) {
|
|
|
|
// :TODO: Load from user_message_status
|
|
|
|
}
|
|
|
|
callback(null);
|
|
|
|
}
|
|
|
|
],
|
|
|
|
function complete(err) {
|
|
|
|
cb(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2015-07-12 06:32:31 +00:00
|
|
|
Message.prototype.persist = function(cb) {
|
|
|
|
|
|
|
|
if(!this.isValid()) {
|
|
|
|
cb(new Error('Cannot persist invalid message!'));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
async.series(
|
|
|
|
[
|
|
|
|
function beginTransaction(callback) {
|
|
|
|
msgDb.run('BEGIN;', function transBegin(err) {
|
|
|
|
callback(err);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
function storeMessage(callback) {
|
|
|
|
msgDb.run(
|
2015-08-20 23:00:35 +00:00
|
|
|
'INSERT INTO message (area_name, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, message, modified_timestamp) ' +
|
|
|
|
'VALUES (?, ?, ?, ?, ?, ?, ?, ?);', [ self.areaName, self.uuid, self.replyToMsgId, self.toUserName, self.fromUserName, self.subject, self.message, self.getMessageTimestampString(self.modTimestamp) ],
|
2015-07-12 06:32:31 +00:00
|
|
|
function msgInsert(err) {
|
|
|
|
if(!err) {
|
|
|
|
self.messageId = this.lastID;
|
|
|
|
}
|
|
|
|
|
|
|
|
callback(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
function storeMeta(callback) {
|
|
|
|
if(!self.meta) {
|
|
|
|
callback(null);
|
|
|
|
} else {
|
|
|
|
// :TODO: this should be it's own method such that meta can be updated
|
|
|
|
var metaStmt = msgDb.prepare(
|
2015-07-17 04:57:08 +00:00
|
|
|
'INSERT INTO message_meta (message_id, meta_category, meta_name, meta_value) ' +
|
|
|
|
'VALUES (?, ?, ?, ?);');
|
2015-07-12 06:32:31 +00:00
|
|
|
|
2015-07-17 04:57:08 +00:00
|
|
|
for(var metaCategroy in self.meta) {
|
|
|
|
async.each(Object.keys(self.meta[metaCategroy]), function meta(metaName, next) {
|
2015-07-17 14:07:43 +00:00
|
|
|
metaStmt.run(self.messageId, Message.MetaCategories[metaCategroy], metaName, self.meta[metaCategroy][metaName], function inserted(err) {
|
2015-07-17 04:57:08 +00:00
|
|
|
next(err);
|
2015-07-12 06:32:31 +00:00
|
|
|
});
|
2015-07-17 04:57:08 +00:00
|
|
|
}, function complete(err) {
|
|
|
|
if(!err) {
|
|
|
|
metaStmt.finalize(function finalized() {
|
|
|
|
callback(null);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
callback(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-07-12 06:32:31 +00:00
|
|
|
}
|
2015-07-13 04:56:33 +00:00
|
|
|
},
|
|
|
|
function storeHashTags(callback) {
|
|
|
|
// :TODO: hash tag support
|
|
|
|
callback(null);
|
2015-07-12 06:32:31 +00:00
|
|
|
}
|
2015-07-13 04:56:33 +00:00
|
|
|
],
|
|
|
|
function complete(err) {
|
|
|
|
msgDb.run(err ? 'ROLLBACK;' : 'COMMIT;', function transEnd(err) {
|
|
|
|
cb(err, self.messageId);
|
|
|
|
});
|
|
|
|
}
|
2015-07-12 06:32:31 +00:00
|
|
|
);
|
2015-09-16 03:55:10 +00:00
|
|
|
};
|
|
|
|
|
2015-09-17 04:31:09 +00:00
|
|
|
Message.prototype.getFTNQuotePrefix = function(source) {
|
|
|
|
source = source || 'fromUserName';
|
|
|
|
|
2015-09-21 01:10:09 +00:00
|
|
|
return ftnUtil.getQuotePrefix(this[source]);
|
2015-09-17 04:31:09 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Message.prototype.getQuoteLines = function(width, options) {
|
2015-09-16 04:44:31 +00:00
|
|
|
// :TODO: options.maxBlankLines = 1
|
2015-09-17 04:31:09 +00:00
|
|
|
|
|
|
|
options = options || {};
|
2015-09-16 04:44:31 +00:00
|
|
|
|
2015-09-17 04:31:09 +00:00
|
|
|
//
|
|
|
|
// Include FSC-0032 style quote prefixes?
|
|
|
|
//
|
|
|
|
// See http://ftsc.org/docs/fsc-0032.001
|
|
|
|
//
|
|
|
|
if(!_.isBoolean(options.includePrefix)) {
|
|
|
|
options.includePrefix = true;
|
|
|
|
}
|
|
|
|
|
2015-09-16 03:55:10 +00:00
|
|
|
var quoteLines = [];
|
|
|
|
|
|
|
|
var origLines = this.message
|
2015-09-20 04:55:09 +00:00
|
|
|
.trim()
|
2015-09-16 03:55:10 +00:00
|
|
|
.replace(/\b/g, '')
|
|
|
|
.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g);
|
|
|
|
|
2015-09-20 04:55:09 +00:00
|
|
|
var quotePrefix = ''; // we need this init even if blank
|
|
|
|
if(options.includePrefix) {
|
2015-09-21 01:10:09 +00:00
|
|
|
quotePrefix = this.getFTNQuotePrefix(options.prefixSource || 'fromUserName');
|
2015-09-20 04:55:09 +00:00
|
|
|
}
|
|
|
|
|
2015-09-16 03:55:10 +00:00
|
|
|
var wrapOpts = {
|
2015-09-20 04:55:09 +00:00
|
|
|
width : width - quotePrefix.length,
|
2015-09-16 03:55:10 +00:00
|
|
|
tabHandling : 'expand',
|
|
|
|
tabWidth : 4,
|
|
|
|
};
|
|
|
|
|
2015-09-20 04:55:09 +00:00
|
|
|
function addPrefix(l) {
|
|
|
|
return quotePrefix + l;
|
2015-09-17 04:31:09 +00:00
|
|
|
}
|
2015-09-16 03:55:10 +00:00
|
|
|
|
2015-09-20 04:55:09 +00:00
|
|
|
var wrapped;
|
2015-09-16 03:55:10 +00:00
|
|
|
for(var i = 0; i < origLines.length; ++i) {
|
2015-09-20 04:55:09 +00:00
|
|
|
wrapped = wordWrapText(origLines[i], wrapOpts).wrapped;
|
|
|
|
Array.prototype.push.apply(quoteLines, _.map(wrapped, addPrefix));
|
2015-09-16 03:55:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return quoteLines;
|
|
|
|
};
|