Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions .github/ISSUE_TEMPLATE/meeting.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,23 @@
'Asia/Tokyo',
'Australia/Sydney'
].map((zone) => {
return `| ${zone} | ${date.setZone(zone).toFormat('EEE dd-MMM-yyyy HH:mm (hh:mm a)')} |`
const zonedDate = date.toZonedDateTimeISO(zone)
const formatter = new Intl.DateTimeFormat('en-US', {
weekday: 'short',
month: 'short',
day: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true,
timeZone: zone
})
const formattedDate = formatter.format(new Date(zonedDate.epochMilliseconds))
return `| ${zone} | ${formattedDate} |`
}).join('\n') %>

Or in your local time:
* https://www.timeanddate.com/worldclock/?iso=<%= date.toFormat("yyyy-MM-dd'T'HH:mm:ss") %>
* https://www.timeanddate.com/worldclock/?iso=<%= date.toZonedDateTimeISO('UTC').toPlainDateTime().toString() %>

## Agenda

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
node-version: [18, 20, 22, 24]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -24,17 +24,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js 18.x
- name: Use Node.js 24
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 24
- run: npm install
- uses: ./
id: maker
with:
token: ${{ secrets.GITHUB_TOKEN }}
schedules: 2020-04-02T17:00:00.0Z/P1D
issueTitle: 'Test Meeting <%= date.toFormat("yyyy-MM-dd") %>'
issueTitle: 'Test Meeting <%= date.toZonedDateTimeISO("UTC").toPlainDate().toString() %>'
createWithin: P2D
meetingLabels: test
agendaLabel: meeting-agenda-test
Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ inputs:
schedules:
description: 'An array of strings representing the meeting schedules in the format YYYY-MM-DD (ISO-8601 intervals).'
required: true
timezone:
description: 'Timezone to use for schedule times (e.g. "America/Chicago", "Europe/Madrid"). Useful for following daylight saving time (DST) / summer time. Default: "UTC"'
required: false
createWithin:
description: 'Defines when the meeting issues are created using ISO-8601 durations. Defaults to one week before the meeting (Using the ISO-8601 durations format, this is P7D).'
default: 'P7D'
Expand Down
16 changes: 14 additions & 2 deletions lib/default-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,23 @@ ${[
'Asia/Tokyo',
'Australia/Sydney'
].map((zone) => {
return `| ${zone} | ${date.setZone(zone).toFormat('EEE dd-MMM-yyyy HH:mm (hh:mm a)')} |`
const zonedDate = date.toZonedDateTimeISO(zone)
const formatter = new Intl.DateTimeFormat('en-US', {
weekday: 'short',
month: 'short',
day: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true,
timeZone: zone
})
const formattedDate = formatter.format(new Date(zonedDate.epochMilliseconds))
return `| ${zone} | ${formattedDate} |`
}).join('\n')}

Or in your local time:
* https://www.timeanddate.com/worldclock/?iso=${date.toFormat("yyyy-MM-dd'T'HH:mm:ss")}
* https://www.timeanddate.com/worldclock/?iso=${date.toZonedDateTimeISO('UTC').toPlainDateTime().toString()}

## Agenda

Expand Down
49 changes: 36 additions & 13 deletions lib/meetings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
'use strict'
const { DateTime, Interval, Duration } = require('luxon')

/* global Temporal */

const polyfill = require('@js-temporal/polyfill')
global.Temporal = polyfill.Temporal
const issues = require('./issues')

module.exports.createNextMeeting = async function (client, opts) {
Expand All @@ -18,8 +22,8 @@ module.exports.setMeetingIssueBody = async function (client, opts) {
}

function getNextIssue (opts) {
const now = opts.now || DateTime.utc()
const date = getNextScheduledMeeting(opts.schedules, now)
const now = opts.now || Temporal.Now.instant()
const date = getNextScheduledMeeting(opts.schedules, now, opts.timezone)
const title = typeof opts.issueTitle === 'function' ? opts.issueTitle({ date }) : opts.issueTitle

const issue = {
Expand All @@ -39,13 +43,15 @@ function getNextIssue (opts) {
}

const shouldCreateNextMeetingIssue = module.exports.shouldCreateNextMeetingIssue = async function (client, opts = {}) {
const now = opts.now || DateTime.utc()
const createWithin = Duration.fromISO(opts.createWithin)
const now = opts.now || Temporal.Now.instant()
const createWithin = Temporal.Duration.from(opts.createWithin)
const issue = getNextIssue(opts)
const { date: next, title: nextIssueTitle } = issue

// Further out than the create within limit
if (next > now.plus(createWithin)) {
const zonedNow = now.toZonedDateTimeISO('UTC')
const zonedNext = next.toZonedDateTimeISO('UTC')
if (zonedNext.epochMilliseconds > zonedNow.add(createWithin).epochMilliseconds) {
return false
}

Expand All @@ -70,19 +76,36 @@ const shouldCreateNextMeetingIssue = module.exports.shouldCreateNextMeetingIssue
return issue
}

const getNextScheduledMeeting = module.exports.getNextScheduledMeeting = function (schedules = [], now = DateTime.utc()) {
const getNextScheduledMeeting = module.exports.getNextScheduledMeeting = function (schedules = [], now = Temporal.Now.instant(), timezone) {
return schedules
.map((s = `${now}/P7D`) => {
// Parse interval
const i = Interval.fromISO(s)
const d = i.toDuration()
const [startStr, durationStr] = s.split('/')
const duration = Temporal.Duration.from(durationStr)
let next

if (timezone) {
// parse as local in specified tz
try {
const zonedStart = Temporal.ZonedDateTime.from(`${startStr}[${timezone}]`)
next = zonedStart.toInstant()
} catch (e) {
console.warn(`invalid timezone '${timezone}'; using UTC`)
console.error(e) // s/b caused by invalid timezone but log error just in case for troubleshooting
}
}

if (!next) {
// parse as UTC
next = Temporal.Instant.from(startStr)
}

// Get datetime for next event after now
let next = i.s
while (next < now) {
next = next.plus(d)
while (next.epochMilliseconds <= now.epochMilliseconds) {
const zonedNext = next.toZonedDateTimeISO(timezone || 'UTC')
next = zonedNext.add(duration).toInstant()
}

return next
}).sort().shift()
}).sort((a, b) => a.epochMilliseconds - b.epochMilliseconds).shift()
}
12 changes: 9 additions & 3 deletions lib/notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
const { default: HackMD } = require('@hackmd/api')
const defaultNotesTemplate = require('./default-notes-template')

module.exports.create = function createNote (notesTemplate, opts) {
module.exports.create = async function createNote (notesTemplate, opts) {
const note = typeof notesTemplate === 'function' ? notesTemplate(opts) : notesTemplate || defaultNotesTemplate(opts)
const hackmd = new HackMD()

return hackmd.newNote(note)
try {
const hackmd = new HackMD()
return await hackmd.newNote(note)
} catch (e) {
console.error('failed to create hackMD note:', e.message)
console.log(`note would have been:\n${note}`)
return null
}
}

async function getNotesTemplate (client, opts) {
Expand Down
Loading