const fs = require('fs') const path = require('path') const c = require('canvas') const EventEmitter = require('events').EventEmitter const config = require(path.join(__dirname, 'config.js')) const request = require(path.join(__dirname, 'request.js')) function pz (z) { if (z < 10) { return '0' + z } return z } function dateFormat (date, yr) { return (yr ? (date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear() + ' ') : '') + pz(date.getHours()) + ':' + pz(date.getMinutes()) } class Drawer { constructor () { this.canvas = c.createCanvas(config.calendar.schedule.width, config.calendar.schedule.height) this.ctx = this.canvas.getContext('2d') } async draw (cal) { let ctx = this.ctx let font = config.calendar.schedule.font let fontColor = config.calendar.schedule.fontColor ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) ctx.fillStyle = config.calendar.schedule.background ctx.fillRect(5, 5, this.canvas.width - 10, this.canvas.height - 10) let added = 0 for (let i in cal.events) { let ev = cal.events[i] if (i > 5) break // Can't fit so many on here :P if (ev.eventStartTime < Date.now() || ev.eventEndTime < Date.now()) continue ctx.fillStyle = fontColor let y = (i * 60) + 30 // Write event name let evn = ev.eventName if (evn.length > 14) { evn = evn.substring(0, 14) + '…' } ctx.font = '26px ' + font ctx.fillText(evn, 10, y) // Write event description ctx.font = '16px ' + font ctx.fillText(ev.description, 10, y + 20) // Write event time let time = dateFormat(ev.eventStartTime) ctx.font = '26px ' + font let tlen = ctx.measureText(time).width ctx.fillText(time, (this.canvas.width - 10) - tlen, y) // Draw line under text ctx.strokeStyle = 'rgba(0,0,0,0.5)' ctx.beginPath() ctx.lineTo(10, y + 20 + 12) ctx.lineTo(this.canvas.width - 10, y + 20 + 12) ctx.stroke() added++ } if (added === 0) { ctx.fillStyle = fontColor ctx.font = '26px ' + font ctx.fillText('No events scheduled.', 10, 35) } await this.toFile() } toFile () { const out = fs.createWriteStream(path.join(process.env.PWD, 'liq', 'schedule.png')) const stream = this.canvas.createPNGStream() stream.pipe(out) return new Promise((resolve, reject) => { out.on('finish', resolve) out.on('error', reject) }) } } class Calendar extends EventEmitter { constructor (conf) { super() this.events = [] this.config = conf this.drawer = new Drawer() this.started = [] this.timers = { a: null, b: null, c: null } } update () { this.drawer.draw(this).then(() => this.emit('reload')) } startTimers () { // Initial run this.calendarFetch().then( (e) => this.update(), (e) => console.error('Calendar fetch failed!', e.stack) ) // Set timers this.timers = { a: setInterval(() => this.calendarFetch().catch((e) => console.error('Calendar fetch failed!', e.stack)), this.config.interval * 1000), b: setInterval(() => this.runners(), 60 * 1000), c: setInterval(() => this.update(), 120 * 1000) } } stopTimers () { clearInterval(this.timers.a) clearInterval(this.timers.b) clearInterval(this.timers.c) } static sortStartTime (a, b) { return a.eventStartTime - b.eventEndTime } static prettifyEvent (item) { let ev = { id: item.id, htmlLink: item.htmlLink, created: new Date(item.created), updated: new Date(item.updated), title: item.summary.replace(/\n/g, ' '), location: item.location, description: (item.description || '').replace(/\n/g, ' '), start: new Date(item.start.dateTime || item.start.date), end: new Date(item.end.dateTime || item.end.date), sequence: item.sequence } ev.length = (ev.end.getTime() - ev.start.getTime()) / 1000 return ev } async calendarFetch () { const timeFrame = this.config.timeFrame const cUrl = this.config.calendar const apiKey = this.config.googleKey let now = Date.now() let url = 'https://www.googleapis.com/calendar/v3/calendars/' + encodeURIComponent(cUrl) + '/events?key=' + apiKey + '&timeMin=' + (new Date(now - 10 * 60 * 1000).toISOString()) + '&timeMax=' + (new Date(now + timeFrame * 1000).toISOString()) + '&singleEvents=true' let data = await request.GET(url) data = JSON.parse(data) now = Date.now() if (!data.items) return let results = [] for (let i = 0; i < data.items.length; i++) { let item = Calendar.prettifyEvent(data.items[i]) if (now < item.end.getTime()) { let desc = item.description let scr = null if (desc.indexOf('$:') !== -1) { let a = desc.split('$:') desc = a[0].trim() scr = a[1].trim() } results.push({ id: item.id, eventName: item.title, eventStartTime: item.start, eventEndTime: item.end, description: desc, descriptor: scr }) } } console.log(`[schedule] Calendar fetched successfully!`) this.events = results.sort(Calendar.sortStartTime) this.emit('events', this.events) } runners () { for (let i in this.events) { let ev = this.events[i] if (!ev.descriptor) continue if (ev.eventStartTime < Date.now() && ev.eventEndTime > Date.now()) { if (this.started.indexOf(ev.id) !== -1) continue console.log(`[schedule] Starting event ${ev.eventName}..`) this.started.push(ev.id) this.emit('event-start', ev) } } } } module.exports = Calendar