2026-03-08 22:38:30 +01:00
|
|
|
#!/usr/bin/env ts-node
|
2026-03-08 22:27:43 +01:00
|
|
|
|
2026-03-08 22:38:30 +01:00
|
|
|
import BringApi from 'bring-shopping';
|
|
|
|
|
|
|
|
|
|
interface Credentials {
|
|
|
|
|
mail: string;
|
|
|
|
|
password: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface BringItem {
|
|
|
|
|
name: string;
|
|
|
|
|
specification: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface BringItems {
|
|
|
|
|
uuid: string;
|
|
|
|
|
status: string;
|
|
|
|
|
purchase: BringItem[];
|
|
|
|
|
recently: BringItem[];
|
|
|
|
|
}
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
// Load credentials from environment variables
|
2026-03-08 22:38:30 +01:00
|
|
|
const CREDENTIALS: Credentials = {
|
|
|
|
|
mail: process.env.BRING_EMAIL || '',
|
|
|
|
|
password: process.env.BRING_PASSWORD || ''
|
2026-03-08 22:27:43 +01:00
|
|
|
};
|
|
|
|
|
|
2026-03-08 22:38:30 +01:00
|
|
|
const LIST_UUID: string = process.env.BRING_LIST_UUID || '';
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
// Validate required environment variables
|
|
|
|
|
if (!CREDENTIALS.mail || !CREDENTIALS.password || !LIST_UUID) {
|
|
|
|
|
console.error('Error: Missing required environment variables');
|
|
|
|
|
console.error('Please set: BRING_EMAIL, BRING_PASSWORD, BRING_LIST_UUID');
|
|
|
|
|
console.error('See SKILL.md for setup instructions');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 22:38:30 +01:00
|
|
|
async function main(): Promise<void> {
|
|
|
|
|
const args: string[] = process.argv.slice(2);
|
|
|
|
|
const command: string | undefined = args[0];
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
if (!command) {
|
|
|
|
|
console.error('Usage: bring <add|list|remove|recent> [args...]');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 22:38:30 +01:00
|
|
|
const bring = new BringApi(CREDENTIALS);
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await bring.login();
|
|
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
|
case 'add':
|
|
|
|
|
await addItem(bring, args.slice(1));
|
|
|
|
|
break;
|
|
|
|
|
case 'list':
|
|
|
|
|
await listItems(bring);
|
|
|
|
|
break;
|
|
|
|
|
case 'remove':
|
|
|
|
|
await removeItem(bring, args.slice(1));
|
|
|
|
|
break;
|
|
|
|
|
case 'recent':
|
|
|
|
|
await recentItems(bring);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
console.error(`Unknown command: ${command}`);
|
|
|
|
|
console.error('Available commands: add, list, remove, recent');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2026-03-08 22:38:30 +01:00
|
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
|
|
|
console.error('Error:', errorMessage);
|
2026-03-08 22:27:43 +01:00
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add an item to the shopping list
|
|
|
|
|
*/
|
2026-03-08 22:38:30 +01:00
|
|
|
async function addItem(bring: any, args: string[]): Promise<void> {
|
|
|
|
|
const itemName: string | undefined = args[0];
|
|
|
|
|
const specification: string = args.slice(1).join(' ') || '';
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
if (!itemName) {
|
|
|
|
|
console.error('Usage: bring add <item> [specification]');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await bring.saveItem(LIST_UUID, itemName, specification);
|
|
|
|
|
|
|
|
|
|
if (specification) {
|
|
|
|
|
console.log(`✓ "${itemName}" (${specification}) added to shopping list`);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`✓ "${itemName}" added to shopping list`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List all items currently on the shopping list
|
|
|
|
|
*/
|
2026-03-08 22:38:30 +01:00
|
|
|
async function listItems(bring: any): Promise<void> {
|
|
|
|
|
const items: BringItems = await bring.getItems(LIST_UUID);
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
if (items.purchase.length === 0) {
|
|
|
|
|
console.log('Shopping list is empty');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('Shopping list:');
|
|
|
|
|
console.log('');
|
2026-03-08 22:38:30 +01:00
|
|
|
items.purchase.forEach((item: BringItem, index: number) => {
|
2026-03-08 22:27:43 +01:00
|
|
|
if (item.specification) {
|
|
|
|
|
console.log(`${index + 1}. ${item.name} (${item.specification})`);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`${index + 1}. ${item.name}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove an item from the shopping list (mark as purchased)
|
|
|
|
|
*/
|
2026-03-08 22:38:30 +01:00
|
|
|
async function removeItem(bring: any, args: string[]): Promise<void> {
|
|
|
|
|
const itemName: string = args.join(' ');
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
if (!itemName) {
|
|
|
|
|
console.error('Usage: bring remove <item>');
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await bring.removeItem(LIST_UUID, itemName);
|
|
|
|
|
console.log(`✓ "${itemName}" removed from shopping list`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Show recently purchased items
|
|
|
|
|
*/
|
2026-03-08 22:38:30 +01:00
|
|
|
async function recentItems(bring: any): Promise<void> {
|
|
|
|
|
const items: BringItems = await bring.getItems(LIST_UUID);
|
2026-03-08 22:27:43 +01:00
|
|
|
|
|
|
|
|
if (items.recently.length === 0) {
|
|
|
|
|
console.log('No recently purchased items');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('Recently purchased:');
|
|
|
|
|
console.log('');
|
2026-03-08 22:38:30 +01:00
|
|
|
items.recently.slice(0, 10).forEach((item: BringItem, index: number) => {
|
2026-03-08 22:27:43 +01:00
|
|
|
if (item.specification) {
|
|
|
|
|
console.log(`${index + 1}. ${item.name} (${item.specification})`);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`${index + 1}. ${item.name}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main();
|