iris-relay 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # iris-relay
2
2
 
3
- Lightweight, zero-dependency Telegram message relay. Tree-shakeable ESM package with built-in `.env` loading — true plug-and-play.
3
+ Lightweight Telegram relay dev tool — crash alerts, deploy notifications, heartbeats, and more. Zero-config, tree-shakeable, built-in `.env` loading.
4
4
 
5
5
  ## Install
6
6
 
@@ -15,17 +15,16 @@ Create a `.env` file in your project root:
15
15
  ```env
16
16
  XERO_BOT_TOKEN=your_bot_token_from_botfather
17
17
  XERO_CHAT_ID=your_telegram_chat_id
18
+ XERO_DRY_RUN=false
18
19
  ```
19
20
 
20
- > **How to get these:**
21
- > - **Bot Token** — message [@BotFather](https://t.me/BotFather) on Telegram to create a bot
22
- > - **Chat ID** — message [@userinfobot](https://t.me/userinfobot) on Telegram to get your chat ID
21
+ > **Bot Token** [@BotFather](https://t.me/BotFather) · **Chat ID** → [@userinfobot](https://t.me/userinfobot)
23
22
 
24
- That's it. No dotenv import needed — `iris-relay` auto-loads your `.env` file.
23
+ No dotenv import needed — `iris-relay` auto-loads your `.env`.
25
24
 
26
- ## Usage
25
+ ---
27
26
 
28
- ### One-off message
27
+ ## Quick Start
29
28
 
30
29
  ```ts
31
30
  import { relay } from "iris-relay";
@@ -33,52 +32,250 @@ import { relay } from "iris-relay";
33
32
  await relay("Hello from my server! 🚀");
34
33
  ```
35
34
 
36
- ### Pre-configured sender
35
+ ---
36
+
37
+ ## Features
38
+
39
+ ### 📨 Core Relay
37
40
 
38
41
  ```ts
39
- import { createRelay } from "iris-relay";
42
+ import { relay, createRelay } from "iris-relay";
43
+
44
+ // One-off message
45
+ await relay("Server started");
40
46
 
41
- const send = createRelay({
42
- botToken: process.env.XERO_BOT_TOKEN,
43
- chatId: process.env.XERO_CHAT_ID,
47
+ // With options
48
+ await relay("Check this out", {
49
+ parseMode: "HTML",
50
+ disablePreview: true,
51
+ silent: true,
52
+ retries: 3, // retry with exponential backoff
53
+ retryDelay: 1000, // base delay in ms
44
54
  });
45
55
 
56
+ // Pre-configured sender
57
+ const send = createRelay({ botToken: "...", chatId: "..." });
46
58
  await send("Deploy successful ✅");
47
- await send("Build failed ❌");
48
59
  ```
49
60
 
50
- ### Options
61
+ ### 🚨 Error Relay
51
62
 
52
63
  ```ts
53
- await relay("Check this out", {
54
- parseMode: "HTML", // HTML | Markdown | MarkdownV2
55
- disablePreview: true, // no link previews
56
- silent: true, // no notification sound
64
+ import { relayError } from "iris-relay";
65
+
66
+ try {
67
+ dangerousOperation();
68
+ } catch (err) {
69
+ await relayError(err); // sends formatted stack trace
70
+ }
71
+ ```
72
+
73
+ ### 📋 JSON Relay
74
+
75
+ ```ts
76
+ import { relayJSON } from "iris-relay";
77
+
78
+ await relayJSON({ users: 42, status: "healthy" }, "Server Stats");
79
+ ```
80
+
81
+ ### 🚀 Deploy Notifications
82
+
83
+ Sends a formatted deploy summary to Telegram. **Git info is auto-detected** — no config needed if you're in a git repo.
84
+
85
+ ```ts
86
+ import { relayDeploy } from "iris-relay";
87
+
88
+ // Minimal — auto-reads git branch, commit hash, and commit message
89
+ await relayDeploy();
90
+
91
+ // With app info
92
+ await relayDeploy({ app: "my-api", env: "production", version: "1.2.3" });
93
+
94
+ // Full example
95
+ await relayDeploy({
96
+ app: "my-api",
97
+ env: "production",
98
+ version: "1.2.3",
99
+ by: "CI/CD Pipeline",
100
+ extra: { region: "us-east-1", duration: "45s" },
101
+ });
102
+ ```
103
+
104
+ **Example Telegram output:**
105
+ ```
106
+ 🚀 Deploy Notification
107
+
108
+ 📦 App: my-api
109
+ 🌍 Env: production
110
+ 🏷️ Version: 1.2.3
111
+ 🌿 Branch: main
112
+ 📝 Commit: a1b2c3d — fix: resolve login bug
113
+ 👤 By: CI/CD Pipeline
114
+ ⏰ Time: 2026-03-08T12:00:00.000Z
115
+ • region: us-east-1
116
+ • duration: 45s
117
+ ```
118
+
119
+ **How auto-detection works:**
120
+
121
+ | Field | Git command | Example output |
122
+ |-------|-------------|---------------|
123
+ | `branch` | `git rev-parse --abbrev-ref HEAD` | `main` |
124
+ | `commit` | `git rev-parse --short HEAD` | `a1b2c3d` |
125
+ | Commit message | `git log -1 --pretty=%s` | `fix: resolve login bug` |
126
+
127
+ > All auto-detected values can be overridden by passing them in the `meta` object. If git is not installed or you're not in a repo, those fields are silently omitted.
128
+
129
+ **CI/CD example (GitHub Actions):**
130
+ ```ts
131
+ await relayDeploy({
132
+ app: "my-api",
133
+ env: "production",
134
+ version: process.env.npm_package_version,
135
+ by: `GitHub Actions (${process.env.GITHUB_ACTOR})`,
136
+ commit: process.env.GITHUB_SHA?.slice(0, 7),
137
+ branch: process.env.GITHUB_REF_NAME,
138
+ });
139
+ ```
140
+
141
+ ### ✍️ Message Builder
142
+
143
+ ```ts
144
+ import { message } from "iris-relay";
145
+
146
+ await message()
147
+ .bold("Deploy")
148
+ .text(" ")
149
+ .code("v1.2.3")
150
+ .text(" to ")
151
+ .italic("production")
152
+ .br()
153
+ .separator()
154
+ .link("View Dashboard", "https://example.com")
155
+ .send();
156
+ ```
157
+
158
+ Builder methods: `.bold()` `.italic()` `.code()` `.codeBlock()` `.strike()` `.underline()` `.link()` `.text()` `.br()` `.separator()`
159
+
160
+ ### 📎 File Relay
161
+
162
+ ```ts
163
+ import { relayFile } from "iris-relay";
164
+
165
+ await relayFile("./logs/error.log", "Latest error log");
166
+ ```
167
+
168
+ ### 💀 Process Crash Watcher
169
+
170
+ ```ts
171
+ import { watchProcess } from "iris-relay";
172
+
173
+ watchProcess(); // done — uncaught exceptions & rejections get reported
174
+ ```
175
+
176
+ ### 💓 Heartbeat
177
+
178
+ ```ts
179
+ import { startHeartbeat } from "iris-relay";
180
+
181
+ const stop = startHeartbeat({
182
+ interval: 60_000, // every minute
183
+ app: "my-api",
57
184
  });
185
+
186
+ // Later: stop();
187
+ ```
188
+
189
+ ### 🔌 Express Middleware
190
+
191
+ ```ts
192
+ import express from "express";
193
+ import { irisMiddleware, irisErrorHandler } from "iris-relay";
194
+
195
+ const app = express();
196
+
197
+ // Reports slow requests (>3s) and 5xx errors
198
+ app.use(irisMiddleware({ slowThreshold: 3000 }));
199
+
200
+ // Your routes...
201
+
202
+ // Catches unhandled errors and sends to Telegram
203
+ app.use(irisErrorHandler());
204
+ ```
205
+
206
+ ### 📡 Multi-Channel
207
+
208
+ ```ts
209
+ import { createChannels } from "iris-relay";
210
+
211
+ const channels = createChannels([
212
+ { name: "alerts", chatId: "-100123456" },
213
+ { name: "deploys", chatId: "-100789012", silent: true },
214
+ { name: "logs", chatId: "-100345678" },
215
+ ]);
216
+
217
+ await channels.send("alerts", "🚨 Server is down!");
218
+ await channels.send("deploys", "🚀 v1.2.3 deployed");
219
+ await channels.broadcast("System maintenance in 5 minutes");
58
220
  ```
59
221
 
60
- ## API
222
+ ### 🧪 Dry Run Mode
223
+
224
+ Set `XERO_DRY_RUN=true` in `.env` to log messages to console instead of sending to Telegram. Perfect for local development.
225
+
226
+ ---
227
+
228
+ ## CLI
229
+
230
+ ```bash
231
+ # Send a message
232
+ npx iris-relay "Deploy complete ✅"
233
+
234
+ # Send with HTML
235
+ npx iris-relay --html "<b>Bold</b> message"
236
+
237
+ # Send a file
238
+ npx iris-relay --file ./logs/error.log "Error log attached"
239
+
240
+ # Silent (no notification sound)
241
+ npx iris-relay --silent "Background update"
242
+ ```
61
243
 
62
- ### `relay(message, config?): Promise<RelayResult>`
244
+ ---
63
245
 
64
- Send a single message. Returns `{ success: boolean, messageId?: number, error?: string }`.
246
+ ## API Reference
65
247
 
66
- | Param | Type | Description |
67
- |-------|------|-------------|
68
- | `message` | `string` | The message text to send |
69
- | `config.botToken` | `string?` | Bot token (defaults to `XERO_BOT_TOKEN` env var) |
70
- | `config.chatId` | `string?` | Chat ID (defaults to `XERO_CHAT_ID` env var) |
71
- | `config.parseMode` | `"HTML" \| "Markdown" \| "MarkdownV2"` | Message formatting |
72
- | `config.disablePreview` | `boolean?` | Disable link previews |
73
- | `config.silent` | `boolean?` | Send without notification sound |
248
+ | Export | Description |
249
+ |--------|-------------|
250
+ | `relay(msg, config?)` | Send a text message |
251
+ | `createRelay(config)` | Create a pre-configured sender |
252
+ | `relayError(err, config?)` | Send formatted error + stack trace |
253
+ | `relayJSON(obj, label?, config?)` | Send pretty-printed JSON |
254
+ | `relayDeploy(meta?, config?)` | Send deploy notification with git info |
255
+ | `relayFile(path, caption?, config?)` | Send a file |
256
+ | `message()` | Create a fluent message builder |
257
+ | `watchProcess(config?)` | Auto-report crashes |
258
+ | `startHeartbeat(opts?, config?)` | Periodic alive pings |
259
+ | `irisMiddleware(opts?)` | Express middleware for slow/error reporting |
260
+ | `irisErrorHandler(config?)` | Express error handler |
261
+ | `createChannels(channels, config?)` | Multi-channel manager |
262
+ | `isDryRun()` | Check if dry run mode is active |
74
263
 
75
- ### `createRelay(config): (message, overrides?) => Promise<RelayResult>`
264
+ ## Config
76
265
 
77
- Create a pre-configured sender function. Useful when sending multiple messages with the same config.
266
+ | Option | Type | Default | Description |
267
+ |--------|------|---------|-------------|
268
+ | `botToken` | `string` | `XERO_BOT_TOKEN` | Bot token |
269
+ | `chatId` | `string` | `XERO_CHAT_ID` | Chat ID |
270
+ | `parseMode` | `"HTML" \| "Markdown" \| "MarkdownV2"` | — | Message format |
271
+ | `disablePreview` | `boolean` | `false` | Disable link previews |
272
+ | `silent` | `boolean` | `false` | No notification sound |
273
+ | `retries` | `number` | `0` | Retry count |
274
+ | `retryDelay` | `number` | `1000` | Base retry delay (ms) |
78
275
 
79
276
  ## Requirements
80
277
 
81
- - Node.js ≥ 18 (uses native `fetch`)
278
+ - Node.js ≥ 18
82
279
 
83
280
  ## License
84
281
 
@@ -0,0 +1,50 @@
1
+ import type { RelayConfig, RelayResult } from "./types.js";
2
+ /**
3
+ * Fluent message builder for composing formatted Telegram messages.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { message } from "iris-relay";
8
+ *
9
+ * await message()
10
+ * .bold("Deploy")
11
+ * .text(" ")
12
+ * .code("v1.2.3")
13
+ * .text(" to ")
14
+ * .italic("production")
15
+ * .send();
16
+ * ```
17
+ */
18
+ export declare class MessageBuilder {
19
+ private parts;
20
+ private config;
21
+ /** Add plain text */
22
+ text(content: string): this;
23
+ /** Add bold text */
24
+ bold(content: string): this;
25
+ /** Add italic text */
26
+ italic(content: string): this;
27
+ /** Add inline code */
28
+ code(content: string): this;
29
+ /** Add a code block with optional language */
30
+ codeBlock(content: string, lang?: string): this;
31
+ /** Add a strikethrough text */
32
+ strike(content: string): this;
33
+ /** Add underlined text */
34
+ underline(content: string): this;
35
+ /** Add a link */
36
+ link(text: string, url: string): this;
37
+ /** Add a newline */
38
+ br(): this;
39
+ /** Add a horizontal separator */
40
+ separator(): this;
41
+ /** Override relay config for this message */
42
+ withConfig(config: RelayConfig): this;
43
+ /** Build the message string without sending */
44
+ build(): string;
45
+ /** Send the composed message */
46
+ send(config?: RelayConfig): Promise<RelayResult>;
47
+ }
48
+ /** Create a new message builder */
49
+ export declare function message(): MessageBuilder;
50
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAO3D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,MAAM,CAAsC;IAEpD,qBAAqB;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,oBAAoB;IACpB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,sBAAsB;IACtB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK7B,sBAAsB;IACtB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,8CAA8C;IAC9C,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAM/C,+BAA+B;IAC/B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK7B,0BAA0B;IAC1B,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKhC,iBAAiB;IACjB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKrC,oBAAoB;IACpB,EAAE,IAAI,IAAI;IAKV,iCAAiC;IACjC,SAAS,IAAI,IAAI;IAKjB,6CAA6C;IAC7C,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKrC,+CAA+C;IAC/C,KAAK,IAAI,MAAM;IAIf,gCAAgC;IAC1B,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;CAGzD;AAED,mCAAmC;AACnC,wBAAgB,OAAO,IAAI,cAAc,CAExC"}
@@ -0,0 +1,94 @@
1
+ import { relay } from "./relay.js";
2
+ /** Escape HTML special characters */
3
+ function esc(text) {
4
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5
+ }
6
+ /**
7
+ * Fluent message builder for composing formatted Telegram messages.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { message } from "iris-relay";
12
+ *
13
+ * await message()
14
+ * .bold("Deploy")
15
+ * .text(" ")
16
+ * .code("v1.2.3")
17
+ * .text(" to ")
18
+ * .italic("production")
19
+ * .send();
20
+ * ```
21
+ */
22
+ export class MessageBuilder {
23
+ parts = [];
24
+ config = { parseMode: "HTML" };
25
+ /** Add plain text */
26
+ text(content) {
27
+ this.parts.push(esc(content));
28
+ return this;
29
+ }
30
+ /** Add bold text */
31
+ bold(content) {
32
+ this.parts.push(`<b>${esc(content)}</b>`);
33
+ return this;
34
+ }
35
+ /** Add italic text */
36
+ italic(content) {
37
+ this.parts.push(`<i>${esc(content)}</i>`);
38
+ return this;
39
+ }
40
+ /** Add inline code */
41
+ code(content) {
42
+ this.parts.push(`<code>${esc(content)}</code>`);
43
+ return this;
44
+ }
45
+ /** Add a code block with optional language */
46
+ codeBlock(content, lang) {
47
+ const langAttr = lang ? ` class="language-${esc(lang)}"` : "";
48
+ this.parts.push(`<pre${langAttr}>${esc(content)}</pre>`);
49
+ return this;
50
+ }
51
+ /** Add a strikethrough text */
52
+ strike(content) {
53
+ this.parts.push(`<s>${esc(content)}</s>`);
54
+ return this;
55
+ }
56
+ /** Add underlined text */
57
+ underline(content) {
58
+ this.parts.push(`<u>${esc(content)}</u>`);
59
+ return this;
60
+ }
61
+ /** Add a link */
62
+ link(text, url) {
63
+ this.parts.push(`<a href="${esc(url)}">${esc(text)}</a>`);
64
+ return this;
65
+ }
66
+ /** Add a newline */
67
+ br() {
68
+ this.parts.push("\n");
69
+ return this;
70
+ }
71
+ /** Add a horizontal separator */
72
+ separator() {
73
+ this.parts.push("\n———————————\n");
74
+ return this;
75
+ }
76
+ /** Override relay config for this message */
77
+ withConfig(config) {
78
+ this.config = { ...this.config, ...config };
79
+ return this;
80
+ }
81
+ /** Build the message string without sending */
82
+ build() {
83
+ return this.parts.join("");
84
+ }
85
+ /** Send the composed message */
86
+ async send(config) {
87
+ return relay(this.build(), { ...this.config, ...config });
88
+ }
89
+ }
90
+ /** Create a new message builder */
91
+ export function message() {
92
+ return new MessageBuilder();
93
+ }
94
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Channel, RelayConfig, RelayResult } from "./types.js";
2
+ /**
3
+ * Multi-channel manager for sending to different Telegram chats.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { createChannels } from "iris-relay";
8
+ *
9
+ * const channels = createChannels([
10
+ * { name: "alerts", chatId: "-100123456" },
11
+ * { name: "deploys", chatId: "-100789012", silent: true },
12
+ * { name: "logs", chatId: "-100345678" },
13
+ * ]);
14
+ *
15
+ * await channels.send("alerts", "🚨 Server is down!");
16
+ * await channels.send("deploys", "🚀 v1.2.3 deployed");
17
+ * await channels.broadcast("System maintenance in 5 minutes");
18
+ * ```
19
+ */
20
+ export declare function createChannels(channels: Channel[], defaultConfig?: RelayConfig): {
21
+ /** Send a message to a specific channel */
22
+ send(channelName: string, message: string, config?: RelayConfig): Promise<RelayResult>;
23
+ /** Broadcast a message to all channels */
24
+ broadcast(message: string, config?: RelayConfig): Promise<Map<string, RelayResult>>;
25
+ /** Get list of registered channel names */
26
+ list(): string[];
27
+ };
28
+ //# sourceMappingURL=channels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../src/channels.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,aAAa,CAAC,EAAE,WAAW;IASvE,2CAA2C;sBACnB,MAAM,WAAW,MAAM,WAAW,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAgB5F,0CAA0C;uBACjB,MAAM,WAAW,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAUzF,2CAA2C;YACnC,MAAM,EAAE;EAIvB"}
@@ -0,0 +1,56 @@
1
+ import { relay, resolveConfig } from "./relay.js";
2
+ /**
3
+ * Multi-channel manager for sending to different Telegram chats.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { createChannels } from "iris-relay";
8
+ *
9
+ * const channels = createChannels([
10
+ * { name: "alerts", chatId: "-100123456" },
11
+ * { name: "deploys", chatId: "-100789012", silent: true },
12
+ * { name: "logs", chatId: "-100345678" },
13
+ * ]);
14
+ *
15
+ * await channels.send("alerts", "🚨 Server is down!");
16
+ * await channels.send("deploys", "🚀 v1.2.3 deployed");
17
+ * await channels.broadcast("System maintenance in 5 minutes");
18
+ * ```
19
+ */
20
+ export function createChannels(channels, defaultConfig) {
21
+ const resolved = resolveConfig(defaultConfig);
22
+ const channelMap = new Map();
23
+ for (const ch of channels) {
24
+ channelMap.set(ch.name, ch);
25
+ }
26
+ return {
27
+ /** Send a message to a specific channel */
28
+ async send(channelName, message, config) {
29
+ const channel = channelMap.get(channelName);
30
+ if (!channel) {
31
+ return { success: false, error: `Channel "${channelName}" not found` };
32
+ }
33
+ return relay(message, {
34
+ ...resolved,
35
+ botToken: channel.botToken ?? resolved.botToken,
36
+ chatId: channel.chatId,
37
+ parseMode: channel.parseMode ?? resolved.parseMode,
38
+ silent: channel.silent ?? resolved.silent,
39
+ ...config,
40
+ });
41
+ },
42
+ /** Broadcast a message to all channels */
43
+ async broadcast(message, config) {
44
+ const results = new Map();
45
+ for (const [name] of channelMap) {
46
+ results.set(name, await this.send(name, message, config));
47
+ }
48
+ return results;
49
+ },
50
+ /** Get list of registered channel names */
51
+ list() {
52
+ return Array.from(channelMap.keys());
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.js","sourceRoot":"","sources":["../src/channels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGlD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAAC,QAAmB,EAAE,aAA2B;IAC3E,MAAM,QAAQ,GAAG,aAAa,CAAC,aAAa,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE9C,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QACxB,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,OAAO;QACH,2CAA2C;QAC3C,KAAK,CAAC,IAAI,CAAC,WAAmB,EAAE,OAAe,EAAE,MAAoB;YACjE,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,WAAW,aAAa,EAAE,CAAC;YAC3E,CAAC;YAED,OAAO,KAAK,CAAC,OAAO,EAAE;gBAClB,GAAG,QAAQ;gBACX,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ;gBAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS;gBAClD,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM;gBACzC,GAAG,MAAM;aACZ,CAAC,CAAC;QACP,CAAC;QAED,0CAA0C;QAC1C,KAAK,CAAC,SAAS,CAAC,OAAe,EAAE,MAAoB;YACjD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;YAE/C,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;YAC9D,CAAC;YAED,OAAO,OAAO,CAAC;QACnB,CAAC;QAED,2CAA2C;QAC3C,IAAI;YACA,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;KACJ,CAAC;AACN,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ import { config } from "dotenv";
3
+ import { relay } from "./relay.js";
4
+ import { relayFile } from "./file.js";
5
+ config();
6
+ const args = process.argv.slice(2);
7
+ function printHelp() {
8
+ console.log(`
9
+ iris-relay — Send messages to Telegram from the CLI
10
+
11
+ Usage:
12
+ iris-relay "Your message here"
13
+ iris-relay --file ./path/to/file.log "Optional caption"
14
+ iris-relay --silent "No notification sound"
15
+ iris-relay --html "<b>Bold</b> message"
16
+
17
+ Options:
18
+ --file <path> Send a file instead of a text message
19
+ --silent Send without notification sound
20
+ --html Parse message as HTML
21
+ --markdown Parse message as Markdown
22
+ --help, -h Show this help message
23
+
24
+ Environment:
25
+ XERO_BOT_TOKEN Your Telegram bot token
26
+ XERO_CHAT_ID Your Telegram chat ID
27
+ `);
28
+ }
29
+ async function main() {
30
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
31
+ printHelp();
32
+ process.exit(0);
33
+ }
34
+ const silent = args.includes("--silent");
35
+ const html = args.includes("--html");
36
+ const markdown = args.includes("--markdown");
37
+ const fileIdx = args.indexOf("--file");
38
+ // Filter out flags to get the message
39
+ const messageArgs = args.filter((a, i) => !a.startsWith("--") && !(i === fileIdx + 1 && fileIdx >= 0));
40
+ const message = messageArgs.join(" ");
41
+ const parseMode = html ? "HTML" : markdown ? "Markdown" : undefined;
42
+ try {
43
+ // File mode
44
+ if (fileIdx >= 0) {
45
+ const filePath = args[fileIdx + 1];
46
+ if (!filePath) {
47
+ console.error("Error: --file requires a path argument");
48
+ process.exit(1);
49
+ }
50
+ const result = await relayFile(filePath, message || undefined, {
51
+ silent,
52
+ parseMode,
53
+ });
54
+ if (result.success) {
55
+ console.log(`✅ File sent: ${filePath}`);
56
+ }
57
+ else {
58
+ console.error(`❌ Failed: ${result.error}`);
59
+ process.exit(1);
60
+ }
61
+ return;
62
+ }
63
+ // Message mode
64
+ if (!message) {
65
+ console.error("Error: No message provided");
66
+ printHelp();
67
+ process.exit(1);
68
+ }
69
+ const result = await relay(message, { silent, parseMode });
70
+ if (result.success) {
71
+ console.log(`✅ Message sent (ID: ${result.messageId})`);
72
+ }
73
+ else {
74
+ console.error(`❌ Failed: ${result.error}`);
75
+ process.exit(1);
76
+ }
77
+ }
78
+ catch (err) {
79
+ console.error(`💥 ${err instanceof Error ? err.message : String(err)}`);
80
+ process.exit(1);
81
+ }
82
+ }
83
+ main();
84
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/file.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { RelayConfig, RelayResult } from "./types.js";
2
+ /**
3
+ * Send a file to your Telegram chat via sendDocument API.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { relayFile } from "iris-relay";
8
+ * await relayFile("./logs/error.log", "Latest error log");
9
+ * ```
10
+ */
11
+ export declare function relayFile(filePath: string, caption?: string, config?: RelayConfig): Promise<RelayResult>;
12
+ //# sourceMappingURL=file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAoB,MAAM,YAAY,CAAC;AAO7E;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,WAAW,CAAC,CAgDtB"}