Add Telegram as a third messaging provider alongside web and twilio. Core Features: - Interactive login flow with phone/SMS/2FA authentication - Send text and media messages (images, videos, audio, documents) - Monitor incoming messages with auto-reply support - Session management at ~/.clawdis/telegram/session/ - Full CLI integration (login, logout, status, send, relay commands) Implementation Details: - Uses telegram npm package for MTProto API access - Supports both URL and local file media sending - Cross-platform path handling (Windows/Unix) - Optional Twilio env vars (supports Telegram-only usage) - Minimal provider abstraction pattern - Comprehensive test coverage (440 tests passing) Changes: - Add Telegram module (client, login, monitor, inbound, outbound, session) - Add provider factory and base interfaces - Wire Telegram functions into CLI deps - Update env validation to make Twilio fields optional - Add telegram to all CLI commands (login, logout, status, send, relay) - Add null checks in Twilio code for optional env fields - Fix send command to properly load session and connect - Add local file support with cross-platform path handling - Update login message to show correct ~/.clawdis path - Add comprehensive tests and documentation Basic Usage: warelay login --provider telegram warelay send --provider telegram --to "@user" --message "Hi" warelay send --provider telegram --to "@user" --media "/path/to/file.jpg" warelay relay --provider telegram All tests pass (63 files, 440 tests). Zero TypeScript errors.
109 lines
2.8 KiB
TypeScript
109 lines
2.8 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { ensureTwilioEnv, readEnv } from "./env.js";
|
|
import type { RuntimeEnv } from "./runtime.js";
|
|
|
|
const baseEnv = {
|
|
TWILIO_ACCOUNT_SID: "AC123",
|
|
TWILIO_WHATSAPP_FROM: "whatsapp:+1555",
|
|
};
|
|
|
|
describe("env helpers", () => {
|
|
const runtime: RuntimeEnv = {
|
|
log: vi.fn(),
|
|
error: vi.fn(),
|
|
exit: vi.fn(() => {
|
|
throw new Error("exit");
|
|
}),
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
process.env = {};
|
|
});
|
|
|
|
function setEnv(vars: Record<string, string | undefined>) {
|
|
process.env = {};
|
|
for (const [k, v] of Object.entries(vars)) {
|
|
if (v === undefined) delete process.env[k];
|
|
else process.env[k] = v;
|
|
}
|
|
}
|
|
|
|
it("reads env with auth token", () => {
|
|
setEnv({
|
|
...baseEnv,
|
|
TWILIO_AUTH_TOKEN: "token",
|
|
TWILIO_API_KEY: undefined,
|
|
TWILIO_API_SECRET: undefined,
|
|
});
|
|
const cfg = readEnv(runtime);
|
|
expect(cfg.accountSid).toBe("AC123");
|
|
expect(cfg.whatsappFrom).toBe("whatsapp:+1555");
|
|
if (cfg.auth && "authToken" in cfg.auth) {
|
|
expect(cfg.auth.authToken).toBe("token");
|
|
} else {
|
|
throw new Error("Expected auth token");
|
|
}
|
|
});
|
|
|
|
it("reads env with API key/secret", () => {
|
|
setEnv({
|
|
...baseEnv,
|
|
TWILIO_AUTH_TOKEN: undefined,
|
|
TWILIO_API_KEY: "key",
|
|
TWILIO_API_SECRET: "secret",
|
|
});
|
|
const cfg = readEnv(runtime);
|
|
if (cfg.auth && "apiKey" in cfg.auth && "apiSecret" in cfg.auth) {
|
|
expect(cfg.auth.apiKey).toBe("key");
|
|
expect(cfg.auth.apiSecret).toBe("secret");
|
|
} else {
|
|
throw new Error("Expected API key/secret");
|
|
}
|
|
});
|
|
|
|
it("passes when no Twilio vars present (Telegram-only mode)", () => {
|
|
setEnv({
|
|
TELEGRAM_API_ID: "12345",
|
|
TELEGRAM_API_HASH: "abcdef",
|
|
});
|
|
const cfg = readEnv(runtime);
|
|
expect(cfg.telegram).toEqual({ apiId: "12345", apiHash: "abcdef" });
|
|
expect(cfg.accountSid).toBeUndefined();
|
|
expect(cfg.auth).toBeUndefined();
|
|
});
|
|
|
|
it("fails when Twilio vars partially set", () => {
|
|
setEnv({
|
|
TWILIO_ACCOUNT_SID: "AC123",
|
|
TWILIO_WHATSAPP_FROM: "",
|
|
TWILIO_AUTH_TOKEN: undefined,
|
|
TWILIO_API_KEY: undefined,
|
|
TWILIO_API_SECRET: undefined,
|
|
});
|
|
expect(() => readEnv(runtime)).toThrow("exit");
|
|
expect(runtime.error).toHaveBeenCalled();
|
|
});
|
|
|
|
it("ensureTwilioEnv passes when token present", () => {
|
|
setEnv({
|
|
...baseEnv,
|
|
TWILIO_AUTH_TOKEN: "token",
|
|
TWILIO_API_KEY: undefined,
|
|
TWILIO_API_SECRET: undefined,
|
|
});
|
|
expect(() => ensureTwilioEnv(runtime)).not.toThrow();
|
|
});
|
|
|
|
it("ensureTwilioEnv fails when missing auth", () => {
|
|
setEnv({
|
|
...baseEnv,
|
|
TWILIO_AUTH_TOKEN: undefined,
|
|
TWILIO_API_KEY: undefined,
|
|
TWILIO_API_SECRET: undefined,
|
|
});
|
|
expect(() => ensureTwilioEnv(runtime)).toThrow("exit");
|
|
});
|
|
});
|