const request = require('request');
const log4js = require('log4js');
const logger = log4js.getLogger();
const STRING = require('string');
const util = require('util');
const Elapsed = require('elapsed');
const Sugar = require('sugar');
logger.level = 'debug';
function processICAL(ical) {
'use strict';
logger.info('+ processICAL');
const workingBlock = [];
const segments = {
'meetingStartID': 'DTSTART;TZID=Europe/London:',
'meetingStartAlt': 'DTSTART:',
'meetingStartAltOther': 'DTSTART;VALUE=DATE:',
'meetingEndID': 'DTEND;TZID=Europe/London:',
'meetingEndAlt': 'DTEND:',
'meetingEndAltOther': 'DTEND;VALUE=DATE:',
'meetingDescID': 'DESCRIPTION:',
'summaryID': 'SUMMARY:',
'begin': 'BEGIN:VEVENT',
'end': 'END:VEVENT',
'beginAlarm': 'BEGIN:VALARM',
'endAlarm': 'END:VALARM',
'recur': 'RRULE:'
};
const rules = ['FREQ', 'WKST', 'UNTIL', 'BYMONTH', 'BYMONTHDAY', 'INTERVAL', 'BYDAY'];
function nThDayOfMonth(monthsAhead, wantedDay) {
const now = new Date();
for(let t = 0; t < monthsAhead; t++) {
}
}
function processRecurrence(workBlock) {
const _workBlock = workBlock;
// logger.debug('Processing recurrence...');
// logger.debug('Processing recurrence...');
const weekBits = { 'SU': 0, 'MO': 1, 'TU': 2, 'WE': 3, 'TH': 4, 'FR': 5, 'SA': 6 };
const now = new Date();
const day = now.getDate();
const dayNum = now.getDay();
const month = now.getMonth();
const year = now.getFullYear();
const recurSettings = { 'freq': null, 'wkst': null, 'until': null, 'bymonth': null, 'bymonthday': null, 'interval': null, 'byday': null };
const firstSplit = _workBlock.recur.split(';');
for (let t = 0; t < firstSplit.length; t++) {
const ws = firstSplit[t].split('=');
if (rules.indexOf(ws[0]) > -1)
recurSettings[ws[0].toLowerCase()] = ws[1];
}
// if all null discard..
if (recurSettings.freq === null && recurSettings.wkst === null && recurSettings.until === null && recurSettings.byday === null && recurSettings.bymonth === null && recurSettings.bymonthday === null && recurSettings.interval === null)
return null;
if (recurSettings.until !== null)
// have we expired?
// var _until = Date.create(recurSettings.until).isPast();
return null;
if (recurSettings.freq !== null) {
// logger.debug(_workBlock);
let newStart, newEnd;
// let d = new Sugar.Date();
/* origStart = Date.create(_workBlock.dtstart);
origEnd = Date.create(_workBlock.dtend);*/
const origStart = new Sugar.Date(_workBlock.dtstart).raw;
const origEnd = new Sugar.Date(_workBlock.dtend).raw;
const _d = origStart.getDate();
const _m = origStart.getMonth();
const _h = origStart.getHours();
const _min = origStart.getMinutes();
const _secs = origStart.getSeconds();
const distance = origEnd - origStart;
// Sugar.Date()
if (recurSettings.freq === 'YEARLY') {
if (recurSettings.bymonth !== null && recurSettings.bymonthday !== null) {
// ok, a day and month.
newStart = Sugar.Date().set({ 'year':year, 'month': recurSettings.bymonth - 1, 'day': recurSettings.bymonthday, 'hour':_h, 'minutes':_min, 'seconds':_secs }).raw;
newEnd = Sugar.Date(newStart).addMilliseconds(distance).raw;
_workBlock.dtstart = newStart;
_workBlock.dtend = newEnd;
}
else if (recurSettings.bymonth === null && recurSettings.bymonthday === null) {
// extract month and year from dtstart
newStart = Sugar.Date().set({ 'year':year, 'month': _m, 'day': _d, 'hour':_h, 'minutes':_min, 'seconds':_secs }).raw;
newEnd = Sugar.Date(newStart).addMilliseconds(distance).raw;
_workBlock.dtstart = newStart;
_workBlock.dtend = newEnd;
}
return _workBlock;
}
if (recurSettings.freq === 'MONTHLY')
if (recurSettings.bymonthday !== null) {
// ok, a day and month.
newStart = Sugar.Date().set({ 'year':year, 'month': month, 'day': recurSettings.bymonthday, 'hour':_h, 'minutes':_min, 'seconds':_secs }).raw;
newEnd = Sugar.Date(newStart).addMilliseconds(distance).raw;
_workBlock.dtstart = newStart;
_workBlock.dtend = newEnd;
}
if (recurSettings.freq === 'WEEKLY' && recurSettings.interval === null) {
const byDayBit = recurSettings.byday.split(',')[0];
const byDayNumber = weekBits[byDayBit];
if (byDayNumber >= dayNum) {
const daysAdded = byDayNumber - dayNum;
newStart = Sugar.Date().set({ 'year':year, 'month': month, 'day': day, 'hour':_h, 'minutes':_min, 'seconds':_secs }).addDays(daysAdded).raw;
newEnd = Sugar.Date(newStart).addMilliseconds(distance).raw;
_workBlock.dtstart = newStart;
_workBlock.dtend = newEnd;
}
}
}
// if we get here we've skipped everything just return the _workblock
return _workBlock;
}
function processBlock(block) {
let _wb;
let workBlock = {
'summary': '',
'dtstart': null,
'dtend': null,
'description': '',
'timeStart': null,
'timeEnd': null,
'duration': 0,
'combined': '',
'recur': null
};
let alarmFlag = false, ws, blockStep;
for (let step = 0; step < block.length; step++) {
blockStep = block[step];
if (blockStep.indexOf(segments.recur) >= 0)
workBlock.recur = STRING(block[step].split(segments.recur)[1]).collapseWhitespace().s;
// logger.debug(workBlock.recur);
if (blockStep.indexOf(segments.summaryID) >= 0)
workBlock.summary = STRING(block[step].split(segments.summaryID)[1]).collapseWhitespace().s;
if (blockStep.indexOf(segments.meetingStartID) >= 0) {
ws = STRING(block[step].split(segments.meetingStartID)[1]).collapseWhitespace().s;
// workBlock.dtstart = Date.create(ws);
workBlock.dtstart = new Sugar.Date(ws).raw;
}
if (blockStep.indexOf(segments.meetingEndID) >= 0) {
ws = STRING(block[step].split(segments.meetingEndID)[1]).collapseWhitespace().s;
// workBlock.dtend = Date.create(ws);
workBlock.dtend = new Sugar.Date(ws).raw;
}
if (blockStep.indexOf(segments.meetingStartAlt) >= 0) {
ws = STRING(block[step].split(segments.meetingStartAlt)[1]).collapseWhitespace().s;
console.log('>> ws', ws);
// workBlock.dtstart = Date.create(ws);
// let d = new Sugar.Date();
workBlock.dtstart = new Sugar.Date(ws).raw;
console.log('>> date', workBlock.dtstart);
}
if (blockStep.indexOf(segments.meetingEndAlt) >= 0) {
ws = STRING(block[step].split(segments.meetingEndAlt)[1]).collapseWhitespace().s;
// workBlock.dtend = Date.create(ws);
workBlock.dtend = new Sugar.Date(ws).raw;
console.log('>> date', workBlock.dtend);
}
if (blockStep.indexOf(segments.meetingStartAltOther) >= 0) {
ws = STRING(block[step].split(segments.meetingStartAltOther)[1]).collapseWhitespace().s;
// workBlock.dtstart = Date.create(ws);
workBlock.dtstart = new Sugar.Date(ws).raw;
}
if (blockStep.indexOf(segments.meetingEndAltOther) >= 0) {
ws = STRING(block[step].split(segments.meetingEndAltOther)[1]).collapseWhitespace().s;
console.log('>> ws', ws);
// workBlock.dtend = Date.create(ws);
workBlock.dtend = new Sugar.Date(ws).raw;
}
if (blockStep.indexOf(segments.meetingDescID) >= 0)
if (!alarmFlag) {
workBlock.description = STRING(block[step].split(segments.meetingDescID)[1]).collapseWhitespace().s;
}
if (blockStep.indexOf(segments.beginAlarm) >= 0)
alarmFlag = true;
}
// We have to check recuring stuff before the cron stuff is processed.
if (workBlock.recur !== null) {
_wb = processRecurrence(workBlock);
// logger.warn('returning:', _wb);
if (_wb !== null)
if (!Array.isArray(_wb)) {
workBlock = _wb;
}
else {
logger.error('We made an array');
}
}
// logger.debug(workBlock);
// let d = new Sugar.Date();
if (workBlock.dtstart !== null) {
// workBlock.timeStart = workBlock.dtstart.format('{24hr}:{mm}:{ss}');
workBlock.timeStart = Sugar.Date(workBlock.dtstart).format('{24hr}:{mm}:{ss}').raw;
console.log('>> workBlock.timeStart', workBlock.timeStart);
workBlock.combined = `${workBlock.timeStart} - '`;
workBlock.long = `${Sugar.Date(workBlock.dtstart).format('{Weekday}').raw}, ${workBlock.timeStart} - `;
console.log('>> workBlock.long', workBlock.long);
}
workBlock.combined = workBlock.combined + workBlock.summary;
workBlock.longcombined = workBlock.long + workBlock.summary;
if (workBlock.dtend !== null)
workBlock.timeEnd = Sugar.Date(workBlock.dtend).format('{24hr}:{mm}:{ss}').raw;
if (workBlock.dtstart !== null && workBlock.dtend !== null) {
const elapsedTime = new Elapsed(workBlock.dtstart, workBlock.dtend);
workBlock.duration = elapsedTime.optimal;
workBlock.combined = `${workBlock.combined }, ${ elapsedTime.optimal}`;
workBlock.longcombined = `${workBlock.longcombined }, ${ elapsedTime.optimal}`;
}
return workBlock;
}
const lines = ical.split('\r\n'), l = lines.length;
let counter = 0;
let alarmed = false;
while (counter < l)
if (lines[counter].indexOf(segments.begin) < 0)
counter++;
else {
let subcounter = 0;
const subBlock = [];
alarmed = false;
while (subcounter < 75)
if (lines[counter + subcounter].indexOf(segments.end) < 0) {
if (lines[counter + subcounter].indexOf(segments.beginAlarm) > -1)
alarmed = true;
if (!alarmed)
subBlock.push(lines[counter + subcounter]);
if (lines[counter + subcounter].indexOf(segments.endAlarm) > -1)
alarmed = false;
subcounter++;
}
else
break;
counter = counter + subcounter;
const b = processBlock(subBlock);
if (Array.isArray(b))
logger.error('!returned an array...');
else
if (b.dtstart !== null)
workingBlock.push(b);
}
logger.info('- processICAL');
// If (workingBlock.dtstart == null) return {};
return workingBlock;
}
module.exports = {
'calendars': ['https://calendar.google.com/calendar/ical/martind2000%40gmail.com/private-40cfebc9f7dcfa7fde6b9bf2f0092c93/basic.ics',
'https://calendar.google.com/calendar/ical/mt5pgdhknvgoc8usfnrso9vkv0%40group.calendar.google.com/private-58876002af9f302a593acfa6fa792dcf/basic.ics',
'https://www.tripit.com/feed/ical/private/DB96E4BB-94A9BD8F9CC1CF51C6CC0D920840F4F5/tripit.ics',
'https://calendar.google.com/calendar/ical/en.uk%23holiday%40group.v.calendar.google.com/public/basic.ics',
'https://calendar.google.com/calendar/ical/i8dglj12p5nuv20sbjmun5s588%40group.calendar.google.com/private-c8adccb41e56d6a2f285078aaed313f5/basic.ics'],
'jsonBlock': [],
'getTodaysSimple': function() {
'use strict';
logger.info('+ getTodaysSimple');
const today = {
'entries': []
};
for (let t = 0; t < this.jsonBlock.length; t++) {
// if (this.jsonBlock[t].dtstart.isToday())
// logger.debug('>> isToday', Sugar.Date(this.jsonBlock[t].dtstart).isToday().raw);
if (Sugar.Date(this.jsonBlock[t].dtstart).isToday().raw)
today.entries.push(this.jsonBlock[t]);
}
logger.info('- getTodaysSimple');
return today;
},
'getTomorrow': function() {
'use strict';
logger.info('+ getTomorrow');
const today = {
'entries': []
};
for (let t = 0; t < this.jsonBlock.length; t++)
if (Sugar.Date(this.jsonBlock[t].dtstart).isTomorrow().raw)
today.entries.push(this.jsonBlock[t]);
logger.info('- getTomorrow');
return today;
},
'getWeek': function() {
'use strict';
logger.info('+ getWeek');
const today = {
'entries': []
};
const now = new Sugar.Date('today').raw;
logger.debug('>> now', now);
const twoDays = new Sugar.Date('today').addDays(2).beginningOfDay().raw;
logger.debug('>> twoDays', twoDays);
const sevenDays = new Sugar.Date('today').addDays(7).beginningOfDay().raw;
logger.debug('>> sevenDays', sevenDays);
logger.debug('>> trip', { now, twoDays, sevenDays });
for (let t = 0; t < this.jsonBlock.length; t++) {
logger.debug('>> between', Sugar.Date(this.jsonBlock[t].dtstart).raw, Sugar.Date(this.jsonBlock[t].dtstart).isBetween(twoDays, sevenDays));
if (Sugar.Date(this.jsonBlock[t].dtstart).isBetween(twoDays, sevenDays).raw)
today.entries.push(this.jsonBlock[t]);
}
logger.info('- getWeek');
return today;
},
'getTodaysMeetings': function() {
'use strict';
logger.info('+ getTodaysMeetings');
const today = {
'previous': [], 'upcoming': [], 'current': {}
};
const now = new Date();
for (let t = 0; t < this.jsonBlock.length; t++)
if (Sugar.Date(this.jsonBlock[t].dtstart).isToday().raw) {
if (Sugar.Date(this.jsonBlock[t].dtstart).isAfter(now).raw)
today.upcoming.push(this.jsonBlock[t]);
else
today.previous.push(this.jsonBlock[t]);
if (now.isBetween(this.jsonBlock[t].dtstart, this.jsonBlock[t].dtend))
today.current = this.jsonBlock[t];
}
// logger.debug(today);
logger.info('- getTodaysMeetings');
return today;
}, 'getSimpleCalV2': function(url, cb) {
'use strict';
const self = this;
// Var calJson = [];
try {
request(url, function(err, res, body) {
if (err) {
logger.error('Get remote Calendar Request failed');
// Callback.call(null, new Error('Request failed'));
return;
}
self.jsonBlock = processICAL(body);
// logger.debug(self.jsonBlock);
const st = self.getTodaysSimple();
if (typeof cb === 'function')
cb(st);
}, function(error, response, body) {
if (response.statusCode !== 200) {
logger.error(response.statusCode);
logger.error(body);
}
});
}
catch (e) {
console.log(e);
}
}, 'getSimpleCalV3': function(url) {
'use strict';
const self = this;
return new Promise(function(resolve, reject) {
try {
request(url, function(err, res, body) {
if (err)
// logger.error(err);
return reject(err);
// Throw err;
self.jsonBlock = processICAL(body);
// logger.debug(self.jsonBlock);
const st = self.getTodaysSimple();
return resolve(st);
}, function(error, response, body) {
if (response.statusCode !== 200) {
logger.error(response.statusCode);
logger.error(body);
return reject(error);
}
});
}
catch (e) {
console.log(e);
return reject(e);
}
});
// Var calJson = [];
}, 'getAdvancedCalV3': function(url) {
'use strict';
const self = this;
return new Promise(function(resolve, reject) {
try {
request(url, function(err, res, body) {
if (err)
// logger.error(err);
return reject(err);
// Throw err;
self.jsonBlock = processICAL(body);
// logger.debug(self.jsonBlock);
const st = self.getTodaysSimple().entries;
const tom = self.getTomorrow().entries;
const week = self.getWeek().entries;
const obj = { 'today': st, 'tomorrow': tom, 'week': week };
// logger.warn(obj);
return resolve(obj);
}, function(error, response, body) {
if (response.statusCode !== 200) {
logger.error(response.statusCode);
logger.error(body);
return reject(error);
}
});
}
catch (e) {
console.log(e);
return reject(e);
}
});
// Var calJson = [];
}
/**
* Created by Martin on 16/02/2016.
*/
};