const request = require('request') const log4js = require('log4js') const logger = log4js.getLogger() const STRING = require('string') let util = require('util') const Elapsed = require('elapsed') require('sugar-date'); function processICAL(ical) { 'use strict'; logger.info('+ processICAL'); let 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) { let _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() let 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 origStart, origEnd, distance, newStart, newEnd origStart = Date.create(_workBlock.dtstart); origEnd = Date.create(_workBlock.dtend); const _d = origStart.getDate() const _m = origStart.getMonth() const _h = origStart.getHours() const _min = origStart.getMinutes() const _secs = origStart.getSeconds() distance = origEnd - origStart; if (recurSettings.freq === 'YEARLY') { if (recurSettings.bymonth !== null && recurSettings.bymonthday !== null) { // ok, a day and month. newStart = Date.create().set({year:year, month: recurSettings.bymonth - 1 , day: recurSettings.bymonthday, hour:_h, minutes:_min, seconds:_secs}); newEnd = Date.create(newStart).addMilliseconds(distance); _workBlock.dtstart = newStart; _workBlock.dtend = newEnd; } else if (recurSettings.bymonth === null && recurSettings.bymonthday === null) { // extract month and year from dtstart newStart = Date.create().set({year:year, month: _m , day: _d, hour:_h, minutes:_min, seconds:_secs}); newEnd = Date.create(newStart).addMilliseconds(distance); _workBlock.dtstart = newStart; _workBlock.dtend = newEnd; } return _workBlock; } if (recurSettings.freq === 'MONTHLY') { if (recurSettings.bymonthday !== null) { // ok, a day and month. newStart = Date.create().set({year:year, month: month , day: recurSettings.bymonthday, hour:_h, minutes:_min, seconds:_secs}); newEnd = Date.create(newStart).addMilliseconds(distance); _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 = Date.create().set({year:year, month: month , day: day, hour:_h, minutes:_min, seconds:_secs}).addDays(daysAdded); newEnd = Date.create(newStart).addMilliseconds(distance); _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); } if (blockStep.indexOf(segments.meetingEndID) >= 0) { ws = STRING(block[step].split(segments.meetingEndID)[1]).collapseWhitespace().s; workBlock.dtend = Date.create(ws); } if (blockStep.indexOf(segments.meetingStartAlt) >= 0) { ws = STRING(block[step].split(segments.meetingStartAlt)[1]).collapseWhitespace().s; workBlock.dtstart = Date.create(ws); } if (blockStep.indexOf(segments.meetingEndAlt) >= 0) { ws = STRING(block[step].split(segments.meetingEndAlt)[1]).collapseWhitespace().s; workBlock.dtend = Date.create(ws); } if (blockStep.indexOf(segments.meetingStartAltOther) >= 0) { ws = STRING(block[step].split(segments.meetingStartAltOther)[1]).collapseWhitespace().s; workBlock.dtstart = Date.create(ws); } if (blockStep.indexOf(segments.meetingEndAltOther) >= 0) { ws = STRING(block[step].split(segments.meetingEndAltOther)[1]).collapseWhitespace().s; workBlock.dtend = Date.create(ws); } 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); if (workBlock.dtstart !== null) { workBlock.timeStart = workBlock.dtstart.format('{24hr}:{mm}:{ss}'); workBlock.combined = `${workBlock.timeStart} - '`; workBlock.long = `${workBlock.dtstart.format('{Weekday}')}, ${workBlock.timeStart} - `; } workBlock.combined = workBlock.combined + workBlock.summary; workBlock.longcombined = workBlock.long + workBlock.summary; if (workBlock.dtend !== null) { workBlock.timeEnd = workBlock.dtend.format('{24hr}:{mm}:{ss}'); } 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()) { 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 (this.jsonBlock[t].dtstart.isTomorrow()) { today.entries.push(this.jsonBlock[t]); } } logger.info('- getTomorrow'); return today; }, getWeek: function() { 'use strict'; logger.info('+ getWeek'); const today = { entries: [] } let now, twoDays, sevenDays now = Date.create('today'); // logger.debug(now); twoDays = Date.create(now).addDays(2).beginningOfDay(); // logger.debug(twoDays); sevenDays = Date.create(twoDays).addDays(5).beginningOfDay(); // logger.debug(now, twoDays, sevenDays); for (let t = 0; t < this.jsonBlock.length; t++) { if (this.jsonBlock[t].dtstart.isBetween(twoDays, sevenDays)) { 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 (this.jsonBlock[t].dtstart.isToday()) { if (this.jsonBlock[t].dtstart.isAfter(now)) { 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.debug(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. */ };