From f8cbfba17b8ee2cf743943254afbb986439a75ee Mon Sep 17 00:00:00 2001 From: Cai Jones <1808209+Riott@users.noreply.github.com> Date: Wed, 26 Apr 2023 22:38:22 +0100 Subject: [PATCH 01/12] Fix timezone to broadcast, detect all day events and the next schedule event during an all day --- lib/commands/implementations/schedule.js | 21 ++++++++++++++++++--- lib/services/schedule.js | 23 +++++++++++++++-------- package.json | 2 +- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/commands/implementations/schedule.js b/lib/commands/implementations/schedule.js index ea7126a..258ae71 100644 --- a/lib/commands/implementations/schedule.js +++ b/lib/commands/implementations/schedule.js @@ -9,12 +9,27 @@ function getNextStreamDate(input, services) { if (!nextStreamDay) { return new CommandOutput(null, `Nothing scheduled ${services.schedule.link()}`); } + const {allDay, name, start, childEvent} = nextStreamDay + const isHappening = moment(start).isBefore(moment()); + const startTime = moment(start).fromNow(); + const link = services.schedule.link() + + let scheduleOutput = ''; + if (isHappening) { + scheduleOutput = allDay ? `${name} is todays all-day event! ${link}` : `${name} is currently happening! it started ${startTime}. ${link}`; + if (allDay && childEvent) { + const childEventIsHappening = moment(childEvent.start).isBefore(moment()); + scheduleOutput = childEventIsHappening ? `${name} is todays all-day event! ${childEvent.name} is currently happening! it started ${moment(childEvent.start).fromNow()}. ${link}` + : `${name} is todays all-day event! coming up in ${moment(childEvent.start).fromNow()} is ${childEvent.name}. ${link}`; + } + } + else { + scheduleOutput = `${name} is coming up next! it starts in ${startTime}. ${link}`; + } return new CommandOutput( null, - `${nextStreamDay.name}${ - nextStreamDay.allDay ? ', an all-day event,' : '' - } scheduled to begin ${moment(nextStreamDay.start).fromNow()} ${services.schedule.link()}`, + scheduleOutput, ); }) .catch((err) => new CommandOutput(err, "Oops. Something didn't work. Check the logs.")); diff --git a/lib/services/schedule.js b/lib/services/schedule.js index c2badda..c9d9c9b 100644 --- a/lib/services/schedule.js +++ b/lib/services/schedule.js @@ -13,26 +13,33 @@ class GoogleCal { findNextStreamDay() { return this.getListOfUpcomingEvents(this.configuration.GOOGLE_CALENDAR_ID).then((events) => { - const now = moment.tz('UTC'); const nextEvent = events.find((event) => { - if (event.start.date !== undefined) { + if (event.start !== undefined) { return true; } - - return !( - moment.tz(event.end.dateTime, 'YYYY-MM-DDTHH:mm:ss', event.end.timeZone).isAfter(now) && - moment.tz(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss', event.start.timeZone).isBefore(now) - ); }); if (!nextEvent) { return null; } + const isAllDay = !nextEvent.start.dateTime // All-day events don't have a start time, only a start day. + let childEvent; + if (isAllDay) { + events.find((event) => { + const isSameDay = moment.tz(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss', event.start.timeZone).isSame(nextEvent.start.date, "day") + if (isSameDay) { + childEvent = {start: event.start.dateTime, name: event.summary} + return true; + } + }); + } + return { start: nextEvent.start.dateTime ?? nextEvent.start.date, name: nextEvent.summary, - allDay: !nextEvent.start.dateTime, // All-day events don't have a start time, only a start day. + allDay: isAllDay, + childEvent: childEvent }; }); } diff --git a/package.json b/package.json index 245cbc7..9fe9821 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "bunyan": "^1.8.14", "fast-levenshtein": "^3.0.0", "google-auth-library": "^6.1.1", - "googleapis": "^61.0.0", + "googleapis": "^118.0.0", "inspector-influx": "^2.7.0", "inspector-metrics": "^1.21.0", "lodash": "^4.17.20", From d9fff3db5cf7876658693796d04d910ee84405dd Mon Sep 17 00:00:00 2001 From: Cai Jones <1808209+Riott@users.noreply.github.com> Date: Wed, 26 Apr 2023 22:49:25 +0100 Subject: [PATCH 02/12] fix tests --- tests/lib/services/schedule.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/lib/services/schedule.test.js b/tests/lib/services/schedule.test.js index bac8364..efcd7f9 100644 --- a/tests/lib/services/schedule.test.js +++ b/tests/lib/services/schedule.test.js @@ -45,6 +45,7 @@ describe('Schedule Tests', () => { start: '2018-11-12T17:00:00-06:00', name: 'Stream', allDay: false, + childEvent: undefined, }); }); }); @@ -57,6 +58,7 @@ describe('Schedule Tests', () => { start: '2023-03-15', name: 'Stop the Steal Debate with Ali Alexander', allDay: true, + childEvent: undefined, }); }); }); From 58de6ef25c076c95b69318594a2f98fb1be3f1ca Mon Sep 17 00:00:00 2001 From: Cai Jones <1808209+Riott@users.noreply.github.com> Date: Thu, 27 Apr 2023 10:01:51 +0100 Subject: [PATCH 03/12] Fix timezone issues, update tests --- lib/commands/implementations/schedule.js | 7 ++- lib/services/schedule.js | 14 +++-- lib/services/service-index.js | 2 +- .../mocks/google-calendar-responses.json | 63 +++++++++++++++++++ tests/lib/services/schedule.test.js | 16 ++++- 5 files changed, 91 insertions(+), 11 deletions(-) diff --git a/lib/commands/implementations/schedule.js b/lib/commands/implementations/schedule.js index 258ae71..d6f6b1d 100644 --- a/lib/commands/implementations/schedule.js +++ b/lib/commands/implementations/schedule.js @@ -9,22 +9,23 @@ function getNextStreamDate(input, services) { if (!nextStreamDay) { return new CommandOutput(null, `Nothing scheduled ${services.schedule.link()}`); } + const {allDay, name, start, childEvent} = nextStreamDay const isHappening = moment(start).isBefore(moment()); const startTime = moment(start).fromNow(); const link = services.schedule.link() - let scheduleOutput = ''; + if (isHappening) { scheduleOutput = allDay ? `${name} is todays all-day event! ${link}` : `${name} is currently happening! it started ${startTime}. ${link}`; if (allDay && childEvent) { const childEventIsHappening = moment(childEvent.start).isBefore(moment()); scheduleOutput = childEventIsHappening ? `${name} is todays all-day event! ${childEvent.name} is currently happening! it started ${moment(childEvent.start).fromNow()}. ${link}` - : `${name} is todays all-day event! coming up in ${moment(childEvent.start).fromNow()} is ${childEvent.name}. ${link}`; + : `${name} is todays all-day event! coming up ${moment(childEvent.start).fromNow()} is ${childEvent.name}. ${link}`; } } else { - scheduleOutput = `${name} is coming up next! it starts in ${startTime}. ${link}`; + scheduleOutput = `${name} is coming up next! it starts ${startTime}. ${link}`; } return new CommandOutput( diff --git a/lib/services/schedule.js b/lib/services/schedule.js index c9d9c9b..82de0ca 100644 --- a/lib/services/schedule.js +++ b/lib/services/schedule.js @@ -2,8 +2,9 @@ const google = require('googleapis').google; // eslint-disable-line prefer-destr const moment = require('moment-timezone'); class GoogleCal { - constructor(configuration) { + constructor(configuration, timezone) { this.configuration = configuration; + this.timezone = timezone; this.scopes = ['https://www.googleapis.com/auth/calendar.readonly']; this.googleCal = google.calendar({ version: 'v3', @@ -18,25 +19,26 @@ class GoogleCal { return true; } }); - if (!nextEvent) { return null; } - + + const startTime = moment.tz(nextEvent.start.dateTime, 'YYYY-MM-DDTHH:mm:ss', this.timezone) const isAllDay = !nextEvent.start.dateTime // All-day events don't have a start time, only a start day. let childEvent; if (isAllDay) { events.find((event) => { - const isSameDay = moment.tz(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss', event.start.timeZone).isSame(nextEvent.start.date, "day") + const startTime = moment.tz(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss', this.timezone) + const isSameDay = startTime.isSame(moment.tz(nextEvent.start.date, this.timezone), 'day'); if (isSameDay) { - childEvent = {start: event.start.dateTime, name: event.summary} + childEvent = {start: startTime.toISOString(true), name: event.summary} return true; } }); } return { - start: nextEvent.start.dateTime ?? nextEvent.start.date, + start: startTime.isValid() ? startTime.toISOString(true) : nextEvent.start.date, name: nextEvent.summary, allDay: isAllDay, childEvent: childEvent diff --git a/lib/services/service-index.js b/lib/services/service-index.js index 6ab4d0b..6163ad0 100644 --- a/lib/services/service-index.js +++ b/lib/services/service-index.js @@ -38,7 +38,7 @@ class Services { this.gulag = gulagService; this.lastfm = new LastFm(serviceConfigurations.lastFm); this.youtube = new YouTube(serviceConfigurations.youtube); - this.schedule = new GoogleCal(serviceConfigurations.googleCalendar); + this.schedule = new GoogleCal(serviceConfigurations.googleCalendar, serviceConfigurations.timezone); this.fakeScheduler = new FakeScheduler(serviceConfigurations.schedule); this.dggApi = new DggApi(serviceConfigurations.dggApi, this.logger); this.twitterApi = new TwitterApi(serviceConfigurations.twitter, this.logger); diff --git a/tests/lib/services/mocks/google-calendar-responses.json b/tests/lib/services/mocks/google-calendar-responses.json index 83c5a31..8d703b9 100644 --- a/tests/lib/services/mocks/google-calendar-responses.json +++ b/tests/lib/services/mocks/google-calendar-responses.json @@ -290,5 +290,68 @@ "eventType": "default" } ] + }, + "getAllDayEventWithSubEvent": { + "kind": "calendar#events", + "etag": "\"p33k8jk40kr7vq0g\"", + "summary": "Destiny - Stream Schedule", + "description": "My public streaming schedule.", + "updated": "2023-03-09T16:45:41.921Z", + "timeZone": "America/New_York", + "accessRole": "reader", + "defaultReminders": [], + "nextPageToken": "EiUKGjVpNHZyaXU4aXZjamtvMWppaGZjZDlsazRkGIDA3fKAnv4CIgcIBRDmkf8v", + "items": [ + { + "kind": "calendar#event", + "etag": "\"3353332517346000\"", + "id": "4ov5ja6j1otg2afbl91fpnvcj7", + "status": "confirmed", + "htmlLink": "https://www.google.com/calendar/event?eid=NG92NWphNmoxb3RnMmFmYmw5MWZwbnZjajcgaTU0ajRjdTlwbDQyNzBhc29rM21xZ2RyaGtAZw", + "created": "2023-02-17T20:37:38.000Z", + "updated": "2023-02-17T20:37:38.673Z", + "summary": "Stop the Steal Debate with Ali Alexander", + "creator": { "email": "steven.bonnell.ii@gmail.com" }, + "organizer": { + "email": "i54j4cu9pl4270asok3mqgdrhk@group.calendar.google.com", + "displayName": "Destiny - Stream Schedule", + "self": true + }, + "start": { "date": "2023-03-15" }, + "end": { "date": "2023-03-16" }, + "transparency": "transparent", + "iCalUID": "4ov5ja6j1otg2afbl91fpnvcj7@google.com", + "sequence": 0, + "eventType": "default" + }, + { + "kind": "calendar#event", + "etag": "\"3353332517346000\"", + "id": "4ov5ja6j1otg2afbl91fpnvcj7", + "status": "confirmed", + "htmlLink": "https://www.google.com/calendar/event?eid=NG92NWphNmoxb3RnMmFmYmw5MWZwbnZjajcgaTU0ajRjdTlwbDQyNzBhc29rM21xZ2RyaGtAZw", + "created": "2023-02-17T20:37:38.000Z", + "updated": "2023-02-17T20:37:38.673Z", + "summary": "Stop the Steal Ping Pong Break", + "creator": { "email": "steven.bonnell.ii@gmail.com" }, + "organizer": { + "email": "i54j4cu9pl4270asok3mqgdrhk@group.calendar.google.com", + "displayName": "Destiny - Stream Schedule", + "self": true + }, + "start": { + "dateTime": "2023-03-15T16:00:00", + "timeZone": "America/Chicago" + }, + "end": { + "dateTime": "2023-03-15T17:00:00", + "timeZone": "America/Chicago" + }, + "transparency": "transparent", + "iCalUID": "4ov5ja6j1otg2afbl91fpnvcj7@google.com", + "sequence": 0, + "eventType": "default" + } + ] } } diff --git a/tests/lib/services/schedule.test.js b/tests/lib/services/schedule.test.js index efcd7f9..e2b6350 100644 --- a/tests/lib/services/schedule.test.js +++ b/tests/lib/services/schedule.test.js @@ -42,7 +42,7 @@ describe('Schedule Tests', () => { .findNextStreamDay() .then(function (response) { return assert.deepStrictEqual(response, { - start: '2018-11-12T17:00:00-06:00', + start: '2018-11-12T17:00:00.000+00:00', name: 'Stream', allDay: false, childEvent: undefined, @@ -63,6 +63,20 @@ describe('Schedule Tests', () => { }); }); + it('Returns the next all-day calendar event with an event on the day', function () { + return buildSchedule(mockResponses['getAllDayEventWithSubEvent']) + .findNextStreamDay() + .then(function (response) { + return assert.deepStrictEqual(response, { + start: '2023-03-15', + name: 'Stop the Steal Debate with Ali Alexander', + allDay: true, + childEvent: {start: "2023-03-15T16:00:00.000+00:00", name: "Stop the Steal Ping Pong Break"}, + }); + }); + }); + + it('Returns `null` if no events', function () { return buildSchedule({ items: [] }) .findNextStreamDay() From 9fab531c6898ea1194b835c0228d27178253abeb Mon Sep 17 00:00:00 2001 From: Cai Jones <1808209+Riott@users.noreply.github.com> Date: Tue, 9 May 2023 10:34:31 +0100 Subject: [PATCH 04/12] Address PR feedback, use UTC in events API call, clean up unnecessary find --- lib/services/schedule.js | 29 ++++++++++++----------------- lib/services/service-index.js | 2 +- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/services/schedule.js b/lib/services/schedule.js index 82de0ca..192d24a 100644 --- a/lib/services/schedule.js +++ b/lib/services/schedule.js @@ -2,9 +2,8 @@ const google = require('googleapis').google; // eslint-disable-line prefer-destr const moment = require('moment-timezone'); class GoogleCal { - constructor(configuration, timezone) { + constructor(configuration) { this.configuration = configuration; - this.timezone = timezone; this.scopes = ['https://www.googleapis.com/auth/calendar.readonly']; this.googleCal = google.calendar({ version: 'v3', @@ -13,32 +12,27 @@ class GoogleCal { } findNextStreamDay() { - return this.getListOfUpcomingEvents(this.configuration.GOOGLE_CALENDAR_ID).then((events) => { - const nextEvent = events.find((event) => { - if (event.start !== undefined) { - return true; - } - }); - if (!nextEvent) { - return null; - } - - const startTime = moment.tz(nextEvent.start.dateTime, 'YYYY-MM-DDTHH:mm:ss', this.timezone) + return this.getListOfUpcomingEvents(this.configuration.GOOGLE_CALENDAR_ID).then((events) => { + if (events.length === 0) + return null + + const nextEvent = events[0]; + const startTime = moment.utc(nextEvent.start.dateTime, 'YYYY-MM-DDTHH:mm:ss') const isAllDay = !nextEvent.start.dateTime // All-day events don't have a start time, only a start day. let childEvent; if (isAllDay) { events.find((event) => { - const startTime = moment.tz(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss', this.timezone) - const isSameDay = startTime.isSame(moment.tz(nextEvent.start.date, this.timezone), 'day'); + const startTime = moment.utc(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss') + const isSameDay = startTime.isSame(moment.utc(nextEvent.start.date), 'day'); if (isSameDay) { - childEvent = {start: startTime.toISOString(true), name: event.summary} + childEvent = {start: startTime, name: event.summary} return true; } }); } return { - start: startTime.isValid() ? startTime.toISOString(true) : nextEvent.start.date, + start: startTime.isValid() ? startTime : nextEvent.start.date, name: nextEvent.summary, allDay: isAllDay, childEvent: childEvent @@ -58,6 +52,7 @@ class GoogleCal { singleEvents: true, timeMin: new Date().toISOString(), maxResults: 5, + timeZone: 'UTC' }) .then((response) => response.data.items); } diff --git a/lib/services/service-index.js b/lib/services/service-index.js index 6163ad0..6ab4d0b 100644 --- a/lib/services/service-index.js +++ b/lib/services/service-index.js @@ -38,7 +38,7 @@ class Services { this.gulag = gulagService; this.lastfm = new LastFm(serviceConfigurations.lastFm); this.youtube = new YouTube(serviceConfigurations.youtube); - this.schedule = new GoogleCal(serviceConfigurations.googleCalendar, serviceConfigurations.timezone); + this.schedule = new GoogleCal(serviceConfigurations.googleCalendar); this.fakeScheduler = new FakeScheduler(serviceConfigurations.schedule); this.dggApi = new DggApi(serviceConfigurations.dggApi, this.logger); this.twitterApi = new TwitterApi(serviceConfigurations.twitter, this.logger); From d24961ee5067df31c3d639ae280b71d0af158d18 Mon Sep 17 00:00:00 2001 From: Cai Jones <1808209+Riott@users.noreply.github.com> Date: Tue, 9 May 2023 10:40:09 +0100 Subject: [PATCH 05/12] add iso string back for test --- lib/services/schedule.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services/schedule.js b/lib/services/schedule.js index 192d24a..bf40c6c 100644 --- a/lib/services/schedule.js +++ b/lib/services/schedule.js @@ -25,14 +25,14 @@ class GoogleCal { const startTime = moment.utc(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss') const isSameDay = startTime.isSame(moment.utc(nextEvent.start.date), 'day'); if (isSameDay) { - childEvent = {start: startTime, name: event.summary} + childEvent = {start: startTime.toISOString(true), name: event.summary} return true; } }); } return { - start: startTime.isValid() ? startTime : nextEvent.start.date, + start: startTime.isValid() ? startTime.toISOString(true) : nextEvent.start.date, name: nextEvent.summary, allDay: isAllDay, childEvent: childEvent From e4e224d726a65a95ed49e93c27131d80dbf2474a Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Wed, 25 Dec 2024 17:21:51 -0800 Subject: [PATCH 06/12] Fix not being able to install deps with `npm ci` The lockfile wasn't synced after updating the version directive for `googleapis` in the package file. --- package-lock.json | 184 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 167 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1dc5101..8ddfca1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "bunyan": "^1.8.14", "fast-levenshtein": "^3.0.0", "google-auth-library": "^6.1.1", - "googleapis": "^61.0.0", + "googleapis": "^118.0.0", "inspector-influx": "^2.7.0", "inspector-metrics": "^1.21.0", "lodash": "^4.17.20", @@ -3259,31 +3259,177 @@ } }, "node_modules/googleapis": { - "version": "61.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-61.0.0.tgz", - "integrity": "sha512-aXaNgWKaALiYrfwrJ0ZYhRo2abyIBcqUjyNMgkbghKJMiCeOwcktlaGseH6JbPlCxXYCE8ZDfvAQqVNsf+6/RA==", + "version": "118.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-118.0.0.tgz", + "integrity": "sha512-Ny6zJOGn5P/YDT6GQbJU6K0lSzEu4Yuxnsn45ZgBIeSQ1RM0FolEjUToLXquZd89DU9wUfqA5XYHPEctk1TFWg==", "dependencies": { - "google-auth-library": "^6.0.0", - "googleapis-common": "^4.4.0" + "google-auth-library": "^8.0.2", + "googleapis-common": "^6.0.0" }, "engines": { - "node": ">=10" + "node": ">=12.0.0" } }, "node_modules/googleapis-common": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-4.4.3.tgz", - "integrity": "sha512-W46WKCk3QtlCCfmZyQIH5zxmDOyeV5Qj+qs7nr2ox08eRkEJMWp6iwv542R/PsokXaGUSrmif4vCC4+rGzRSsQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz", + "integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==", "dependencies": { "extend": "^3.0.2", - "gaxios": "^4.0.0", - "google-auth-library": "^6.0.0", + "gaxios": "^5.0.1", + "google-auth-library": "^8.0.2", "qs": "^6.7.0", "url-template": "^2.0.8", - "uuid": "^8.0.0" + "uuid": "^9.0.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=12.0.0" + } + }, + "node_modules/googleapis-common/node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/googleapis-common/node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/googleapis-common/node_modules/google-auth-library": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", + "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.3.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/googleapis-common/node_modules/google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "deprecated": "Package is no longer maintained", + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/googleapis-common/node_modules/gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "dependencies": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/googleapis/node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/googleapis/node_modules/gcp-metadata": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "dependencies": { + "gaxios": "^5.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/googleapis/node_modules/google-auth-library": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", + "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "dependencies": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^5.0.0", + "gcp-metadata": "^5.3.0", + "gtoken": "^6.1.0", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/googleapis/node_modules/google-p12-pem": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", + "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", + "deprecated": "Package is no longer maintained", + "dependencies": { + "node-forge": "^1.3.1" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/googleapis/node_modules/gtoken": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", + "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "dependencies": { + "gaxios": "^5.0.1", + "google-p12-pem": "^4.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=12.0.0" } }, "node_modules/gopd": { @@ -8172,9 +8318,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } From b53e834291452c5f7f02c2e111cda88e2cfaeb8b Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Wed, 25 Dec 2024 17:29:25 -0800 Subject: [PATCH 07/12] Format schedule service and command implementation --- lib/commands/implementations/schedule.js | 30 ++++++++++++------------ lib/services/schedule.js | 27 +++++++++++---------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/lib/commands/implementations/schedule.js b/lib/commands/implementations/schedule.js index d6f6b1d..aa9c1e8 100644 --- a/lib/commands/implementations/schedule.js +++ b/lib/commands/implementations/schedule.js @@ -10,28 +10,28 @@ function getNextStreamDate(input, services) { return new CommandOutput(null, `Nothing scheduled ${services.schedule.link()}`); } - const {allDay, name, start, childEvent} = nextStreamDay + const { allDay, name, start, childEvent } = nextStreamDay; const isHappening = moment(start).isBefore(moment()); const startTime = moment(start).fromNow(); - const link = services.schedule.link() + const link = services.schedule.link(); let scheduleOutput = ''; - + if (isHappening) { - scheduleOutput = allDay ? `${name} is todays all-day event! ${link}` : `${name} is currently happening! it started ${startTime}. ${link}`; - if (allDay && childEvent) { - const childEventIsHappening = moment(childEvent.start).isBefore(moment()); - scheduleOutput = childEventIsHappening ? `${name} is todays all-day event! ${childEvent.name} is currently happening! it started ${moment(childEvent.start).fromNow()}. ${link}` - : `${name} is todays all-day event! coming up ${moment(childEvent.start).fromNow()} is ${childEvent.name}. ${link}`; - } - } - else { + scheduleOutput = allDay + ? `${name} is todays all-day event! ${link}` + : `${name} is currently happening! it started ${startTime}. ${link}`; + if (allDay && childEvent) { + const childEventIsHappening = moment(childEvent.start).isBefore(moment()); + // prettier-ignore + scheduleOutput = childEventIsHappening + ? `${name} is todays all-day event! ${childEvent.name} is currently happening! it started ${moment(childEvent.start).fromNow()}. ${link}` + : `${name} is todays all-day event! coming up ${moment(childEvent.start).fromNow()} is ${childEvent.name}. ${link}`; + } + } else { scheduleOutput = `${name} is coming up next! it starts ${startTime}. ${link}`; } - return new CommandOutput( - null, - scheduleOutput, - ); + return new CommandOutput(null, scheduleOutput); }) .catch((err) => new CommandOutput(err, "Oops. Something didn't work. Check the logs.")); } diff --git a/lib/services/schedule.js b/lib/services/schedule.js index bf40c6c..5e0106d 100644 --- a/lib/services/schedule.js +++ b/lib/services/schedule.js @@ -12,30 +12,31 @@ class GoogleCal { } findNextStreamDay() { - return this.getListOfUpcomingEvents(this.configuration.GOOGLE_CALENDAR_ID).then((events) => { - if (events.length === 0) - return null - - const nextEvent = events[0]; - const startTime = moment.utc(nextEvent.start.dateTime, 'YYYY-MM-DDTHH:mm:ss') - const isAllDay = !nextEvent.start.dateTime // All-day events don't have a start time, only a start day. + return this.getListOfUpcomingEvents(this.configuration.GOOGLE_CALENDAR_ID).then((events) => { + if (events.length === 0) { + return null; + } + + const nextEvent = events[0]; + const startTime = moment.utc(nextEvent.start.dateTime, 'YYYY-MM-DDTHH:mm:ss'); + const isAllDay = !nextEvent.start.dateTime; // All-day events don't have a start time, only a start day. let childEvent; if (isAllDay) { events.find((event) => { - const startTime = moment.utc(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss') + const startTime = moment.utc(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss'); const isSameDay = startTime.isSame(moment.utc(nextEvent.start.date), 'day'); if (isSameDay) { - childEvent = {start: startTime.toISOString(true), name: event.summary} + childEvent = { start: startTime.toISOString(true), name: event.summary }; return true; } }); } - + return { start: startTime.isValid() ? startTime.toISOString(true) : nextEvent.start.date, name: nextEvent.summary, - allDay: isAllDay, - childEvent: childEvent + allDay: isAllDay, + childEvent: childEvent, }; }); } @@ -52,7 +53,7 @@ class GoogleCal { singleEvents: true, timeMin: new Date().toISOString(), maxResults: 5, - timeZone: 'UTC' + timeZone: 'UTC', }) .then((response) => response.data.items); } From 2f0c7d7d2805a26f04461a3dee084a7bd000507a Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Wed, 25 Dec 2024 19:33:20 -0800 Subject: [PATCH 08/12] Refactor processing of events Converting all events in the API response into objects that only contain relevant properties immediately makes the logic easier to follow. We can also leave datetimes as Moment.js objects until it's time to build the string response for the `!schedule` command. --- lib/commands/implementations/schedule.js | 8 +-- lib/services/schedule.js | 71 +++++++++++++++--------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/lib/commands/implementations/schedule.js b/lib/commands/implementations/schedule.js index aa9c1e8..44b59ed 100644 --- a/lib/commands/implementations/schedule.js +++ b/lib/commands/implementations/schedule.js @@ -11,8 +11,8 @@ function getNextStreamDate(input, services) { } const { allDay, name, start, childEvent } = nextStreamDay; - const isHappening = moment(start).isBefore(moment()); - const startTime = moment(start).fromNow(); + const isHappening = start.isBefore(moment()); + const startTime = start.fromNow(); const link = services.schedule.link(); let scheduleOutput = ''; @@ -24,8 +24,8 @@ function getNextStreamDate(input, services) { const childEventIsHappening = moment(childEvent.start).isBefore(moment()); // prettier-ignore scheduleOutput = childEventIsHappening - ? `${name} is todays all-day event! ${childEvent.name} is currently happening! it started ${moment(childEvent.start).fromNow()}. ${link}` - : `${name} is todays all-day event! coming up ${moment(childEvent.start).fromNow()} is ${childEvent.name}. ${link}`; + ? `${name} is todays all-day event! ${childEvent.name} is currently happening! it started ${childEvent.start.fromNow()}. ${link}` + : `${name} is todays all-day event! coming up ${childEvent.start.fromNow()} is ${childEvent.name}. ${link}`; } } else { scheduleOutput = `${name} is coming up next! it starts ${startTime}. ${link}`; diff --git a/lib/services/schedule.js b/lib/services/schedule.js index 5e0106d..f831dfe 100644 --- a/lib/services/schedule.js +++ b/lib/services/schedule.js @@ -1,6 +1,33 @@ const google = require('googleapis').google; // eslint-disable-line prefer-destructuring const moment = require('moment-timezone'); +/** + * @typedef {Object} Event + * @property {boolean} allDay + * @property {moment.Moment} start + * @property {string} name + */ + +const EVENT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ss'; + +/** + * @returns {Event} + */ +function buildEvent(event) { + // All-day events don't have a start time, only a start day. + const allDay = !event.start.dateTime; + + const start = allDay + ? moment.utc(event.start.date) + : moment.utc(event.start.dateTime, EVENT_DATETIME_FORMAT); + + return { + allDay, + start, + name: event.summary, + }; +} + class GoogleCal { constructor(configuration) { this.configuration = configuration; @@ -12,33 +39,23 @@ class GoogleCal { } findNextStreamDay() { - return this.getListOfUpcomingEvents(this.configuration.GOOGLE_CALENDAR_ID).then((events) => { - if (events.length === 0) { - return null; - } - - const nextEvent = events[0]; - const startTime = moment.utc(nextEvent.start.dateTime, 'YYYY-MM-DDTHH:mm:ss'); - const isAllDay = !nextEvent.start.dateTime; // All-day events don't have a start time, only a start day. - let childEvent; - if (isAllDay) { - events.find((event) => { - const startTime = moment.utc(event.start.dateTime, 'YYYY-MM-DDTHH:mm:ss'); - const isSameDay = startTime.isSame(moment.utc(nextEvent.start.date), 'day'); - if (isSameDay) { - childEvent = { start: startTime.toISOString(true), name: event.summary }; - return true; - } - }); - } - - return { - start: startTime.isValid() ? startTime.toISOString(true) : nextEvent.start.date, - name: nextEvent.summary, - allDay: isAllDay, - childEvent: childEvent, - }; - }); + return this.getListOfUpcomingEvents(this.configuration.GOOGLE_CALENDAR_ID).then( + (googleEvents) => { + const events = googleEvents.map(buildEvent); + if (!events.length) { + return null; + } + + const nextEvent = events[0]; + if (nextEvent.allDay) { + nextEvent.childEvent = events.find((event) => { + return !event.allDay && event.start.isSame(nextEvent.start, 'day'); + }); + } + + return nextEvent; + }, + ); } link() { From cd7aba50dcc4737f6c91b8d3cb400e5b73abe134 Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Wed, 25 Dec 2024 22:14:21 -0800 Subject: [PATCH 09/12] Remove unneeded datetime processing format It appears the latest version of the Google Calendar API affixes a timezone offset to the end of event datetime strings. As such, Moment.js is capable of correctly parsing the string without help. --- lib/services/schedule.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/services/schedule.js b/lib/services/schedule.js index f831dfe..b716a65 100644 --- a/lib/services/schedule.js +++ b/lib/services/schedule.js @@ -8,8 +8,6 @@ const moment = require('moment-timezone'); * @property {string} name */ -const EVENT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ss'; - /** * @returns {Event} */ @@ -17,9 +15,7 @@ function buildEvent(event) { // All-day events don't have a start time, only a start day. const allDay = !event.start.dateTime; - const start = allDay - ? moment.utc(event.start.date) - : moment.utc(event.start.dateTime, EVENT_DATETIME_FORMAT); + const start = allDay ? moment.utc(event.start.date) : moment.utc(event.start.dateTime); return { allDay, From c1f293539a17853e1fca27611fdca108aceff573 Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Wed, 25 Dec 2024 22:18:08 -0800 Subject: [PATCH 10/12] Fix unit tests The datetime strings in the sample response payload still followed the old API, which don't have time zone offsets. The assertions in each schedule service-related test should account for start times being Moment.js objects. --- .../mocks/google-calendar-responses.json | 28 +++++++++---------- tests/lib/services/schedule.test.js | 17 ++++++----- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/tests/lib/services/mocks/google-calendar-responses.json b/tests/lib/services/mocks/google-calendar-responses.json index 8d703b9..45de1b2 100644 --- a/tests/lib/services/mocks/google-calendar-responses.json +++ b/tests/lib/services/mocks/google-calendar-responses.json @@ -29,16 +29,16 @@ "self": true }, "start": { - "dateTime": "2018-11-12T17:00:00-06:00", + "dateTime": "2018-11-12T17:00:00Z", "timeZone": "America/Chicago" }, "end": { - "dateTime": "2018-11-13T00:00:00-06:00", + "dateTime": "2018-11-13T00:00:00Z", "timeZone": "America/Chicago" }, "recurringEventId": "37gr7bg214nu3avfnso74ofeek", "originalStartTime": { - "dateTime": "2018-11-12T17:00:00-06:00", + "dateTime": "2018-11-12T17:00:00Z", "timeZone": "America/Chicago" }, "iCalUID": "37gr7bg214nu3avfnso74ofeek@google.com", @@ -68,16 +68,16 @@ "self": true }, "start": { - "dateTime": "2018-11-13T17:00:00-06:00", + "dateTime": "2018-11-13T17:00:00Z", "timeZone": "America/Chicago" }, "end": { - "dateTime": "2018-11-14T00:00:00-06:00", + "dateTime": "2018-11-14T00:00:00Z", "timeZone": "America/Chicago" }, "recurringEventId": "37gr7bg214nu3avfnso74ofeek", "originalStartTime": { - "dateTime": "2018-11-13T17:00:00-06:00", + "dateTime": "2018-11-13T17:00:00Z", "timeZone": "America/Chicago" }, "iCalUID": "37gr7bg214nu3avfnso74ofeek@google.com", @@ -107,16 +107,16 @@ "self": true }, "start": { - "dateTime": "2018-11-14T16:30:00-06:00", + "dateTime": "2018-11-14T16:30:00Z", "timeZone": "America/Chicago" }, "end": { - "dateTime": "2018-11-14T21:00:00-06:00", + "dateTime": "2018-11-14T21:00:00Z", "timeZone": "America/Chicago" }, "recurringEventId": "70oj8e1i64pjeb9j70o3ib9k6lgm4bb26gqjcbb6cgs6cpb3cdj3cp9m6k_R20181114T223000", "originalStartTime": { - "dateTime": "2018-11-14T16:30:00-06:00", + "dateTime": "2018-11-14T16:30:00Z", "timeZone": "America/Chicago" }, "iCalUID": "70oj8e1i64pjeb9j70o3ib9k6lgm4bb26gqjcbb6cgs6cpb3cdj3cp9m6k_R20181114T223000@google.com", @@ -146,16 +146,16 @@ "self": true }, "start": { - "dateTime": "2018-11-14T21:00:00-06:00", + "dateTime": "2018-11-14T21:00:00Z", "timeZone": "America/Chicago" }, "end": { - "dateTime": "2018-11-15T01:00:00-06:00", + "dateTime": "2018-11-15T01:00:00Z", "timeZone": "America/Chicago" }, "recurringEventId": "6t3s1oqf861ner26ehhknn2b88", "originalStartTime": { - "dateTime": "2018-11-14T21:00:00-06:00", + "dateTime": "2018-11-14T21:00:00Z", "timeZone": "America/Chicago" }, "iCalUID": "6t3s1oqf861ner26ehhknn2b88@google.com", @@ -340,11 +340,11 @@ "self": true }, "start": { - "dateTime": "2023-03-15T16:00:00", + "dateTime": "2023-03-15T16:00:00Z", "timeZone": "America/Chicago" }, "end": { - "dateTime": "2023-03-15T17:00:00", + "dateTime": "2023-03-15T17:00:00Z", "timeZone": "America/Chicago" }, "transparency": "transparent", diff --git a/tests/lib/services/schedule.test.js b/tests/lib/services/schedule.test.js index e2b6350..63e5bac 100644 --- a/tests/lib/services/schedule.test.js +++ b/tests/lib/services/schedule.test.js @@ -1,6 +1,7 @@ const assert = require('assert'); const sinon = require('sinon'); const proxyquire = require('proxyquire').noCallThru(); +const moment = require('moment'); const mockResponses = require('./mocks/google-calendar-responses.json'); describe('Schedule Tests', () => { @@ -42,10 +43,9 @@ describe('Schedule Tests', () => { .findNextStreamDay() .then(function (response) { return assert.deepStrictEqual(response, { - start: '2018-11-12T17:00:00.000+00:00', - name: 'Stream', allDay: false, - childEvent: undefined, + start: moment.utc('2018-11-12T17:00:00Z'), + name: 'Stream', }); }); }); @@ -55,7 +55,7 @@ describe('Schedule Tests', () => { .findNextStreamDay() .then(function (response) { return assert.deepStrictEqual(response, { - start: '2023-03-15', + start: moment.utc('2023-03-15'), name: 'Stop the Steal Debate with Ali Alexander', allDay: true, childEvent: undefined, @@ -68,15 +68,18 @@ describe('Schedule Tests', () => { .findNextStreamDay() .then(function (response) { return assert.deepStrictEqual(response, { - start: '2023-03-15', + start: moment.utc('2023-03-15'), name: 'Stop the Steal Debate with Ali Alexander', allDay: true, - childEvent: {start: "2023-03-15T16:00:00.000+00:00", name: "Stop the Steal Ping Pong Break"}, + childEvent: { + allDay: false, + start: moment.utc('2023-03-15T16:00:00Z'), + name: 'Stop the Steal Ping Pong Break', + }, }); }); }); - it('Returns `null` if no events', function () { return buildSchedule({ items: [] }) .findNextStreamDay() From 64178f78331baf3e2b8b53b7ee33be0a10f5fd96 Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Wed, 25 Dec 2024 22:34:41 -0800 Subject: [PATCH 11/12] Rephrase schedule command responses --- lib/commands/implementations/schedule.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/commands/implementations/schedule.js b/lib/commands/implementations/schedule.js index 44b59ed..77b5c35 100644 --- a/lib/commands/implementations/schedule.js +++ b/lib/commands/implementations/schedule.js @@ -7,7 +7,7 @@ function getNextStreamDate(input, services) { .findNextStreamDay() .then((nextStreamDay) => { if (!nextStreamDay) { - return new CommandOutput(null, `Nothing scheduled ${services.schedule.link()}`); + return new CommandOutput(null, `Nothing is scheduled. ${services.schedule.link()}`); } const { allDay, name, start, childEvent } = nextStreamDay; @@ -18,17 +18,18 @@ function getNextStreamDate(input, services) { if (isHappening) { scheduleOutput = allDay - ? `${name} is todays all-day event! ${link}` - : `${name} is currently happening! it started ${startTime}. ${link}`; + ? `${name}, an all-day event, is scheduled today. ${link}` + : `${name} is currently happening! It started ${startTime}. ${link}`; + if (allDay && childEvent) { const childEventIsHappening = moment(childEvent.start).isBefore(moment()); // prettier-ignore scheduleOutput = childEventIsHappening - ? `${name} is todays all-day event! ${childEvent.name} is currently happening! it started ${childEvent.start.fromNow()}. ${link}` - : `${name} is todays all-day event! coming up ${childEvent.start.fromNow()} is ${childEvent.name}. ${link}`; + ? `${name}, an all-day event, is scheduled today. ${childEvent.name} is currently happening! It started ${childEvent.start.fromNow()}. ${link}` + : `${name}, an all-day event, is scheduled today. Coming up ${childEvent.start.fromNow()} is ${childEvent.name}. ${link}`; } } else { - scheduleOutput = `${name} is coming up next! it starts ${startTime}. ${link}`; + scheduleOutput = `${name} is the next scheduled event. It starts ${startTime}. ${link}`; } return new CommandOutput(null, scheduleOutput); From f7d14cb7f064eb8239ba678aa3fef86aab225816 Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Wed, 25 Dec 2024 22:53:47 -0800 Subject: [PATCH 12/12] Reduce duplicate logic when buidling response --- lib/commands/implementations/schedule.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/commands/implementations/schedule.js b/lib/commands/implementations/schedule.js index 77b5c35..acba291 100644 --- a/lib/commands/implementations/schedule.js +++ b/lib/commands/implementations/schedule.js @@ -12,26 +12,26 @@ function getNextStreamDate(input, services) { const { allDay, name, start, childEvent } = nextStreamDay; const isHappening = start.isBefore(moment()); - const startTime = start.fromNow(); - const link = services.schedule.link(); let scheduleOutput = ''; - if (isHappening) { - scheduleOutput = allDay - ? `${name}, an all-day event, is scheduled today. ${link}` - : `${name} is currently happening! It started ${startTime}. ${link}`; - - if (allDay && childEvent) { + if (isHappening && allDay) { + scheduleOutput = `${name}, an all-day event, is scheduled today.`; + if (childEvent) { const childEventIsHappening = moment(childEvent.start).isBefore(moment()); + // prettier-ignore - scheduleOutput = childEventIsHappening - ? `${name}, an all-day event, is scheduled today. ${childEvent.name} is currently happening! It started ${childEvent.start.fromNow()}. ${link}` - : `${name}, an all-day event, is scheduled today. Coming up ${childEvent.start.fromNow()} is ${childEvent.name}. ${link}`; + scheduleOutput += childEventIsHappening + ? ` ${childEvent.name} is currently happening! It started ${childEvent.start.fromNow()}.` + : ` Coming up ${childEvent.start.fromNow()} is ${childEvent.name}.`; } + } else if (isHappening) { + scheduleOutput = `${name} is currently happening! It started ${start.fromNow()}.`; } else { - scheduleOutput = `${name} is the next scheduled event. It starts ${startTime}. ${link}`; + scheduleOutput = `${name} is the next scheduled event. It starts ${start.fromNow()}.`; } + scheduleOutput += ` ${services.schedule.link()}`; + return new CommandOutput(null, scheduleOutput); }) .catch((err) => new CommandOutput(err, "Oops. Something didn't work. Check the logs."));