diff --git a/CHANGELOG.md b/CHANGELOG.md index da40683..8d92e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,9 +51,14 @@ Unlike `opencode-notifier` and `opencode-notificator`, this plugin: ## [Unreleased] +### Added + +- Support for additional Bark API parameters: subtitle, url, group, icon, sound, call, ciphertext, level +- Environment variable support for all new configuration options (DAY_APP_SUBTITLE, DAY_APP_URL, DAY_APP_GROUP, DAY_APP_ICON, DAY_APP_SOUND, DAY_APP_CALL, DAY_APP_CIPHERTEXT, DAY_APP_LEVEL) +- Backward compatibility for config file names (supports both opencode-notify.json and opencode-message-notify.json) + ### Planned -- Support for custom notification sounds via Bark - Batch notification mode for long sessions - Usage limit alerts - Multi-device support diff --git a/README.md b/README.md index 42db76a..e36b372 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,14 @@ Create `~/.config/opencode/opencode-notify.json`: { "token": "your_bark_device_token", "title": "OpenCode", + "subtitle": "Agent Task", + "url": "https://github.com/...", + "group": "opencode", + "icon": "https://example.com/icon.png", + "sound": "alarm", + "call": 1, + "ciphertext": "...", + "level": "timeSensitive", "notifyOnComplete": true, "notifyOnPermission": true, "includeUsageStats": true, @@ -121,6 +129,14 @@ Create `~/.config/opencode/opencode-notify.json`: |--------|------|---------|-------------| | `token` | string | (env) | Bark device token | | `title` | string | `"OpenCode"` | Notification title prefix | +| `subtitle` | string | undefined | Notification subtitle (iOS path parameter) | +| `url` | string | undefined | URL to open when notification is clicked | +| `group` | string | undefined | Notification group name for grouping | +| `icon` | string | undefined | Custom notification icon URL (iOS 15+) | +| `sound` | string | undefined | Notification sound name | +| `call` | string/number | undefined | Repeat sound for 30 seconds | +| `ciphertext` | string | undefined | Encrypted message content | +| `level` | string | undefined | Notification level: active, timeSensitive, passive, critical | | `notifyOnComplete` | boolean | `true` | Send notification on session completion | | `notifyOnPermission` | boolean | `true` | Send notification for permission requests | | `includeUsageStats` | boolean | `true` | Include cost and token statistics | diff --git a/src/bark.ts b/src/bark.ts index a810172..f057e95 100644 --- a/src/bark.ts +++ b/src/bark.ts @@ -22,7 +22,40 @@ export async function sendBarkNotification( const encodedTitle = encodeURIComponent(title); const encodedBody = encodeURIComponent(body.trim()); - const url = `https://api.day.app/${config.token}/${encodedTitle}/${encodedBody}`; + + // Build URL path + let path = `/${config.token}/${encodedTitle}`; + if (config.subtitle?.trim()) { + path += `/${encodeURIComponent(config.subtitle.trim())}`; + } + path += `/${encodedBody}`; + + // Build query parameters + const params = new URLSearchParams(); + if (config.url?.trim()) { + params.append("url", config.url.trim()); + } + if (config.group?.trim()) { + params.append("group", config.group.trim()); + } + if (config.icon?.trim()) { + params.append("icon", config.icon.trim()); + } + if (config.sound?.trim()) { + params.append("sound", config.sound.trim()); + } + if (config.call !== undefined && config.call !== null) { + params.append("call", String(config.call)); + } + if (config.ciphertext?.trim()) { + params.append("ciphertext", config.ciphertext.trim()); + } + if (config.level?.trim()) { + params.append("level", config.level.trim()); + } + + const queryString = params.toString(); + const url = `https://api.day.app${path}${queryString ? `?${queryString}` : ""}`; try { await fetch(url); diff --git a/src/config.ts b/src/config.ts index 3894981..57ea7b9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -6,7 +6,7 @@ import * as fs from "fs"; import * as path from "path"; import type { PluginConfig } from "./types.js"; -const CONFIG_FILE_NAME = "opencode-message-notify.json"; +const CONFIG_FILE_NAMES = ["opencode-notify.json", "opencode-message-notify.json"]; const CONFIG_DIR = ".config/opencode"; /** @@ -18,10 +18,17 @@ function getHomeDir(): string { /** * Get the configuration file path + * Tries new filename first, falls back to old filename */ -function getConfigPath(): string { +function getConfigPath(): string | null { const homeDir = getHomeDir(); - return path.join(homeDir, CONFIG_DIR, CONFIG_FILE_NAME); + for (const fileName of CONFIG_FILE_NAMES) { + const configPath = path.join(homeDir, CONFIG_DIR, fileName); + if (fs.existsSync(configPath)) { + return configPath; + } + } + return null; } /** @@ -41,21 +48,46 @@ export function loadConfig(): PluginConfig { }; // Try to load from config file - if (fs.existsSync(configPath)) { + if (configPath) { try { const fileContent = fs.readFileSync(configPath, "utf-8"); const fileConfig = JSON.parse(fileContent); return { ...defaultConfig, ...fileConfig }; } catch { - // If config file is invalid, use defaults console.warn(`[opencode-message-notify] Invalid config file at ${configPath}`); } } - // Load from environment variable as fallback - const envToken = process.env.DAY_APP_TOKEN?.trim(); - if (envToken) { - defaultConfig.token = envToken; + // Load from environment variables (env vars override defaults) + if (process.env.DAY_APP_TOKEN?.trim()) { + defaultConfig.token = process.env.DAY_APP_TOKEN.trim(); + } + if (process.env.DAY_APP_TITLE?.trim()) { + defaultConfig.title = process.env.DAY_APP_TITLE.trim(); + } + if (process.env.DAY_APP_SUBTITLE?.trim()) { + defaultConfig.subtitle = process.env.DAY_APP_SUBTITLE.trim(); + } + if (process.env.DAY_APP_URL?.trim()) { + defaultConfig.url = process.env.DAY_APP_URL.trim(); + } + if (process.env.DAY_APP_GROUP?.trim()) { + defaultConfig.group = process.env.DAY_APP_GROUP.trim(); + } + if (process.env.DAY_APP_ICON?.trim()) { + defaultConfig.icon = process.env.DAY_APP_ICON.trim(); + } + if (process.env.DAY_APP_SOUND?.trim()) { + defaultConfig.sound = process.env.DAY_APP_SOUND.trim(); + } + if (process.env.DAY_APP_CALL?.trim()) { + defaultConfig.call = process.env.DAY_APP_CALL.trim(); + } + if (process.env.DAY_APP_CIPHERTEXT?.trim()) { + defaultConfig.ciphertext = process.env.DAY_APP_CIPHERTEXT.trim(); + } + if (process.env.DAY_APP_LEVEL?.trim()) { + defaultConfig.level = process.env.DAY_APP_LEVEL.trim() as 'active' | 'timeSensitive' | 'passive' | 'critical'; } return defaultConfig; diff --git a/src/types.ts b/src/types.ts index ba1cd81..241be7c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,6 +16,22 @@ export interface PluginConfig { token?: string; /** Notification title template */ title?: string; + /** Notification subtitle (iOS path parameter) */ + subtitle?: string; + /** URL to open when notification is clicked */ + url?: string; + /** Notification group name for grouping */ + group?: string; + /** Custom notification icon URL (iOS 15+) */ + icon?: string; + /** Notification sound name */ + sound?: string; + /** Repeat sound for 30 seconds */ + call?: string | number; + /** Encrypted message content */ + ciphertext?: string; + /** Notification level: active, timeSensitive, passive, critical */ + level?: 'active' | 'timeSensitive' | 'passive' | 'critical'; /** Whether to send notifications on session completion */ notifyOnComplete?: boolean; /** Whether to send notifications on permission requests */