@bobfrankston/gcal 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gcal.ts +52 -4
- package/glib/gutils.ts +23 -0
- package/package.json +1 -1
package/gcal.ts
CHANGED
|
@@ -90,7 +90,8 @@ async function listEvents(
|
|
|
90
90
|
accessToken: string,
|
|
91
91
|
calendarId = 'primary',
|
|
92
92
|
maxResults = 10,
|
|
93
|
-
timeMin?: string
|
|
93
|
+
timeMin?: string,
|
|
94
|
+
timeMax?: string
|
|
94
95
|
): Promise<GoogleEvent[]> {
|
|
95
96
|
const params = new URLSearchParams({
|
|
96
97
|
maxResults: maxResults.toString(),
|
|
@@ -98,6 +99,9 @@ async function listEvents(
|
|
|
98
99
|
orderBy: 'startTime',
|
|
99
100
|
timeMin: timeMin || new Date().toISOString()
|
|
100
101
|
});
|
|
102
|
+
if (timeMax) {
|
|
103
|
+
params.set('timeMax', timeMax);
|
|
104
|
+
}
|
|
101
105
|
|
|
102
106
|
const url = `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events?${params}`;
|
|
103
107
|
const res = await apiFetch(url, accessToken);
|
|
@@ -239,14 +243,18 @@ Options:
|
|
|
239
243
|
-defaultUser <email> Set default user for future use
|
|
240
244
|
-c, -calendar <id> Calendar ID (default: primary)
|
|
241
245
|
-n <count> Number of events to list
|
|
246
|
+
-after <when> List events after this date/time
|
|
247
|
+
-before <when> List events before this date/time
|
|
242
248
|
-v, -verbose Show event IDs and links
|
|
243
249
|
|
|
244
250
|
Examples:
|
|
245
251
|
gcal meeting.ics Import ICS file
|
|
246
252
|
gcal list List next 10 events
|
|
253
|
+
gcal list -after tomorrow -before "jan 30"
|
|
247
254
|
gcal add "Dentist" "Friday 3pm" "1h"
|
|
248
255
|
gcal add "Lunch" "1/14/2026 12:00" "1h"
|
|
249
|
-
gcal add "Meeting" "tomorrow
|
|
256
|
+
gcal add "Meeting" "tomorrow 10am"
|
|
257
|
+
gcal add "Call" "3pm" "30m"
|
|
250
258
|
gcal add "Appointment" "jan 15 2pm"
|
|
251
259
|
gcal -defaultUser bob@gmail.com Set default user
|
|
252
260
|
|
|
@@ -266,6 +274,8 @@ interface ParsedArgs {
|
|
|
266
274
|
help: boolean;
|
|
267
275
|
verbose: boolean;
|
|
268
276
|
icsFile: string; /** Direct .ics file path */
|
|
277
|
+
after: string; /** Filter: events after this date/time */
|
|
278
|
+
before: string; /** Filter: events before this date/time */
|
|
269
279
|
}
|
|
270
280
|
|
|
271
281
|
function parseArgs(argv: string[]): ParsedArgs {
|
|
@@ -278,7 +288,9 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
278
288
|
count: 10,
|
|
279
289
|
help: false,
|
|
280
290
|
verbose: false,
|
|
281
|
-
icsFile: ''
|
|
291
|
+
icsFile: '',
|
|
292
|
+
after: '',
|
|
293
|
+
before: ''
|
|
282
294
|
};
|
|
283
295
|
|
|
284
296
|
const unknown: string[] = [];
|
|
@@ -303,6 +315,14 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
303
315
|
case '-n':
|
|
304
316
|
result.count = parseInt(argv[++i]) || 10;
|
|
305
317
|
break;
|
|
318
|
+
case '-after':
|
|
319
|
+
case '--after':
|
|
320
|
+
result.after = argv[++i] || '';
|
|
321
|
+
break;
|
|
322
|
+
case '-before':
|
|
323
|
+
case '--before':
|
|
324
|
+
result.before = argv[++i] || '';
|
|
325
|
+
break;
|
|
306
326
|
case '-v':
|
|
307
327
|
case '-verbose':
|
|
308
328
|
case '--verbose':
|
|
@@ -428,7 +448,9 @@ async function main(): Promise<void> {
|
|
|
428
448
|
case 'list': {
|
|
429
449
|
const count = parsed.args[0] ? parseInt(parsed.args[0]) : parsed.count;
|
|
430
450
|
const token = await getAccessToken(user, false);
|
|
431
|
-
const
|
|
451
|
+
const timeMin = parsed.after ? parseDateTime(parsed.after).toISOString() : undefined;
|
|
452
|
+
const timeMax = parsed.before ? parseDateTime(parsed.before).toISOString() : undefined;
|
|
453
|
+
const events = await listEvents(token, parsed.calendar, count, timeMin, timeMax);
|
|
432
454
|
|
|
433
455
|
if (events.length === 0) {
|
|
434
456
|
console.log('No upcoming events found.');
|
|
@@ -484,6 +506,32 @@ async function main(): Promise<void> {
|
|
|
484
506
|
const durationMins = parseDuration(duration);
|
|
485
507
|
const endTime = new Date(startTime.getTime() + durationMins * 60 * 1000);
|
|
486
508
|
|
|
509
|
+
// Check for suspicious dates (likely parsing errors)
|
|
510
|
+
const now = new Date();
|
|
511
|
+
const twoYearsFromNow = new Date(now);
|
|
512
|
+
twoYearsFromNow.setFullYear(twoYearsFromNow.getFullYear() + 2);
|
|
513
|
+
|
|
514
|
+
let warning = '';
|
|
515
|
+
if (startTime < now) {
|
|
516
|
+
warning = `Date is in the past: ${formatDateTime({ dateTime: startTime.toISOString() })}`;
|
|
517
|
+
} else if (startTime > twoYearsFromNow) {
|
|
518
|
+
warning = `Date is more than 2 years away: ${formatDateTime({ dateTime: startTime.toISOString() })}`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (warning) {
|
|
522
|
+
console.log(`\nWarning: ${warning}`);
|
|
523
|
+
console.log(`Input was: "${when}"`);
|
|
524
|
+
const readline = await import('readline');
|
|
525
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
526
|
+
const response = await new Promise<string>(resolve => {
|
|
527
|
+
rl.question('Continue? (y/N) ', answer => { rl.close(); resolve(answer); });
|
|
528
|
+
});
|
|
529
|
+
if (response.toLowerCase() !== 'y') {
|
|
530
|
+
console.log('Cancelled.');
|
|
531
|
+
process.exit(0);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
487
535
|
const event: GoogleEvent = {
|
|
488
536
|
summary: title,
|
|
489
537
|
start: {
|
package/glib/gutils.ts
CHANGED
|
@@ -307,6 +307,29 @@ export function parseDateTime(input: string): Date {
|
|
|
307
307
|
return d;
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
// Handle time with am/pm: "10am", "3pm", "10:30am", "3:45pm" - assume today
|
|
311
|
+
const timeAmPmMatch = lower.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/);
|
|
312
|
+
if (timeAmPmMatch) {
|
|
313
|
+
const [, hour, min, ampm] = timeAmPmMatch;
|
|
314
|
+
const d = new Date(now);
|
|
315
|
+
let h = parseInt(hour);
|
|
316
|
+
if (ampm === 'pm' && h < 12) h += 12;
|
|
317
|
+
if (ampm === 'am' && h === 12) h = 0;
|
|
318
|
+
d.setHours(h, parseInt(min || '0'), 0, 0);
|
|
319
|
+
return d;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Handle bare hour: "10", "14" - interpret as today at that hour (not a month)
|
|
323
|
+
const bareHourMatch = input.match(/^(\d{1,2})$/);
|
|
324
|
+
if (bareHourMatch) {
|
|
325
|
+
const hour = parseInt(bareHourMatch[1]);
|
|
326
|
+
if (hour >= 0 && hour <= 23) {
|
|
327
|
+
const d = new Date(now);
|
|
328
|
+
d.setHours(hour, 0, 0, 0);
|
|
329
|
+
return d;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
310
333
|
// Try native Date parsing
|
|
311
334
|
const parsed = new Date(input);
|
|
312
335
|
if (!isNaN(parsed.getTime())) {
|