Skip to content

Commit aaa8164

Browse files
committed
feat: initial release v0.1.0 — Send minimalist homelab alerts with ASCII sparklines to Discord/Slack.
0 parents  commit aaa8164

11 files changed

Lines changed: 1239 additions & 0 deletions

File tree

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
node_modules/
2+
dist/
3+
.env
4+
.env.local
5+
*.log
6+
.DS_Store
7+
Thumbs.db
8+
coverage/
9+
.turbo/
10+
*.tsbuildinfo

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 AdametherzLab
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# webhook-spark ⚡
2+
3+
**Send minimalist homelab alerts with ASCII sparklines to Discord/Slack**
4+
5+
## ✨ Features
6+
7+
- 📊 **Beautiful ASCII sparklines** – Turn boring number arrays into visual trends
8+
- 🔌 **Webhook support** – Discord and Slack out of the box
9+
- 🚀 **Zero dependencies** – Uses only Node.js/Bun built-ins
10+
- 📦 **Tiny footprint** – Less than 10KB minified
11+
- 🛡️ **TypeScript first** – Full type safety and autocomplete
12+
- 🎨 **Customizable** – Multiple character sets and styling options
13+
14+
## 📦 Installation
15+
16+
```bash
17+
# Using Bun (recommended)
18+
bun add webhook-spark
19+
20+
# Using npm
21+
npm install webhook-spark
22+
23+
# Using yarn
24+
yarn add webhook-spark
25+
```
26+
27+
## 🚀 Quick Start
28+
29+
```typescript
30+
// REMOVED external import: import { generateSparkline, sendWebhook } from 'webhook-spark';
31+
32+
// Create a sparkline from your metrics
33+
const cpuUsage = [10, 25, 60, 85, 90, 45, 30];
34+
const sparkline = generateSparkline(cpuUsage);
35+
36+
// Send to Discord
37+
await sendWebhook({
38+
url: 'https://discord.com/api/webhooks/your-webhook-id',
39+
provider: 'discord',
40+
content: `CPU usage: ${sparkline}`,
41+
username: 'Server Monitor'
42+
});
43+
44+
console.log(`📈 Sparkline sent: ${sparkline}`);
45+
// Output: 📈 Sparkline sent: ▁▂▅▇██▃▂
46+
```
47+
48+
## 📖 API Reference
49+
50+
### Sparkline Generation
51+
52+
```typescript
53+
// REMOVED external import: import { generateSparkline, generateSparklineWithOutliers } from 'webhook-spark';
54+
55+
// Basic sparkline
56+
const sparkline = generateSparkline([1, 2, 3, 4, 5]);
57+
// Returns: ▁▂▃▄▅
58+
59+
// With custom options
60+
const custom = generateSparkline([10, 50, 90], {
61+
characterSet: '·∙●○◉◎',
62+
minValue: 0,
63+
maxValue: 100
64+
});
65+
66+
// Handle outliers
67+
const withOutliers = generateSparklineWithOutliers(
68+
[1, 1000, 2, 3, 4],
69+
{ threshold: 2 } // Values > 2 standard deviations marked
70+
);
71+
```
72+
73+
### Webhook Delivery
74+
75+
```typescript
76+
// REMOVED external import: import { sendWebhook } from 'webhook-spark';
77+
78+
// Discord example
79+
await sendWebhook({
80+
url: 'DISCORD_WEBHOOK_URL',
81+
provider: 'discord',
82+
content: 'Server alert!',
83+
embeds: [{
84+
title: 'CPU Usage',
85+
description: generateSparkline([10, 25, 60, 85]),
86+
color: 0xff0000,
87+
timestamp: new Date().toISOString()
88+
}]
89+
});
90+
91+
// Slack example
92+
await sendWebhook({
93+
url: 'SLACK_WEBHOOK_URL',
94+
provider: 'slack',
95+
content: 'Daily metrics',
96+
blocks: [{
97+
type: 'section',
98+
text: { type: 'mrkdwn', text: `*Memory usage:* ${generateSparkline([30, 45, 60])}` }
99+
}]
100+
});
101+
```
102+
103+
### Type Utilities
104+
105+
```typescript
106+
// REMOVED external import: import { isNumericArray, isWebhookConfig } from 'webhook-spark';
107+
108+
// Type guards for validation
109+
if (isNumericArray(data)) {
110+
// data is now typed as readonly number[]
111+
const sparkline = generateSparkline(data);
112+
}
113+
114+
const config = { url: '...', provider: 'discord' };
115+
if (isWebhookConfig(config)) {
116+
// config is valid WebhookConfig
117+
await sendWebhook(config);
118+
}
119+
```
120+
121+
## 🧪 Examples
122+
123+
### Homelab CPU Monitor
124+
125+
```typescript
126+
// REMOVED external import: import { generateSparkline, sendWebhook } from 'webhook-spark';
127+
import os from 'os';
128+
129+
// Simulate collecting CPU metrics
130+
const cpuMetrics = [45, 60, 75, 85, 90, 80, 65];
131+
const sparkline = generateSparkline(cpuMetrics);
132+
133+
// Send alert if high usage
134+
if (cpuMetrics[cpuMetrics.length - 1] > 80) {
135+
await sendWebhook({
136+
url: process.env.DISCORD_WEBHOOK!,
137+
provider: 'discord',
138+
content: `🚨 High CPU usage detected!`,
139+
embeds: [{
140+
title: 'CPU Trend',
141+
description: `\`${sparkline}\``,
142+
fields: [
143+
{ name: 'Current', value: `${cpuMetrics[cpuMetrics.length - 1]}%`, inline: true },
144+
{ name: 'Peak', value: `${Math.max(...cpuMetrics)}%`, inline: true }
145+
],
146+
color: 0xff5500
147+
}]
148+
});
149+
}
150+
```
151+
152+
### Daily Health Report
153+
154+
```typescript
155+
// REMOVED external import: import { generateSparkline, generateASCIIArt } from 'webhook-spark';
156+
157+
// Create a dashboard-like report
158+
const report = `
159+
📊 **Daily System Report**
160+
━━━━━━━━━━━━━━━━━━━━
161+
CPU: ${generateSparkline([10, 25, 40, 60, 45, 30])}
162+
Memory: ${generateSparkline([50, 55, 60, 65, 70, 68])}
163+
Disk: ${generateSparkline([85, 86, 87, 88, 89, 90])}
164+
165+
${generateASCIIArt('HEALTHY', { font: 'block' })}
166+
`;
167+
168+
await sendWebhook({
169+
url: process.env.SLACK_WEBHOOK!,
170+
provider: 'slack',
171+
content: report
172+
});
173+
```
174+
175+
## 🔧 Configuration
176+
177+
```json
178+
{
179+
"defaultWebhook": "https://discord.com/api/webhooks/your-id",
180+
"defaultProvider": "discord",
181+
"dataDir": "~/.webhook-spark/data",
182+
"maxEntries": 1000
183+
}
184+
```
185+
186+
## 🤝 Contributing
187+
188+
Found a bug? Have an idea for a new feature? Contributions are welcome!
189+
190+
1. Fork the repository
191+
2. Create a feature branch (`git checkout -b cool-new-feature`)
192+
3. Commit your changes (`git commit -am 'Add cool feature'`)
193+
4. Push to the branch (`git push origin cool-new-feature`)
194+
5. Open a Pull Request
195+
196+
## 📄 License
197+
198+
MIT © AdametherzLab
199+
200+
---
201+
202+
Made with ⚡ by homelab enthusiasts for homelab enthusiasts. Keep your servers happy and your alerts beautiful!

SECURITY.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
5+
If you discover a security vulnerability in this project, please report it responsibly.
6+
7+
**Contact:** security@adametherzlab.com
8+
9+
**Process:**
10+
1. Email the details of the vulnerability
11+
2. Include steps to reproduce if possible
12+
3. We will acknowledge your report within 48 hours
13+
4. We aim to fix critical issues within 7 days
14+
15+
## Supported Versions
16+
17+
| Version | Supported |
18+
| ------- | --------- |
19+
| 0.x.x | Yes |
20+
21+
## Disclosure Policy
22+
23+
- We follow responsible disclosure practices
24+
- Please do not publicly disclose vulnerabilities before they are fixed
25+
- We will credit reporters in our release notes (unless you prefer anonymity)

package.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@adametherzlab/webhook-spark",
3+
"version": "0.1.0",
4+
"description": "Send minimalist homelab alerts with ASCII sparklines to Discord/Slack.",
5+
"type": "module",
6+
"main": "src/index.ts",
7+
"module": "src/index.ts",
8+
"types": "src/index.ts",
9+
"exports": {
10+
".": {
11+
"types": "./src/index.ts",
12+
"import": "./src/index.ts",
13+
"default": "./src/index.ts"
14+
}
15+
},
16+
"scripts": {
17+
"test": "bun test",
18+
"build": "bun build src/index.ts --outdir dist",
19+
"typecheck": "tsc --noEmit"
20+
},
21+
"devDependencies": {
22+
"bun-types": "latest",
23+
"typescript": "^5.5.0"
24+
},
25+
"keywords": [
26+
"typescript",
27+
"bun",
28+
"homelab",
29+
"devops",
30+
"monitoring",
31+
"discord-webhook",
32+
"cli"
33+
],
34+
"author": "AdametherzLab",
35+
"license": "MIT",
36+
"repository": {
37+
"type": "git",
38+
"url": "https://github.com/AdametherzLab/webhook-spark.git"
39+
},
40+
"homepage": "https://github.com/AdametherzLab/webhook-spark#readme",
41+
"bugs": {
42+
"url": "https://github.com/AdametherzLab/webhook-spark/issues"
43+
},
44+
"engines": {
45+
"node": ">=18"
46+
},
47+
"sideEffects": false
48+
}

src/index.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type {
2+
SparklineConfig,
3+
SparklineCharacterSet,
4+
ASCIIArtConfig,
5+
WebhookPayload,
6+
WebhookResponse,
7+
WebhookConfig,
8+
ValidationError,
9+
WebhookError,
10+
NumericArray,
11+
WebhookSendResult,
12+
SparklineGenerationResult,
13+
ConfigFile,
14+
DiscordEmbed,
15+
SlackBlock,
16+
HttpMethod,
17+
HttpRequestOptions,
18+
WebhookProvider,
19+
} from "./types.js";
20+
import { generateSparkline, generateSparklineWithOutliers, generateASCIIArt } from "./sparkline.js";
21+
import { sendWebhook } from "./webhook.js";
22+
import { isNumericArray, isSparklineConfig, isWebhookConfig } from "./types.js";
23+
24+
export {
25+
generateSparkline,
26+
generateSparklineWithOutliers,
27+
generateASCIIArt,
28+
sendWebhook,
29+
isNumericArray,
30+
isSparklineConfig,
31+
isWebhookConfig,
32+
};
33+
export type {
34+
SparklineConfig,
35+
SparklineCharacterSet,
36+
ASCIIArtConfig,
37+
WebhookPayload,
38+
WebhookResponse,
39+
WebhookConfig,
40+
ValidationError,
41+
WebhookError,
42+
NumericArray,
43+
WebhookSendResult,
44+
SparklineGenerationResult,
45+
ConfigFile,
46+
DiscordEmbed,
47+
SlackBlock,
48+
HttpMethod,
49+
HttpRequestOptions,
50+
WebhookProvider,
51+
};

0 commit comments

Comments
 (0)