@toanalien/ahha
v0.1.1
Published
Notification library for Node.js. Send messages to Slack, Discord, Telegram, Email, and more through URL-based configuration.
Maintainers
Readme
ahha
Notification library for Node.js. Send messages to Slack, Discord, Telegram, Email, and more through URL-based configuration.
Inspired by Shoutrrr (Go) and Apprise (Python).
Features
- URL-based configuration -- configure services with a single URL string
- TypeScript-first -- full type safety and IntelliSense
- Zero runtime dependencies -- uses native
fetchand Node.js built-ins - 6 built-in services -- Slack, Discord, Telegram, Email (SMTP), Webhook, Ntfy
- 4 delivery strategies -- direct, fallback, broadcast, round-robin
- Extensible -- register custom services with
registerService() - Dual output -- ESM + CommonJS
Install
npm install @toanalien/ahhaRequires Node.js 18+.
Quick Start
import { send, createSender } from '@toanalien/ahha';
// One-liner: send via a single service
await send('ntfy://[email protected]', 'Deployment complete!');
// Multi-service sender with broadcast (default)
const sender = createSender([
'slack://xoxb:token@C12345678',
'discord://webhooktoken@webhookid',
'telegram://123456:ABCDEF@telegram?chats=-100123',
]);
await sender.send('Server is up!');
// Fallback strategy: try services in order until one succeeds
const fallback = createSender([
'slack://xoxb:token@channel',
'ntfy://[email protected]',
], { strategy: 'fallback' });
await fallback.send('Alert!');Supported Services
| Service | URL Format | Example |
|---------|-----------|---------|
| Slack (webhook) | slack://hook:TOKEN@webhook | slack://hook:T00/B00/XXX@webhook |
| Slack (bot) | slack://xoxb:TOKEN@CHANNEL | slack://xoxb:xoxb-123@C12345678 |
| Discord | discord://TOKEN@WEBHOOK_ID | discord://abcdef@123456789 |
| Telegram | telegram://TOKEN@telegram?chats=ID | telegram://123:ABC@telegram?chats=-100 |
| Email | smtp://USER:PASS@HOST:PORT?to=ADDR | smtp://user:[email protected]:[email protected] |
| Webhook | generic+https://HOST/PATH | generic+https://example.com/webhook |
| Ntfy | ntfy://TOPIC[@HOST] | ntfy://[email protected] |
Delivery Strategies
| Strategy | Behavior |
|----------|----------|
| broadcast (default) | Send to all services concurrently |
| fallback | Try services in order, stop on first success |
| round-robin | Rotate through services across calls |
| direct | Send via first service only |
const sender = createSender(urls, { strategy: 'fallback' });Service Details
Slack
// Webhook
await send('slack://hook:T00000/B00000/XXXXXXX@webhook', 'Hello!');
// Bot API
await send('slack://xoxb:xoxb-your-token@C12345678', 'Hello!');Discord
await send('discord://webhook-token@webhook-id', 'Hello Discord!');Telegram
// Multiple chats, HTML parse mode
await send(
'telegram://123456:ABC-DEF@telegram?chats=-100123,-100456&parsemode=HTML',
'<b>Bold alert!</b>'
);Email (SMTP)
// TLS on port 465
await send(
'smtp://user:[email protected]:[email protected]&subject=Alert',
'Email body here'
);Generic Webhook
// JSON template with custom headers
await send(
'generic+https://api.example.com/notify?template=json&@Authorization=Bearer%20token',
'Hello webhook!',
{ title: 'Alert' }
);
// Raw text
await send('generic+https://example.com/hook', 'plain text body');Ntfy
// Public ntfy.sh
await send('ntfy://my-topic', 'Hello!', {
title: 'Server Alert',
priority: '5',
tags: 'warning,server',
});
// Self-hosted
await send('ntfy://[email protected]', 'Hello!');Custom Services
import { registerService, send } from '@toanalien/ahha';
registerService({
scheme: 'my-service',
create(config) {
return {
async send(message, params) {
// Your notification logic here
await fetch(`https://my-api.com/notify`, {
method: 'POST',
body: JSON.stringify({ text: message }),
});
return { success: true, service: 'my-service', timestamp: new Date() };
},
};
},
});
await send('my-service://config', 'Hello custom!');API Reference
send(url, message, params?)
Send a notification via a single service URL. Returns Promise<SendResult>.
createSender(urls, options?)
Create a reusable sender for multiple URLs. Returns Sender.
Options:
strategy:'broadcast'|'fallback'|'round-robin'|'direct'(default:'broadcast')
Sender.send(message, params?)
Send via all configured services. Returns Promise<SendResult[]>.
registerService(definition)
Register a custom service. definition must have scheme and create(config).
listServices()
Returns array of registered service scheme names.
parseUrl(url)
Parse a notification URL into components. Returns ParsedUrl.
Types
interface SendResult {
success: boolean;
service: string;
timestamp: Date;
error?: Error;
}
type Params = Record<string, string>;License
MIT
