2020-05-11 03:56:05 +00:00
|
|
|
// ENiGMA½
|
|
|
|
const { MenuModule } = require('./menu_module');
|
|
|
|
const Message = require('./message');
|
|
|
|
const { Errors } = require('./enig_error');
|
|
|
|
const {
|
|
|
|
getMessageAreaByTag,
|
2020-05-12 15:12:55 +00:00
|
|
|
getMessageConferenceByTag,
|
2020-05-11 03:56:05 +00:00
|
|
|
hasMessageConfAndAreaRead,
|
2020-05-12 01:52:01 +00:00
|
|
|
getAllAvailableMessageAreaTags,
|
2020-05-11 03:56:05 +00:00
|
|
|
} = require('./message_area');
|
|
|
|
const FileArea = require('./file_base_area');
|
|
|
|
const { QWKPacketWriter } = require('./qwk_mail_packet');
|
|
|
|
const { renderSubstr } = require('./string_util');
|
|
|
|
const Config = require('./config').get;
|
|
|
|
const FileEntry = require('./file_entry');
|
|
|
|
const DownloadQueue = require('./download_queue');
|
2020-05-13 00:53:47 +00:00
|
|
|
const { getISOTimestampString } = require('./database');
|
2020-05-11 03:56:05 +00:00
|
|
|
|
|
|
|
// deps
|
|
|
|
const async = require('async');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const fse = require('fs-extra');
|
|
|
|
const temptmp = require('temptmp');
|
|
|
|
const paths = require('path');
|
2022-06-05 20:04:25 +00:00
|
|
|
const { v4: UUIDv4 } = require('uuid');
|
2020-05-13 00:53:47 +00:00
|
|
|
const moment = require('moment');
|
2020-05-11 03:56:05 +00:00
|
|
|
|
|
|
|
const FormIds = {
|
2022-06-05 20:04:25 +00:00
|
|
|
main: 0,
|
2020-05-11 03:56:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const MciViewIds = {
|
2022-06-05 20:04:25 +00:00
|
|
|
main: {
|
|
|
|
status: 1,
|
|
|
|
progressBar: 2,
|
2020-05-11 03:56:05 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
customRangeStart: 10,
|
|
|
|
},
|
2020-05-11 03:56:05 +00:00
|
|
|
};
|
|
|
|
|
2020-05-13 00:53:47 +00:00
|
|
|
const UserProperties = {
|
2022-06-05 20:04:25 +00:00
|
|
|
ExportOptions: 'qwk_export_options',
|
|
|
|
ExportAreas: 'qwk_export_msg_areas',
|
2020-05-13 00:53:47 +00:00
|
|
|
};
|
|
|
|
|
2020-05-11 03:56:05 +00:00
|
|
|
exports.moduleInfo = {
|
2022-06-05 20:04:25 +00:00
|
|
|
name: 'QWK Export',
|
|
|
|
desc: 'Exports a QWK Packet for download',
|
|
|
|
author: 'NuSkooler',
|
2020-05-11 03:56:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.getModule = class MessageBaseQWKExport extends MenuModule {
|
|
|
|
constructor(options) {
|
|
|
|
super(options);
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
this.config = Object.assign(
|
|
|
|
{},
|
|
|
|
_.get(options, 'menuConfig.config'),
|
|
|
|
options.extraArgs
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
this.config.progBarChar = renderSubstr(this.config.progBarChar || '▒', 0, 1);
|
|
|
|
this.config.bbsID =
|
|
|
|
this.config.bbsID || _.get(Config(), 'messageNetworks.qwk.bbsID', 'ENIGMA');
|
2020-05-11 03:56:05 +00:00
|
|
|
|
|
|
|
this.tempName = `${UUIDv4().substr(-8).toUpperCase()}.QWK`;
|
2022-06-05 20:04:25 +00:00
|
|
|
this.sysTempDownloadArea = FileArea.getFileAreaByTag(
|
|
|
|
FileArea.WellKnownAreaTags.TempDownloads
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mciReady(mciData, cb) {
|
|
|
|
super.mciReady(mciData, err => {
|
|
|
|
if (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
async.waterfall(
|
|
|
|
[
|
2022-06-05 20:04:25 +00:00
|
|
|
callback => {
|
|
|
|
this.prepViewController(
|
|
|
|
'main',
|
|
|
|
FormIds.main,
|
|
|
|
mciData.menu,
|
|
|
|
err => {
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
callback => {
|
2020-05-11 03:56:05 +00:00
|
|
|
this.temptmp = temptmp.createTrackedSession('qwkuserexp');
|
2022-06-05 20:04:25 +00:00
|
|
|
this.temptmp.mkdir(
|
|
|
|
{ prefix: 'enigqwkwriter-' },
|
|
|
|
(err, tempDir) => {
|
|
|
|
if (err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.tempPacketDir = tempDir;
|
|
|
|
|
|
|
|
const sysTempDownloadDir =
|
|
|
|
FileArea.getAreaDefaultStorageDirectory(
|
|
|
|
this.sysTempDownloadArea
|
|
|
|
);
|
|
|
|
|
|
|
|
// ensure dir exists
|
|
|
|
fse.mkdirs(sysTempDownloadDir, err => {
|
|
|
|
return callback(err, sysTempDownloadDir);
|
|
|
|
});
|
2020-05-11 03:56:05 +00:00
|
|
|
}
|
2022-06-05 20:04:25 +00:00
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
},
|
|
|
|
(sysTempDownloadDir, callback) => {
|
2020-05-12 01:52:01 +00:00
|
|
|
this._performExport(sysTempDownloadDir, err => {
|
2020-05-11 03:56:05 +00:00
|
|
|
return callback(err);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
],
|
|
|
|
err => {
|
|
|
|
this.temptmp.cleanup();
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
// :TODO: doesn't do anything currently:
|
|
|
|
if ('NORESULTS' === err.reasonCode) {
|
2022-06-05 20:04:25 +00:00
|
|
|
return this.gotoMenu(
|
|
|
|
this.menuConfig.config.noResultsMenu ||
|
|
|
|
'qwkExportNoResults'
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this.prevMenu();
|
|
|
|
}
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
finishedLoading() {
|
|
|
|
this.prevMenu();
|
|
|
|
}
|
|
|
|
|
2020-05-12 01:52:01 +00:00
|
|
|
_getUserQWKExportOptions() {
|
2020-05-13 00:53:47 +00:00
|
|
|
let qwkOptions = this.client.user.getProperty(UserProperties.ExportOptions);
|
2020-05-12 01:52:01 +00:00
|
|
|
try {
|
|
|
|
qwkOptions = JSON.parse(qwkOptions);
|
2022-06-05 20:04:25 +00:00
|
|
|
} catch (e) {
|
2020-05-12 01:52:01 +00:00
|
|
|
qwkOptions = {
|
2022-06-05 20:04:25 +00:00
|
|
|
enableQWKE: true,
|
|
|
|
enableHeadersExtension: true,
|
|
|
|
enableAtKludges: true,
|
|
|
|
archiveFormat: 'application/zip',
|
2020-05-12 01:52:01 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
return qwkOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
_getUserQWKExportAreas() {
|
2020-05-13 00:53:47 +00:00
|
|
|
let qwkExportAreas = this.client.user.getProperty(UserProperties.ExportAreas);
|
2020-05-12 01:52:01 +00:00
|
|
|
try {
|
2020-05-13 00:53:47 +00:00
|
|
|
qwkExportAreas = JSON.parse(qwkExportAreas).map(exportArea => {
|
|
|
|
if (exportArea.newerThanTimestamp) {
|
|
|
|
exportArea.newerThanTimestamp = moment(exportArea.newerThanTimestamp);
|
|
|
|
}
|
|
|
|
return exportArea;
|
|
|
|
});
|
2022-06-05 20:04:25 +00:00
|
|
|
} catch (e) {
|
2020-05-12 01:52:01 +00:00
|
|
|
// default to all public and private without 'since'
|
|
|
|
qwkExportAreas = getAllAvailableMessageAreaTags(this.client).map(areaTag => {
|
|
|
|
return { areaTag };
|
|
|
|
});
|
|
|
|
|
|
|
|
// Include user's private area
|
|
|
|
qwkExportAreas.push({
|
2022-06-05 20:04:25 +00:00
|
|
|
areaTag: Message.WellKnownAreaTags.Private,
|
2020-05-12 01:52:01 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return qwkExportAreas;
|
|
|
|
}
|
|
|
|
|
|
|
|
_performExport(sysTempDownloadDir, cb) {
|
2020-05-11 03:56:05 +00:00
|
|
|
const statusView = this.viewControllers.main.getView(MciViewIds.main.status);
|
2022-06-05 20:04:25 +00:00
|
|
|
const updateStatus = status => {
|
2020-05-11 03:56:05 +00:00
|
|
|
if (statusView) {
|
|
|
|
statusView.setText(status);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const progBarView = this.viewControllers.main.getView(
|
|
|
|
MciViewIds.main.progressBar
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
const updateProgressBar = (curr, total) => {
|
|
|
|
if (progBarView) {
|
2022-06-05 20:04:25 +00:00
|
|
|
const prog = Math.floor((curr / total) * progBarView.dimens.width);
|
2020-05-11 03:56:05 +00:00
|
|
|
progBarView.setText(this.config.progBarChar.repeat(prog));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let cancel = false;
|
|
|
|
|
|
|
|
let lastProgUpdate = 0;
|
|
|
|
const progressHandler = (state, next) => {
|
|
|
|
// we can produce a TON of updates; only update progress at most every 3/4s
|
|
|
|
if (Date.now() - lastProgUpdate > 750) {
|
|
|
|
switch (state.step) {
|
2022-06-05 20:04:25 +00:00
|
|
|
case 'next_area':
|
2020-05-11 03:56:05 +00:00
|
|
|
updateStatus(state.status);
|
|
|
|
updateProgressBar(0, 0);
|
2022-06-05 20:04:25 +00:00
|
|
|
this.updateCustomViewTextsWithFilter(
|
|
|
|
'main',
|
|
|
|
MciViewIds.main.customRangeStart,
|
|
|
|
state
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
break;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
case 'message':
|
2020-05-11 03:56:05 +00:00
|
|
|
updateStatus(state.status);
|
|
|
|
updateProgressBar(state.current, state.total);
|
2022-06-05 20:04:25 +00:00
|
|
|
this.updateCustomViewTextsWithFilter(
|
|
|
|
'main',
|
|
|
|
MciViewIds.main.customRangeStart,
|
|
|
|
state
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
break;
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
default:
|
2020-05-11 03:56:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
lastProgUpdate = Date.now();
|
|
|
|
}
|
|
|
|
|
|
|
|
return next(cancel ? Errors.UserInterrupt('User canceled') : null);
|
|
|
|
};
|
|
|
|
|
|
|
|
const keyPressHandler = (ch, key) => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if ('escape' === key.name) {
|
2020-05-11 03:56:05 +00:00
|
|
|
cancel = true;
|
|
|
|
this.client.removeListener('key press', keyPressHandler);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-05-13 00:53:47 +00:00
|
|
|
let totalExported = 0;
|
2020-05-11 03:56:05 +00:00
|
|
|
const processMessagesWithFilter = (filter, cb) => {
|
|
|
|
Message.findMessages(filter, (err, messageIds) => {
|
|
|
|
if (err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
let current = 1;
|
2022-06-05 20:04:25 +00:00
|
|
|
async.eachSeries(
|
|
|
|
messageIds,
|
|
|
|
(messageId, nextMessageId) => {
|
|
|
|
const message = new Message();
|
|
|
|
message.load({ messageId }, err => {
|
2020-05-11 03:56:05 +00:00
|
|
|
if (err) {
|
|
|
|
return nextMessageId(err);
|
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const progress = {
|
|
|
|
message,
|
|
|
|
step: 'message',
|
|
|
|
total: ++totalExported,
|
|
|
|
areaCurrent: current,
|
|
|
|
areaCount: messageIds.length,
|
|
|
|
status: `${_.truncate(message.subject, {
|
|
|
|
length: 25,
|
|
|
|
})} (${current} / ${messageIds.length})`,
|
|
|
|
};
|
|
|
|
|
|
|
|
progressHandler(progress, err => {
|
|
|
|
if (err) {
|
|
|
|
return nextMessageId(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
packetWriter.appendMessage(message);
|
|
|
|
current += 1;
|
2020-05-11 03:56:05 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
return nextMessageId(null);
|
|
|
|
});
|
2020-05-11 03:56:05 +00:00
|
|
|
});
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
|
|
|
err => {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-05-12 01:52:01 +00:00
|
|
|
const packetWriter = new QWKPacketWriter(
|
|
|
|
Object.assign(this._getUserQWKExportOptions(), {
|
2022-06-05 20:04:25 +00:00
|
|
|
user: this.client.user,
|
|
|
|
bbsID: this.config.bbsID,
|
2020-05-12 01:52:01 +00:00
|
|
|
})
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
|
|
|
|
packetWriter.on('warning', warning => {
|
2022-06-05 20:04:25 +00:00
|
|
|
this.client.log.warn({ warning }, 'QWK packet writer warning');
|
2020-05-11 03:56:05 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
async.waterfall(
|
|
|
|
[
|
2022-06-05 20:04:25 +00:00
|
|
|
callback => {
|
2020-05-11 03:56:05 +00:00
|
|
|
// don't count idle monitor while processing
|
|
|
|
this.client.stopIdleMonitor();
|
|
|
|
|
|
|
|
// let user cancel
|
|
|
|
this.client.on('key press', keyPressHandler);
|
|
|
|
|
|
|
|
packetWriter.once('ready', () => {
|
|
|
|
return callback(null);
|
|
|
|
});
|
|
|
|
|
|
|
|
packetWriter.once('error', err => {
|
2022-06-05 20:04:25 +00:00
|
|
|
this.client.log.error(
|
|
|
|
{ error: err.message },
|
|
|
|
'QWK packet writer error'
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
cancel = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
packetWriter.init();
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
callback => {
|
2020-05-12 01:52:01 +00:00
|
|
|
// For each public area -> for each message
|
2020-05-13 00:53:47 +00:00
|
|
|
const userExportAreas = this._getUserQWKExportAreas();
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const publicExportAreas = userExportAreas.filter(exportArea => {
|
|
|
|
return exportArea.areaTag !== Message.WellKnownAreaTags.Private;
|
|
|
|
});
|
|
|
|
async.eachSeries(
|
|
|
|
publicExportAreas,
|
|
|
|
(exportArea, nextExportArea) => {
|
|
|
|
const area = getMessageAreaByTag(exportArea.areaTag);
|
2022-11-28 04:07:56 +00:00
|
|
|
let conf;
|
|
|
|
if (area) {
|
|
|
|
conf = getMessageConferenceByTag(area.confTag);
|
|
|
|
}
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!area || !conf) {
|
|
|
|
// :TODO: remove from user properties - this area does not exist
|
|
|
|
this.client.log.warn(
|
|
|
|
{ areaTag: exportArea.areaTag },
|
|
|
|
'Cannot QWK export area as it does not exist'
|
|
|
|
);
|
|
|
|
return nextExportArea(null);
|
|
|
|
}
|
2020-05-11 03:56:05 +00:00
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!hasMessageConfAndAreaRead(this.client, area)) {
|
|
|
|
this.client.log.warn(
|
|
|
|
{ areaTag: area.areaTag },
|
|
|
|
'Cannot QWK export area due to ACS'
|
|
|
|
);
|
|
|
|
return nextExportArea(null);
|
2020-05-11 03:56:05 +00:00
|
|
|
}
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
const progress = {
|
|
|
|
conf,
|
|
|
|
area,
|
|
|
|
step: 'next_area',
|
|
|
|
status: `Gathering in ${conf.name} - ${area.name}...`,
|
2020-05-11 03:56:05 +00:00
|
|
|
};
|
|
|
|
|
2022-06-05 20:04:25 +00:00
|
|
|
progressHandler(progress, err => {
|
|
|
|
if (err) {
|
|
|
|
return nextExportArea(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
const filter = {
|
|
|
|
resultType: 'id',
|
|
|
|
areaTag: exportArea.areaTag,
|
|
|
|
newerThanTimestamp: exportArea.newerThanTimestamp,
|
|
|
|
};
|
|
|
|
|
|
|
|
processMessagesWithFilter(filter, err => {
|
|
|
|
return nextExportArea(err);
|
|
|
|
});
|
2020-05-11 03:56:05 +00:00
|
|
|
});
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
|
|
|
err => {
|
|
|
|
return callback(err, userExportAreas);
|
|
|
|
}
|
|
|
|
);
|
2020-05-11 03:56:05 +00:00
|
|
|
},
|
2020-05-12 01:57:25 +00:00
|
|
|
(userExportAreas, callback) => {
|
|
|
|
// Private messages to current user if the user has
|
|
|
|
// elected to export private messages
|
2022-06-05 20:04:25 +00:00
|
|
|
const privateExportArea = userExportAreas.find(
|
|
|
|
exportArea =>
|
|
|
|
exportArea.areaTag === Message.WellKnownAreaTags.Private
|
|
|
|
);
|
2020-05-13 00:53:47 +00:00
|
|
|
if (!privateExportArea) {
|
2020-05-12 01:57:25 +00:00
|
|
|
return callback(null);
|
|
|
|
}
|
|
|
|
|
2020-05-11 03:56:05 +00:00
|
|
|
const filter = {
|
2022-06-05 20:04:25 +00:00
|
|
|
resultType: 'id',
|
|
|
|
privateTagUserId: this.client.user.userId,
|
|
|
|
newerThanTimestamp: privateExportArea.newerThanTimestamp,
|
2020-05-11 03:56:05 +00:00
|
|
|
};
|
|
|
|
return processMessagesWithFilter(filter, callback);
|
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
callback => {
|
2020-05-11 03:56:05 +00:00
|
|
|
let packetInfo;
|
|
|
|
packetWriter.once('packet', info => {
|
|
|
|
packetInfo = info;
|
|
|
|
});
|
|
|
|
|
|
|
|
packetWriter.once('finished', () => {
|
|
|
|
return callback(null, packetInfo);
|
|
|
|
});
|
|
|
|
|
|
|
|
packetWriter.finish(this.tempPacketDir);
|
|
|
|
},
|
|
|
|
(packetInfo, callback) => {
|
2020-05-13 00:53:47 +00:00
|
|
|
if (0 === totalExported) {
|
|
|
|
return callback(Errors.NothingToDo('No messages exported'));
|
|
|
|
}
|
|
|
|
|
2020-05-11 03:56:05 +00:00
|
|
|
const sysDownloadPath = paths.join(sysTempDownloadDir, this.tempName);
|
|
|
|
fse.move(packetInfo.path, sysDownloadPath, err => {
|
2020-06-16 01:08:55 +00:00
|
|
|
return callback(err, sysDownloadPath, packetInfo);
|
2020-05-11 03:56:05 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
(sysDownloadPath, packetInfo, callback) => {
|
|
|
|
const newEntry = new FileEntry({
|
2022-06-05 20:04:25 +00:00
|
|
|
areaTag: this.sysTempDownloadArea.areaTag,
|
|
|
|
fileName: paths.basename(sysDownloadPath),
|
|
|
|
storageTag: this.sysTempDownloadArea.storageTags[0],
|
|
|
|
meta: {
|
|
|
|
upload_by_username: this.client.user.username,
|
|
|
|
upload_by_user_id: this.client.user.userId,
|
|
|
|
byte_size: packetInfo.stats.size,
|
|
|
|
session_temp_dl: 1, // download is valid until session is over
|
2020-05-11 03:56:05 +00:00
|
|
|
|
|
|
|
// :TODO: something like this: allow to override the displayed/downloaded as filename
|
|
|
|
// separate from the actual on disk filename. E.g. we could always download as "ENIGMA.QWK"
|
2020-05-12 01:57:25 +00:00
|
|
|
//visible_filename : paths.basename(packetInfo.path),
|
2022-06-05 20:04:25 +00:00
|
|
|
},
|
2020-05-11 03:56:05 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
newEntry.desc = 'QWK Export';
|
|
|
|
|
|
|
|
newEntry.persist(err => {
|
2022-06-05 20:04:25 +00:00
|
|
|
if (!err) {
|
2020-05-11 03:56:05 +00:00
|
|
|
// queue it!
|
2020-05-12 01:52:01 +00:00
|
|
|
DownloadQueue.get(this.client).addTemporaryDownload(newEntry);
|
2020-05-11 03:56:05 +00:00
|
|
|
}
|
|
|
|
return callback(err);
|
|
|
|
});
|
2020-05-13 00:53:47 +00:00
|
|
|
},
|
2022-06-05 20:04:25 +00:00
|
|
|
callback => {
|
2020-05-13 00:53:47 +00:00
|
|
|
// update user's export area dates; they can always change/reset them again
|
2022-06-05 20:04:25 +00:00
|
|
|
const updatedUserExportAreas = this._getUserQWKExportAreas().map(
|
|
|
|
exportArea => {
|
|
|
|
return Object.assign(exportArea, {
|
|
|
|
newerThanTimestamp: getISOTimestampString(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
2020-05-13 00:53:47 +00:00
|
|
|
|
|
|
|
return this.client.user.persistProperty(
|
|
|
|
UserProperties.ExportAreas,
|
|
|
|
JSON.stringify(updatedUserExportAreas),
|
|
|
|
callback
|
|
|
|
);
|
|
|
|
},
|
2020-05-11 03:56:05 +00:00
|
|
|
],
|
|
|
|
err => {
|
|
|
|
this.client.startIdleMonitor(); // re-enable
|
|
|
|
this.client.removeListener('key press', keyPressHandler);
|
|
|
|
|
|
|
|
if (!err) {
|
|
|
|
updateStatus('A QWK packet has been placed in your download queue');
|
2020-05-13 00:53:47 +00:00
|
|
|
} else if (err.code === Errors.NothingToDo().code) {
|
|
|
|
updateStatus('No messages to export with current criteria');
|
|
|
|
err = null;
|
2020-05-11 03:56:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2022-06-05 20:04:25 +00:00
|
|
|
};
|