fix: update all Telegram paths to use clawdis branding
- Fixed normalizeAllowFromEntry to properly handle telegram: prefix - Removed unused telegram.allowFrom config field from schema - Updated all documentation to use ~/.clawdis paths with legacy fallback notes - Fixed selectProviders to include Twilio support - Added provider disconnect in agent delivery - Improved media type detection - Made Telegram inbound media save buffer to disk for Claude access All Telegram identifiers now require telegram: prefix in shared inbound.allowFrom config.
This commit is contained in:
parent
6e9f615a95
commit
4ac19117c1
@ -9,7 +9,7 @@ This document provides architectural guidance for adding Telegram as a third mes
|
||||
1. **MTProto client**: Users log in with their personal Telegram account (phone + 2FA)
|
||||
2. **Same security model**: `allowFrom` whitelist controls who can trigger auto-replies
|
||||
3. **Provider abstraction**: Unified interface across Twilio, Web, and Telegram providers
|
||||
4. **Session storage**: File-based session like WhatsApp Web (`~/.warelay/telegram/session/`)
|
||||
4. **Session storage**: File-based session like WhatsApp Web (`~/.clawdis/telegram/session/`)
|
||||
|
||||
---
|
||||
|
||||
@ -32,7 +32,7 @@ This document provides architectural guidance for adding Telegram as a third mes
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Warelay["warelay CLI (Container)"]
|
||||
subgraph Warelay["clawdis CLI (Container)"]
|
||||
CLI[[CLI / Commander]]
|
||||
Send[[Send Command]]
|
||||
Relay[[Relay Command]]
|
||||
@ -57,7 +57,7 @@ flowchart LR
|
||||
WhatsApp -->|Baileys| IDP
|
||||
```
|
||||
|
||||
**Caption:** warelay system context showing CLI entry points and provider connections.
|
||||
**Caption:** clawdis system context showing CLI entry points and provider connections.
|
||||
|
||||
**Evidence:** `src/cli/program.ts:L1-L50`, `src/commands/send.ts:L1-L150`
|
||||
|
||||
@ -156,7 +156,7 @@ export async function pickProvider(pref: Provider | "auto"): Promise<Provider> {
|
||||
| **Auth Model** | QR code scan | Phone + code + 2FA |
|
||||
| **Connection Type** | Persistent WebSocket | Persistent TCP/WebSocket |
|
||||
| **Library** | Baileys | GramJS |
|
||||
| **Session Storage** | `~/.warelay/credentials/` | `~/.warelay/telegram/session/` |
|
||||
| **Session Storage** | `~/.clawdis/credentials/` | `~/.clawdis/telegram/session/` |
|
||||
| **Message Send** | Socket message | MTProto request |
|
||||
| **Inbound Handling** | Event listener | Event listener |
|
||||
| **Delivery Status** | Limited (receipts) | Full (receipts + read) |
|
||||
@ -189,7 +189,7 @@ sequenceDiagram
|
||||
|
||||
**Key characteristics:**
|
||||
- Persistent connection with session state
|
||||
- QR-based authentication stored at `~/.warelay/credentials/`
|
||||
- QR-based authentication stored at `~/.clawdis/credentials/`
|
||||
- Media sent as buffers with automatic optimization
|
||||
- IPC server for relay mode to prevent session corruption
|
||||
|
||||
@ -216,7 +216,7 @@ sequenceDiagram
|
||||
|
||||
**Key characteristics:**
|
||||
- Persistent connection with session state (like WhatsApp Web)
|
||||
- Phone-based authentication stored at `~/.warelay/telegram/session/`
|
||||
- Phone-based authentication stored at `~/.clawdis/telegram/session/`
|
||||
- Media sent as buffers
|
||||
- Same patterns as Baileys for session management
|
||||
|
||||
@ -276,7 +276,7 @@ flowchart TB
|
||||
end
|
||||
|
||||
subgraph Storage["Local Storage"]
|
||||
SessionFile[("~/.warelay/telegram/session/")]
|
||||
SessionFile[("~/.clawdis/telegram/session/")]
|
||||
end
|
||||
|
||||
Login -->|phone + code| Client
|
||||
@ -640,7 +640,7 @@ TELEGRAM_API_ID=12345678
|
||||
TELEGRAM_API_HASH=0123456789abcdef0123456789abcdef
|
||||
```
|
||||
|
||||
### 5.2 Extended warelay.json Schema
|
||||
### 5.2 Extended clawdis.json Schema
|
||||
|
||||
```typescript
|
||||
// Extended WarelayConfig type
|
||||
@ -668,13 +668,13 @@ export type WarelayConfig = {
|
||||
};
|
||||
```
|
||||
|
||||
### 5.3 Sample warelay.json with Telegram
|
||||
### 5.3 Sample clawdis.json with Telegram
|
||||
|
||||
```json5
|
||||
{
|
||||
"logging": {
|
||||
"level": "info",
|
||||
"file": "~/.warelay/logs/warelay.log"
|
||||
"file": "~/.clawdis/logs/clawdis.log"
|
||||
},
|
||||
|
||||
// Telegram-specific settings
|
||||
@ -979,28 +979,14 @@ warelay relay --provider telegram --verbose
|
||||
|
||||
**Context:** Need to store Telegram session like WhatsApp Web credentials.
|
||||
|
||||
**Decision:** Store at `~/.warelay/telegram/session/` following existing patterns.
|
||||
**Decision:** Store at `~/.clawdis/telegram/session/` following existing patterns.
|
||||
|
||||
**Consequences:**
|
||||
- Consistent with `~/.warelay/credentials/` for WhatsApp
|
||||
- Consistent with `~/.clawdis/credentials/` for WhatsApp
|
||||
- User-specific storage
|
||||
- Easy to backup/restore
|
||||
- Clear separation between providers
|
||||
|
||||
### ADR-004: Security Model Consistency
|
||||
|
||||
**Status:** Accepted
|
||||
|
||||
**Context:** WhatsApp uses `allowFrom` whitelist. Telegram needs same model.
|
||||
|
||||
**Decision:** Use `telegram.allowFrom` in config with usernames and user IDs.
|
||||
|
||||
**Consequences:**
|
||||
- Consistent security model across providers
|
||||
- Users understand the pattern already
|
||||
- Supports both `@username` and numeric IDs
|
||||
- Same behavior: whitelist = only listed users trigger auto-reply
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: File Evidence Index
|
||||
|
||||
@ -33,24 +33,26 @@ You'll be prompted for:
|
||||
2. **SMS verification code** (sent to your Telegram app or SMS)
|
||||
3. **2FA password** (if you have two-factor authentication enabled)
|
||||
|
||||
Session is saved to `~/.warelay/telegram/session/` and persists across restarts.
|
||||
Session is saved to `~/.clawdis/telegram/session/` (or `~/.warelay/telegram/session/` for legacy compatibility) and persists across restarts.
|
||||
|
||||
### 4. Configure Whitelist (Optional)
|
||||
|
||||
In `~/.warelay/warelay.json`:
|
||||
In `~/.clawdis/clawdis.json` (or `~/.warelay/warelay.json` for legacy):
|
||||
```json5
|
||||
{
|
||||
telegram: {
|
||||
// Only these users can trigger auto-replies
|
||||
inbound: {
|
||||
// Only these users can trigger auto-replies (works for both Telegram and WhatsApp)
|
||||
allowFrom: [
|
||||
"@username", // Telegram username (with @)
|
||||
"+1234567890", // Phone number (with +)
|
||||
"123456789" // User ID (numeric)
|
||||
"telegram:@username", // Telegram username (with telegram: prefix)
|
||||
"telegram:123456789", // Telegram user ID (numeric)
|
||||
"+1234567890" // WhatsApp phone number (E.164 format)
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Telegram identifiers in `allowFrom` should use the `telegram:` prefix (e.g., `telegram:@alice`). WhatsApp uses E.164 phone numbers (e.g., `+1234567890`).
|
||||
|
||||
**Security note:** If `allowFrom` is empty or omitted, all incoming messages will trigger auto-replies. Use a whitelist in production.
|
||||
|
||||
## CLI Usage
|
||||
@ -113,7 +115,7 @@ Shows recent sent/received messages with delivery status.
|
||||
warelay logout --provider telegram
|
||||
```
|
||||
|
||||
Removes the saved session from `~/.warelay/telegram/session/`.
|
||||
Removes the saved session from `~/.clawdis/telegram/session/` (or `~/.warelay/telegram/session/` for legacy).
|
||||
|
||||
## Features
|
||||
|
||||
@ -150,21 +152,22 @@ Control who can trigger auto-replies via `allowFrom` config:
|
||||
|
||||
```json5
|
||||
{
|
||||
telegram: {
|
||||
allowFrom: ["@alice", "@bob", "123456789"]
|
||||
inbound: {
|
||||
allowFrom: ["telegram:@alice", "telegram:@bob", "telegram:123456789"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Username** (`@alice`): Match by Telegram username
|
||||
- **Phone number** (`+15551234567`): Match by phone number
|
||||
- **User ID** (`123456789`): Match by numeric Telegram user ID
|
||||
- **Username** (`telegram:@alice`): Match by Telegram username (requires `telegram:` prefix)
|
||||
- **User ID** (`telegram:123456789`): Match by numeric Telegram user ID (requires `telegram:` prefix)
|
||||
|
||||
**Note:** The `telegram:` prefix is required for Telegram identifiers in the shared `inbound.allowFrom` config to distinguish them from WhatsApp phone numbers.
|
||||
|
||||
If `allowFrom` is empty or omitted, **all messages trigger auto-replies** (use with caution).
|
||||
|
||||
### Session Storage
|
||||
|
||||
Session files are stored encrypted at `~/.warelay/telegram/session/`:
|
||||
Session files are stored encrypted at `~/.clawdis/telegram/session/` (or `~/.warelay/telegram/session/` for legacy):
|
||||
- Contains authentication tokens and keys
|
||||
- Persists across restarts
|
||||
- Should be treated as sensitive (equivalent to login credentials)
|
||||
@ -236,7 +239,7 @@ warelay login --provider telegram
|
||||
**Solution:**
|
||||
```bash
|
||||
# Remove corrupted session
|
||||
rm -rf ~/.warelay/telegram/session/
|
||||
rm -rf ~/.clawdis/telegram/session/
|
||||
|
||||
# Re-login
|
||||
warelay login --provider telegram
|
||||
@ -248,10 +251,8 @@ warelay login --provider telegram
|
||||
|
||||
```json5
|
||||
{
|
||||
telegram: {
|
||||
allowFrom: ["@alice", "@bob"]
|
||||
},
|
||||
inbound: {
|
||||
allowFrom: ["telegram:@alice", "telegram:@bob"],
|
||||
reply: {
|
||||
mode: "text",
|
||||
text: "Thanks for your message! I'll get back to you soon."
|
||||
@ -264,10 +265,8 @@ warelay login --provider telegram
|
||||
|
||||
```json5
|
||||
{
|
||||
telegram: {
|
||||
allowFrom: ["@alice", "+15551234567"]
|
||||
},
|
||||
inbound: {
|
||||
allowFrom: ["telegram:@alice", "+15551234567"],
|
||||
reply: {
|
||||
mode: "command",
|
||||
bodyPrefix: "You are a helpful assistant on Telegram. Be concise.\n\n",
|
||||
@ -287,10 +286,8 @@ warelay login --provider telegram
|
||||
|
||||
```json5
|
||||
{
|
||||
telegram: {
|
||||
allowFrom: ["@alice", "@bob", "@charlie"]
|
||||
},
|
||||
inbound: {
|
||||
allowFrom: ["telegram:@alice", "telegram:@bob", "telegram:@charlie"],
|
||||
reply: {
|
||||
mode: "command",
|
||||
command: ["claude", "{{BodyStripped}}"],
|
||||
@ -347,10 +344,10 @@ Telegram has rate limits for personal accounts:
|
||||
Session files contain authentication tokens:
|
||||
```bash
|
||||
# Backup
|
||||
cp -r ~/.warelay/telegram/session/ ~/backups/warelay-telegram-session/
|
||||
cp -r ~/.clawdis/telegram/session/ ~/backups/clawdis-telegram-session/
|
||||
|
||||
# Restore
|
||||
cp -r ~/backups/warelay-telegram-session/ ~/.warelay/telegram/session/
|
||||
cp -r ~/backups/clawdis-telegram-session/ ~/.clawdis/telegram/session/
|
||||
```
|
||||
|
||||
### 4. Monitor Logs
|
||||
@ -391,14 +388,7 @@ tmux new -s warelay-telegram -d "warelay relay --provider telegram --verbose"
|
||||
|
||||
### Custom Session Storage
|
||||
|
||||
Override session path in config:
|
||||
```json5
|
||||
{
|
||||
telegram: {
|
||||
sessionPath: "/custom/path/to/session/"
|
||||
}
|
||||
}
|
||||
```
|
||||
Session storage location is currently fixed at `~/.warelay/telegram/session/` (legacy path) or `~/.clawdis/telegram/session/` (new path). Custom session paths via config are not yet supported.
|
||||
|
||||
### Verbose Output
|
||||
|
||||
@ -428,7 +418,7 @@ Output includes:
|
||||
|
||||
Media downloads use streaming to temporary files, eliminating memory buffering:
|
||||
|
||||
- Files downloaded to `~/.warelay/telegram-temp`
|
||||
- Files downloaded to `~/.clawdis/telegram-temp`
|
||||
- No memory spike regardless of file size
|
||||
- Automatic cleanup after send (success or failure)
|
||||
- Orphaned files cleaned on process restart (1 hour TTL)
|
||||
@ -491,7 +481,7 @@ If you encounter issues:
|
||||
1. **Check logs:** Run with `--verbose` flag
|
||||
2. **Verify credentials:** Ensure API ID/Hash are correct
|
||||
3. **Test login:** Try `warelay login --provider telegram` manually
|
||||
4. **Check session:** Verify `~/.warelay/telegram/session/` exists and is readable
|
||||
5. **Review config:** Ensure `~/.warelay/warelay.json` is valid JSON5
|
||||
4. **Check session:** Verify `~/.clawdis/telegram/session/` (or `~/.warelay/telegram/session/` for legacy) exists and is readable
|
||||
5. **Review config:** Ensure `~/.clawdis/clawdis.json` (or `~/.warelay/warelay.json` for legacy) is valid JSON5
|
||||
|
||||
For bugs or feature requests, file an issue on GitHub.
|
||||
|
||||
@ -27,11 +27,42 @@ import { ensureTwilioEnv } from "../env.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { pickProvider } from "../provider-web.js";
|
||||
import { createInitializedProvider } from "../providers/factory.js";
|
||||
import type { TelegramProviderConfig } from "../providers/base/types.js";
|
||||
import type { TelegramProviderConfig, ProviderMedia } from "../providers/base/types.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import type { Provider } from "../utils.js";
|
||||
import { sendViaIpc } from "../web/ipc.js";
|
||||
|
||||
/**
|
||||
* Detect media type from URL/file extension.
|
||||
* Falls back to 'image' for unknown types.
|
||||
*/
|
||||
function detectMediaType(url: string): ProviderMedia["type"] {
|
||||
const lower = url.toLowerCase();
|
||||
|
||||
// Video extensions
|
||||
if (lower.match(/\.(mp4|mov|avi|mkv|webm|m4v)($|\?)/)) {
|
||||
return "video";
|
||||
}
|
||||
|
||||
// Audio extensions
|
||||
if (lower.match(/\.(mp3|wav|ogg|m4a|aac|flac|opus)($|\?)/)) {
|
||||
return "audio";
|
||||
}
|
||||
|
||||
// Voice (specific audio formats)
|
||||
if (lower.match(/\.(ogg|opus)($|\?)/) && lower.includes("voice")) {
|
||||
return "voice";
|
||||
}
|
||||
|
||||
// Document extensions
|
||||
if (lower.match(/\.(pdf|doc|docx|txt|xls|xlsx|ppt|pptx|zip|rar)($|\?)/)) {
|
||||
return "document";
|
||||
}
|
||||
|
||||
// Image extensions (or default)
|
||||
return "image";
|
||||
}
|
||||
|
||||
type AgentCommandOpts = {
|
||||
message: string;
|
||||
to?: string;
|
||||
@ -413,21 +444,27 @@ export async function agentCommand(
|
||||
};
|
||||
const telegramProvider =
|
||||
await createInitializedProvider("telegram", telegramConfig);
|
||||
const chunks = chunkText(text, 4096);
|
||||
if (chunks.length > 0 || media.length > 0) {
|
||||
const firstChunk = chunks.length > 0 ? chunks[0] : "";
|
||||
const firstMedia = media[0];
|
||||
await telegramProvider.send(opts.to, firstChunk, {
|
||||
media: firstMedia ? [{ type: "image", url: firstMedia }] : undefined,
|
||||
});
|
||||
}
|
||||
for (let i = 1; i < chunks.length; i++) {
|
||||
await telegramProvider.send(opts.to, chunks[i]);
|
||||
}
|
||||
for (let i = 1; i < media.length; i++) {
|
||||
await telegramProvider.send(opts.to, "", {
|
||||
media: [{ type: "image", url: media[i] }],
|
||||
});
|
||||
|
||||
try {
|
||||
const chunks = chunkText(text, 4096);
|
||||
if (chunks.length > 0 || media.length > 0) {
|
||||
const firstChunk = chunks.length > 0 ? chunks[0] : "";
|
||||
const firstMedia = media[0];
|
||||
await telegramProvider.send(opts.to, firstChunk, {
|
||||
media: firstMedia ? [{ type: detectMediaType(firstMedia), url: firstMedia }] : undefined,
|
||||
});
|
||||
}
|
||||
for (let i = 1; i < chunks.length; i++) {
|
||||
await telegramProvider.send(opts.to, chunks[i]);
|
||||
}
|
||||
for (let i = 1; i < media.length; i++) {
|
||||
await telegramProvider.send(opts.to, "", {
|
||||
media: [{ type: detectMediaType(media[i]), url: media[i] }],
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
// Always disconnect to prevent connection leaks
|
||||
await telegramProvider.disconnect();
|
||||
}
|
||||
} else {
|
||||
const chunks = chunkText(text, 1600);
|
||||
|
||||
@ -233,11 +233,6 @@ const WarelaySchema = z.object({
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
telegram: z
|
||||
.object({
|
||||
allowFrom: z.array(z.string()).optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export function loadConfig(): WarelayConfig {
|
||||
|
||||
@ -42,7 +42,7 @@ export interface DownloadResult {
|
||||
|
||||
/**
|
||||
* Get temp directory for Telegram downloads.
|
||||
* Uses ~/.warelay/telegram-temp for consistency with media store.
|
||||
* Uses ~/.clawdis/telegram-temp for consistency with media store.
|
||||
*/
|
||||
export function getTelegramTempDir(): string {
|
||||
return TEMP_DIR;
|
||||
|
||||
@ -42,23 +42,38 @@ function formatTimestamp(ts: number, config?: ReturnType<typeof loadConfig>): st
|
||||
/**
|
||||
* Convert ProviderMessage to MsgContext for auto-reply system.
|
||||
*/
|
||||
function providerMessageToContext(
|
||||
async function providerMessageToContext(
|
||||
message: ProviderMessage,
|
||||
config?: ReturnType<typeof loadConfig>,
|
||||
): MsgContext {
|
||||
): Promise<MsgContext> {
|
||||
const timestampPrefix = formatTimestamp(message.timestamp, config);
|
||||
const bodyWithTimestamp = timestampPrefix
|
||||
? `${timestampPrefix}${message.body}`
|
||||
: message.body;
|
||||
|
||||
// Handle media: if buffer exists but no URL, save the buffer and create a file path
|
||||
let mediaUrl = message.media?.[0]?.url;
|
||||
let mediaPath = message.media?.[0]?.fileName;
|
||||
|
||||
if (!mediaUrl && message.media?.[0]?.buffer) {
|
||||
// Save Telegram media buffer to disk
|
||||
const { saveMediaBuffer } = await import("../media/store.js");
|
||||
const saved = await saveMediaBuffer(
|
||||
message.media[0].buffer,
|
||||
message.media[0].mimeType,
|
||||
"telegram",
|
||||
);
|
||||
mediaPath = saved.path;
|
||||
}
|
||||
|
||||
return {
|
||||
Body: bodyWithTimestamp,
|
||||
From: message.from,
|
||||
To: message.to,
|
||||
MessageSid: message.id,
|
||||
MediaUrl: message.media?.[0]?.url,
|
||||
MediaUrl: mediaUrl,
|
||||
MediaType: message.media?.[0]?.mimeType,
|
||||
MediaPath: message.media?.[0]?.fileName,
|
||||
MediaPath: mediaPath,
|
||||
};
|
||||
}
|
||||
|
||||
@ -144,7 +159,7 @@ async function handleInboundMessage(
|
||||
runtime: RuntimeEnv,
|
||||
): Promise<void> {
|
||||
const config = loadConfig();
|
||||
const ctx = providerMessageToContext(message, config);
|
||||
const ctx = await providerMessageToContext(message, config);
|
||||
|
||||
// Check allowFrom filter
|
||||
const allowFrom = config.inbound?.allowFrom;
|
||||
|
||||
@ -39,7 +39,7 @@ export async function sendTextMessage(
|
||||
*
|
||||
* Media sources:
|
||||
* - Buffer: Sent directly from memory (existing behavior)
|
||||
* - URL: Streamed to temp file (~/.warelay/telegram-temp), sent, then cleaned up
|
||||
* - URL: Streamed to temp file (~/.clawdis/telegram-temp), sent, then cleaned up
|
||||
*
|
||||
* Streaming eliminates OOM risk for large files by avoiding memory buffering.
|
||||
* Temp files are automatically cleaned up after send (success or failure).
|
||||
|
||||
15
src/utils.ts
15
src/utils.ts
@ -25,8 +25,19 @@ export function normalizeAllowFromEntry(
|
||||
if (!trimmed) return "";
|
||||
|
||||
if (provider === "telegram") {
|
||||
// Telegram uses @username format
|
||||
return trimmed.startsWith("@") ? trimmed : `@${trimmed}`;
|
||||
// Strip telegram: prefix if present (allowFrom entries may or may not have it)
|
||||
const withoutPrefix = trimmed.startsWith("telegram:")
|
||||
? trimmed.slice("telegram:".length)
|
||||
: trimmed;
|
||||
|
||||
// Ensure @username format, then add telegram: prefix
|
||||
const username = withoutPrefix.startsWith("@")
|
||||
? withoutPrefix
|
||||
: withoutPrefix.match(/^\d+$/)
|
||||
? withoutPrefix // numeric ID, keep as-is
|
||||
: `@${withoutPrefix}`;
|
||||
|
||||
return `telegram:${username}`;
|
||||
}
|
||||
|
||||
// WhatsApp (both web and twilio) use E.164 phone numbers
|
||||
|
||||
@ -232,6 +232,19 @@ export async function pickProvider(pref: Provider | "auto"): Promise<Provider> {
|
||||
* Select providers for multi-provider relay.
|
||||
* Validates authentication and filters out unavailable providers.
|
||||
*/
|
||||
/**
|
||||
* Check if Twilio is configured via environment variables.
|
||||
*/
|
||||
function isTwilioConfigured(): boolean {
|
||||
const required = ["TWILIO_ACCOUNT_SID", "TWILIO_WHATSAPP_FROM"];
|
||||
const hasRequired = required.every((k) => Boolean(process.env[k]));
|
||||
const hasToken = Boolean(process.env.TWILIO_AUTH_TOKEN);
|
||||
const hasKey = Boolean(
|
||||
process.env.TWILIO_API_KEY && process.env.TWILIO_API_SECRET,
|
||||
);
|
||||
return hasRequired && (hasToken || hasKey);
|
||||
}
|
||||
|
||||
export async function selectProviders(
|
||||
prefs: (Provider | "auto")[],
|
||||
): Promise<Provider[]> {
|
||||
@ -259,7 +272,14 @@ export async function selectProviders(
|
||||
);
|
||||
}
|
||||
|
||||
// Note: twilio not heavily used in this branch
|
||||
// Check twilio
|
||||
if (isTwilioConfigured()) {
|
||||
available.push("twilio");
|
||||
} else {
|
||||
skipped.push(
|
||||
"twilio (not configured - set TWILIO_ACCOUNT_SID, TWILIO_WHATSAPP_FROM, and auth credentials in .env)",
|
||||
);
|
||||
}
|
||||
|
||||
if (available.length > 0 && skipped.length > 0) {
|
||||
console.log(
|
||||
@ -295,7 +315,13 @@ export async function selectProviders(
|
||||
);
|
||||
}
|
||||
} else if (pref === "twilio") {
|
||||
skipped.push("twilio (not heavily used in this branch)");
|
||||
if (isTwilioConfigured()) {
|
||||
available.push("twilio");
|
||||
} else {
|
||||
skipped.push(
|
||||
"twilio (not configured - set TWILIO_ACCOUNT_SID, TWILIO_WHATSAPP_FROM, and auth credentials in .env)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user