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. */ };