From c82e17654ef6e174acd0b4b383d233d3d47cf481 Mon Sep 17 00:00:00 2001 From: Kaan Barmore-Genc Date: Sun, 19 Apr 2026 23:03:18 -0500 Subject: [PATCH] Add discord-send CLI and personal CLAUDE.md discord-send is a Bun script that posts to a Discord webhook (URL read from ~/.config/discord-send/webhook), with optional --attach for files. The personal CLAUDE.md tells Claude Code to use it for notifications and image sharing. Both deploy via Dotter. Co-Authored-By: Claude Opus 4.7 (1M context) --- .dotter/global.toml | 16 ++++++++ bin/discord-send | 93 +++++++++++++++++++++++++++++++++++++++++++++ claude/CLAUDE.md | 10 +++++ setup.sh | 2 +- 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100755 bin/discord-send create mode 100644 claude/CLAUDE.md diff --git a/.dotter/global.toml b/.dotter/global.toml index ff38ee2..7e16296 100644 --- a/.dotter/global.toml +++ b/.dotter/global.toml @@ -60,5 +60,21 @@ depends = [] [asdf.variables] +[claude] +depends = [] + +[claude.files] +"claude/CLAUDE.md" = "~/.claude/CLAUDE.md" + +[claude.variables] + +[bin] +depends = [] + +[bin.files] +"bin/discord-send" = "~/.local/bin/discord-send" + +[bin.variables] + [settings] default_target_type = "automatic" diff --git a/bin/discord-send b/bin/discord-send new file mode 100755 index 0000000..3bd1fd2 --- /dev/null +++ b/bin/discord-send @@ -0,0 +1,93 @@ +#!/usr/bin/env bun + +import { basename, join } from "node:path"; +import { homedir } from "node:os"; + +const configDir = process.env.XDG_CONFIG_HOME || join(homedir(), ".config"); +const webhookFile = join(configDir, "discord-send", "webhook"); + +function usage() { + console.error( + "Usage: discord-send [--attach ] \n" + + "\n" + + "Sends a message to a Discord channel via webhook.\n" + + `Reads the webhook URL from ${webhookFile}.`, + ); +} + +const args = Bun.argv.slice(2); +let attachPath = null; +const messageParts = []; + +for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg === "--attach") { + attachPath = args[++i]; + if (!attachPath) { + console.error("Error: --attach requires a file path"); + process.exit(2); + } + } else if (arg.startsWith("--attach=")) { + attachPath = arg.slice("--attach=".length); + } else if (arg === "-h" || arg === "--help") { + usage(); + process.exit(0); + } else { + messageParts.push(arg); + } +} + +const message = messageParts.join(" "); + +const webhookFileHandle = Bun.file(webhookFile); +if (!(await webhookFileHandle.exists())) { + console.error( + `Error: webhook file not found: ${webhookFile}\n` + + "Create it and write your Discord webhook URL inside.", + ); + process.exit(1); +} +const webhookUrl = (await webhookFileHandle.text()).trim(); +if (!webhookUrl) { + console.error(`Error: webhook file is empty: ${webhookFile}`); + process.exit(1); +} + +if (!message && !attachPath) { + usage(); + process.exit(2); +} + +let body; +const headers = {}; + +if (attachPath) { + const file = Bun.file(attachPath); + if (!(await file.exists())) { + console.error(`Error: file not found: ${attachPath}`); + process.exit(1); + } + const filename = basename(attachPath); + const payload = { attachments: [{ id: 0, filename }] }; + if (message) payload.content = message; + + const form = new FormData(); + form.append("payload_json", JSON.stringify(payload)); + form.append("files[0]", file, filename); + body = form; +} else { + headers["Content-Type"] = "application/json"; + body = JSON.stringify({ content: message }); +} + +const response = await fetch(webhookUrl, { method: "POST", headers, body }); + +if (!response.ok) { + const text = await response.text(); + console.error( + `Error: Discord returned ${response.status} ${response.statusText}\n${text}`, + ); + process.exit(1); +} + +console.log("Message sent successfully"); diff --git a/claude/CLAUDE.md b/claude/CLAUDE.md new file mode 100644 index 0000000..bc7ace2 --- /dev/null +++ b/claude/CLAUDE.md @@ -0,0 +1,10 @@ +# Personal Claude Code Instructions + +## Notifying me / sharing images via Discord + +The `discord-send` CLI is on my PATH. Use it whenever I ask you to notify me when something is done, or to show me an image or video — send it to my Discord and I'll see it there. + +```sh +discord-send 'Build finished successfully' +discord-send --attach ./screenshot.png 'Here is the rendered page' +``` diff --git a/setup.sh b/setup.sh index 8dad085..20e08ab 100755 --- a/setup.sh +++ b/setup.sh @@ -229,7 +229,7 @@ cat > .dotter/local.toml << EOF # Machine-specific Dotter configuration # This file is generated by setup.sh -packages = ["git", "nvim", "fish", "zellij", "htop", "asdf"] +packages = ["git", "nvim", "fish", "zellij", "htop", "asdf", "claude", "bin"] [variables] git_name = "$GIT_NAME"