Enhance calendar CLI: add search, date, range, month commands

This commit is contained in:
Hoid 2026-02-18 07:38:57 +00:00
parent 2206332ff0
commit 27d8f16313
2 changed files with 105 additions and 7 deletions

View file

@ -141,6 +141,12 @@ calendar today # Today's events (default)
calendar tomorrow # Tomorrow's events
calendar week # Next 7 days
calendar next # Next 14 days
calendar month # Next 30 days
calendar date 2026-02-20 # Events on a specific date
calendar 2026-02-20 # Shorthand for date
calendar range 2026-02-18 2026-02-25 # Date range
calendar search dentist # Search by name/location (next 90 days)
calendar search rpg 180 # Search with custom day range
```
- Credentials: `.credentials/services.env` (NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_PASS, CALDAV_CALENDAR)

View file

@ -137,7 +137,29 @@ async function main() {
const args = process.argv.slice(2);
const cmd = args[0] || 'today';
// Parse date string (YYYY-MM-DD or natural offsets)
function parseDate(str) {
const m = str.match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (m) {
const d = new Date(Date.UTC(+m[1], +m[2]-1, +m[3]));
d.setUTCHours(0, 0, 0, 0);
return d;
}
// Try as number of days offset
const n = parseInt(str);
if (!isNaN(n)) {
const d = new Date();
d.setUTCHours(0, 0, 0, 0);
d.setUTCDate(d.getUTCDate() + n);
return d;
}
return null;
}
let range;
let label = cmd;
const searchQuery = args.slice(1).join(' ').toLowerCase();
switch (cmd) {
case 'today':
range = getDateRange(0, 1);
@ -151,12 +173,71 @@ async function main() {
case 'next':
range = getDateRange(0, 14);
break;
case 'month':
range = getDateRange(0, 30);
break;
case 'range': {
// calendar range 2026-02-18 2026-02-25
const from = args[1] && parseDate(args[1]);
const to = args[2] && parseDate(args[2]);
if (!from || !to) {
console.error('Usage: calendar range <start-date> <end-date>');
console.error(' Dates: YYYY-MM-DD or offset in days (e.g., 0 = today, 7 = in 7 days)');
process.exit(1);
}
const fmt = d => d.toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '');
range = { start: fmt(from), end: fmt(to) };
label = `${args[1]} to ${args[2]}`;
break;
}
case 'search': {
// calendar search <query> [days] — search events by name, default 30 days
if (!searchQuery) {
console.error('Usage: calendar search <query> [days]');
console.error(' Searches event summaries/locations. Default: next 30 days.');
process.exit(1);
}
const days = parseInt(args[args.length - 1]);
const searchDays = (!isNaN(days) && args.length > 2) ? days : 90;
range = getDateRange(0, searchDays);
label = `search "${searchQuery}" (${searchDays} days)`;
break;
}
case 'date': {
// calendar date 2026-02-20
const d = args[1] && parseDate(args[1]);
if (!d) {
console.error('Usage: calendar date <YYYY-MM-DD>');
process.exit(1);
}
const next = new Date(d);
next.setUTCDate(next.getUTCDate() + 1);
const fmt = d => d.toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '');
range = { start: fmt(d), end: fmt(next) };
label = args[1];
break;
}
default:
console.log('Usage: calendar [today|tomorrow|week|next]');
// Try as date: calendar 2026-02-20
const asDate = parseDate(cmd);
if (asDate) {
const next = new Date(asDate);
next.setUTCDate(next.getUTCDate() + 1);
const fmt = d => d.toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '');
range = { start: fmt(asDate), end: fmt(next) };
label = cmd;
break;
}
console.log('Usage: calendar <command>');
console.log(' today — Events for today (default)');
console.log(' tomorrow — Events for tomorrow');
console.log(' week — Events for the next 7 days');
console.log(' next — Events for the next 14 days');
console.log(' week — Next 7 days');
console.log(' next — Next 14 days');
console.log(' month — Next 30 days');
console.log(' date <YYYY-MM-DD> — Events on a specific date');
console.log(' <YYYY-MM-DD> — Shorthand for date');
console.log(' range <from> <to> — Events in date range (YYYY-MM-DD or day offset)');
console.log(' search <query> [days] — Search events by name/location (default: 90 days)');
process.exit(0);
}
@ -167,9 +248,20 @@ async function main() {
process.exit(1);
}
const events = parseEvents(res.data);
let events = parseEvents(res.data);
// Filter by search query if searching
if (cmd === 'search' && searchQuery) {
const q = searchQuery.replace(/\s+\d+$/, ''); // strip trailing days number
events = events.filter(e =>
(e.summary || '').toLowerCase().includes(q) ||
(e.location || '').toLowerCase().includes(q) ||
(e.description || '').toLowerCase().includes(q)
);
}
if (events.length === 0) {
console.log(`No events (${cmd}).`);
console.log(`No events (${label}).`);
return;
}