Enhance calendar CLI: add search, date, range, month commands
This commit is contained in:
parent
2206332ff0
commit
27d8f16313
2 changed files with 105 additions and 7 deletions
6
TOOLS.md
6
TOOLS.md
|
|
@ -141,6 +141,12 @@ calendar today # Today's events (default)
|
||||||
calendar tomorrow # Tomorrow's events
|
calendar tomorrow # Tomorrow's events
|
||||||
calendar week # Next 7 days
|
calendar week # Next 7 days
|
||||||
calendar next # Next 14 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)
|
- Credentials: `.credentials/services.env` (NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_PASS, CALDAV_CALENDAR)
|
||||||
|
|
|
||||||
102
bin/calendar
102
bin/calendar
|
|
@ -137,7 +137,29 @@ async function main() {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const cmd = args[0] || 'today';
|
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 range;
|
||||||
|
let label = cmd;
|
||||||
|
const searchQuery = args.slice(1).join(' ').toLowerCase();
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case 'today':
|
case 'today':
|
||||||
range = getDateRange(0, 1);
|
range = getDateRange(0, 1);
|
||||||
|
|
@ -151,12 +173,71 @@ async function main() {
|
||||||
case 'next':
|
case 'next':
|
||||||
range = getDateRange(0, 14);
|
range = getDateRange(0, 14);
|
||||||
break;
|
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:
|
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(' today — Events for today (default)');
|
||||||
console.log(' tomorrow — Events for tomorrow');
|
console.log(' tomorrow — Events for tomorrow');
|
||||||
console.log(' week — Events for the next 7 days');
|
console.log(' week — Next 7 days');
|
||||||
console.log(' next — Events for the next 14 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);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,9 +248,20 @@ async function main() {
|
||||||
process.exit(1);
|
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) {
|
if (events.length === 0) {
|
||||||
console.log(`No events (${cmd}).`);
|
console.log(`No events (${label}).`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue