mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
Gateway/Plugins: device pairing + phone control plugins (#11755)
This commit is contained in:
163
scripts/dev/gateway-smoke.ts
Normal file
163
scripts/dev/gateway-smoke.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import WebSocket from "ws";
|
||||
|
||||
type GatewayReqFrame = { type: "req"; id: string; method: string; params?: unknown };
|
||||
type GatewayResFrame = { type: "res"; id: string; ok: boolean; payload?: unknown; error?: unknown };
|
||||
type GatewayEventFrame = { type: "event"; event: string; seq?: number; payload?: unknown };
|
||||
type GatewayFrame = GatewayReqFrame | GatewayResFrame | GatewayEventFrame | { type: string };
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const getArg = (flag: string) => {
|
||||
const idx = args.indexOf(flag);
|
||||
if (idx !== -1 && idx + 1 < args.length) {
|
||||
return args[idx + 1];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const urlRaw = getArg("--url") ?? process.env.OPENCLAW_GATEWAY_URL;
|
||||
const token = getArg("--token") ?? process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
|
||||
if (!urlRaw || !token) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Usage: bun scripts/dev/gateway-smoke.ts --url <wss://host[:port]> --token <gateway.auth.token>\n" +
|
||||
"Or set env: OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const url = new URL(urlRaw.includes("://") ? urlRaw : `wss://${urlRaw}`);
|
||||
if (!url.port) {
|
||||
url.port = url.protocol === "wss:" ? "443" : "80";
|
||||
}
|
||||
|
||||
const randomId = () => randomUUID();
|
||||
|
||||
async function main() {
|
||||
const ws = new WebSocket(url.toString(), { handshakeTimeout: 8000 });
|
||||
const pending = new Map<
|
||||
string,
|
||||
{
|
||||
resolve: (res: GatewayResFrame) => void;
|
||||
reject: (err: Error) => void;
|
||||
timeout: ReturnType<typeof setTimeout>;
|
||||
}
|
||||
>();
|
||||
|
||||
const request = (method: string, params?: unknown, timeoutMs = 12000) =>
|
||||
new Promise<GatewayResFrame>((resolve, reject) => {
|
||||
const id = randomId();
|
||||
const frame: GatewayReqFrame = { type: "req", id, method, params };
|
||||
const timeout = setTimeout(() => {
|
||||
pending.delete(id);
|
||||
reject(new Error(`timeout waiting for ${method}`));
|
||||
}, timeoutMs);
|
||||
pending.set(id, { resolve, reject, timeout });
|
||||
ws.send(JSON.stringify(frame));
|
||||
});
|
||||
|
||||
const waitOpen = () =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const t = setTimeout(() => reject(new Error("ws open timeout")), 8000);
|
||||
ws.once("open", () => {
|
||||
clearTimeout(t);
|
||||
resolve();
|
||||
});
|
||||
ws.once("error", (err) => {
|
||||
clearTimeout(t);
|
||||
reject(err instanceof Error ? err : new Error(String(err)));
|
||||
});
|
||||
});
|
||||
|
||||
const toText = (data: WebSocket.RawData) => {
|
||||
if (typeof data === "string") {
|
||||
return data;
|
||||
}
|
||||
if (data instanceof ArrayBuffer) {
|
||||
return Buffer.from(data).toString("utf8");
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
return Buffer.concat(data.map((chunk) => Buffer.from(chunk))).toString("utf8");
|
||||
}
|
||||
return Buffer.from(data as Buffer).toString("utf8");
|
||||
};
|
||||
|
||||
ws.on("message", (data) => {
|
||||
const text = toText(data);
|
||||
let frame: GatewayFrame | null = null;
|
||||
try {
|
||||
frame = JSON.parse(text) as GatewayFrame;
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (!frame || typeof frame !== "object" || !("type" in frame)) {
|
||||
return;
|
||||
}
|
||||
if (frame.type === "res") {
|
||||
const res = frame as GatewayResFrame;
|
||||
const waiter = pending.get(res.id);
|
||||
if (waiter) {
|
||||
pending.delete(res.id);
|
||||
clearTimeout(waiter.timeout);
|
||||
waiter.resolve(res);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (frame.type === "event") {
|
||||
const evt = frame as GatewayEventFrame;
|
||||
if (evt.event === "connect.challenge") {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
await waitOpen();
|
||||
|
||||
// Match iOS "operator" session defaults: token auth, no device identity.
|
||||
const connectRes = await request("connect", {
|
||||
minProtocol: 3,
|
||||
maxProtocol: 3,
|
||||
client: {
|
||||
id: "openclaw-ios",
|
||||
displayName: "openclaw gateway smoke test",
|
||||
version: "dev",
|
||||
platform: "dev",
|
||||
mode: "ui",
|
||||
instanceId: "openclaw-dev-smoke",
|
||||
},
|
||||
locale: "en-US",
|
||||
userAgent: "gateway-smoke",
|
||||
role: "operator",
|
||||
scopes: ["operator.read", "operator.write", "operator.admin"],
|
||||
caps: [],
|
||||
auth: { token },
|
||||
});
|
||||
|
||||
if (!connectRes.ok) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("connect failed:", connectRes.error);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const healthRes = await request("health");
|
||||
if (!healthRes.ok) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("health failed:", healthRes.error);
|
||||
process.exit(3);
|
||||
}
|
||||
|
||||
const historyRes = await request("chat.history", { sessionKey: "main" }, 15000);
|
||||
if (!historyRes.ok) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("chat.history failed:", historyRes.error);
|
||||
process.exit(4);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("ok: connected + health + chat.history");
|
||||
ws.close();
|
||||
}
|
||||
|
||||
await main();
|
||||
373
scripts/dev/ios-node-e2e.ts
Normal file
373
scripts/dev/ios-node-e2e.ts
Normal file
@@ -0,0 +1,373 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import WebSocket from "ws";
|
||||
|
||||
type GatewayReqFrame = { type: "req"; id: string; method: string; params?: unknown };
|
||||
type GatewayResFrame = { type: "res"; id: string; ok: boolean; payload?: unknown; error?: unknown };
|
||||
type GatewayEventFrame = { type: "event"; event: string; seq?: number; payload?: unknown };
|
||||
type GatewayFrame = GatewayReqFrame | GatewayResFrame | GatewayEventFrame | { type: string };
|
||||
|
||||
type NodeListPayload = {
|
||||
ts?: number;
|
||||
nodes?: Array<{
|
||||
nodeId: string;
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
connected?: boolean;
|
||||
paired?: boolean;
|
||||
commands?: string[];
|
||||
permissions?: unknown;
|
||||
}>;
|
||||
};
|
||||
|
||||
type NodeListNode = NonNullable<NodeListPayload["nodes"]>[number];
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const getArg = (flag: string) => {
|
||||
const idx = args.indexOf(flag);
|
||||
if (idx !== -1 && idx + 1 < args.length) {
|
||||
return args[idx + 1];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const hasFlag = (flag: string) => args.includes(flag);
|
||||
|
||||
const urlRaw = getArg("--url") ?? process.env.OPENCLAW_GATEWAY_URL;
|
||||
const token = getArg("--token") ?? process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
const nodeHint = getArg("--node");
|
||||
const dangerous = hasFlag("--dangerous") || process.env.OPENCLAW_RUN_DANGEROUS === "1";
|
||||
const jsonOut = hasFlag("--json");
|
||||
|
||||
if (!urlRaw || !token) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Usage: bun scripts/dev/ios-node-e2e.ts --url <wss://host[:port]> --token <gateway.auth.token> [--node <id|name-substring>] [--dangerous] [--json]\n" +
|
||||
"Or set env: OPENCLAW_GATEWAY_URL / OPENCLAW_GATEWAY_TOKEN",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const url = new URL(urlRaw.includes("://") ? urlRaw : `wss://${urlRaw}`);
|
||||
if (!url.port) {
|
||||
url.port = url.protocol === "wss:" ? "443" : "80";
|
||||
}
|
||||
|
||||
const randomId = () => randomUUID();
|
||||
|
||||
const isoNow = () => new Date().toISOString();
|
||||
const isoMinusMs = (ms: number) => new Date(Date.now() - ms).toISOString();
|
||||
|
||||
type TestCase = {
|
||||
id: string;
|
||||
command: string;
|
||||
params?: unknown;
|
||||
timeoutMs?: number;
|
||||
dangerous?: boolean;
|
||||
};
|
||||
|
||||
function formatErr(err: unknown): string {
|
||||
if (!err) {
|
||||
return "error";
|
||||
}
|
||||
if (typeof err === "string") {
|
||||
return err;
|
||||
}
|
||||
if (err instanceof Error) {
|
||||
return err.message || String(err);
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(err);
|
||||
} catch {
|
||||
return Object.prototype.toString.call(err);
|
||||
}
|
||||
}
|
||||
|
||||
function pickIosNode(list: NodeListPayload, hint?: string): NodeListNode | null {
|
||||
const nodes = (list.nodes ?? []).filter((n) => n && n.connected);
|
||||
const ios = nodes.filter((n) => (n.platform ?? "").toLowerCase().includes("ios"));
|
||||
if (ios.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (!hint) {
|
||||
return ios[0] ?? null;
|
||||
}
|
||||
const h = hint.toLowerCase();
|
||||
return (
|
||||
ios.find((n) => n.nodeId.toLowerCase() === h) ??
|
||||
ios.find((n) => (n.displayName ?? "").toLowerCase().includes(h)) ??
|
||||
ios.find((n) => n.nodeId.toLowerCase().includes(h)) ??
|
||||
ios[0] ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const ws = new WebSocket(url.toString(), { handshakeTimeout: 8000 });
|
||||
const pending = new Map<
|
||||
string,
|
||||
{
|
||||
resolve: (res: GatewayResFrame) => void;
|
||||
reject: (err: Error) => void;
|
||||
timeout: ReturnType<typeof setTimeout>;
|
||||
}
|
||||
>();
|
||||
|
||||
const request = (method: string, params?: unknown, timeoutMs = 12_000) =>
|
||||
new Promise<GatewayResFrame>((resolve, reject) => {
|
||||
const id = randomId();
|
||||
const frame: GatewayReqFrame = { type: "req", id, method, params };
|
||||
const timeout = setTimeout(() => {
|
||||
pending.delete(id);
|
||||
reject(new Error(`timeout waiting for ${method}`));
|
||||
}, timeoutMs);
|
||||
pending.set(id, { resolve, reject, timeout });
|
||||
ws.send(JSON.stringify(frame));
|
||||
});
|
||||
|
||||
const waitOpen = () =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const t = setTimeout(() => reject(new Error("ws open timeout")), 8000);
|
||||
ws.once("open", () => {
|
||||
clearTimeout(t);
|
||||
resolve();
|
||||
});
|
||||
ws.once("error", (err) => {
|
||||
clearTimeout(t);
|
||||
reject(err instanceof Error ? err : new Error(String(err)));
|
||||
});
|
||||
});
|
||||
|
||||
const toText = (data: WebSocket.RawData) => {
|
||||
if (typeof data === "string") {
|
||||
return data;
|
||||
}
|
||||
if (data instanceof ArrayBuffer) {
|
||||
return Buffer.from(data).toString("utf8");
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
return Buffer.concat(data.map((chunk) => Buffer.from(chunk))).toString("utf8");
|
||||
}
|
||||
return Buffer.from(data as Buffer).toString("utf8");
|
||||
};
|
||||
|
||||
ws.on("message", (data) => {
|
||||
const text = toText(data);
|
||||
let frame: GatewayFrame | null = null;
|
||||
try {
|
||||
frame = JSON.parse(text) as GatewayFrame;
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (!frame || typeof frame !== "object" || !("type" in frame)) {
|
||||
return;
|
||||
}
|
||||
if (frame.type === "res") {
|
||||
const res = frame as GatewayResFrame;
|
||||
const waiter = pending.get(res.id);
|
||||
if (waiter) {
|
||||
pending.delete(res.id);
|
||||
clearTimeout(waiter.timeout);
|
||||
waiter.resolve(res);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (frame.type === "event") {
|
||||
// Ignore; caller can extend to watch node.pair.* etc.
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
await waitOpen();
|
||||
|
||||
const connectRes = await request("connect", {
|
||||
minProtocol: 3,
|
||||
maxProtocol: 3,
|
||||
client: {
|
||||
id: "cli",
|
||||
displayName: "openclaw ios node e2e",
|
||||
version: "dev",
|
||||
platform: "dev",
|
||||
mode: "cli",
|
||||
instanceId: "openclaw-dev-ios-node-e2e",
|
||||
},
|
||||
locale: "en-US",
|
||||
userAgent: "ios-node-e2e",
|
||||
role: "operator",
|
||||
scopes: ["operator.read", "operator.write", "operator.admin"],
|
||||
caps: [],
|
||||
auth: { token },
|
||||
});
|
||||
|
||||
if (!connectRes.ok) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("connect failed:", connectRes.error);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const healthRes = await request("health");
|
||||
if (!healthRes.ok) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("health failed:", healthRes.error);
|
||||
process.exit(3);
|
||||
}
|
||||
|
||||
const nodesRes = await request("node.list");
|
||||
if (!nodesRes.ok) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("node.list failed:", nodesRes.error);
|
||||
process.exit(4);
|
||||
}
|
||||
|
||||
const listPayload = (nodesRes.payload ?? {}) as NodeListPayload;
|
||||
let node = pickIosNode(listPayload, nodeHint);
|
||||
if (!node) {
|
||||
const waitSeconds = Number.parseInt(getArg("--wait-seconds") ?? "25", 10);
|
||||
const deadline = Date.now() + Math.max(1, waitSeconds) * 1000;
|
||||
while (!node && Date.now() < deadline) {
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
const res = await request("node.list").catch(() => null);
|
||||
if (!res?.ok) {
|
||||
continue;
|
||||
}
|
||||
node = pickIosNode((res.payload ?? {}) as NodeListPayload, nodeHint);
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("No connected iOS nodes found. (Is the iOS app connected to the gateway?)");
|
||||
process.exit(5);
|
||||
}
|
||||
|
||||
const tests: TestCase[] = [
|
||||
{ id: "device.info", command: "device.info" },
|
||||
{ id: "device.status", command: "device.status" },
|
||||
{
|
||||
id: "system.notify",
|
||||
command: "system.notify",
|
||||
params: { title: "OpenClaw E2E", body: `ios-node-e2e @ ${isoNow()}`, delivery: "system" },
|
||||
},
|
||||
{
|
||||
id: "contacts.search",
|
||||
command: "contacts.search",
|
||||
params: { query: null, limit: 5 },
|
||||
},
|
||||
{
|
||||
id: "calendar.events",
|
||||
command: "calendar.events",
|
||||
params: { startISO: isoMinusMs(6 * 60 * 60 * 1000), endISO: isoNow(), limit: 10 },
|
||||
},
|
||||
{
|
||||
id: "reminders.list",
|
||||
command: "reminders.list",
|
||||
params: { status: "incomplete", limit: 10 },
|
||||
},
|
||||
{
|
||||
id: "motion.pedometer",
|
||||
command: "motion.pedometer",
|
||||
params: { startISO: isoMinusMs(60 * 60 * 1000), endISO: isoNow() },
|
||||
},
|
||||
{
|
||||
id: "photos.latest",
|
||||
command: "photos.latest",
|
||||
params: { limit: 1, maxWidth: 512, quality: 0.7 },
|
||||
},
|
||||
{
|
||||
id: "camera.snap",
|
||||
command: "camera.snap",
|
||||
params: { facing: "back", maxWidth: 768, quality: 0.7, format: "jpeg" },
|
||||
dangerous: true,
|
||||
timeoutMs: 20_000,
|
||||
},
|
||||
{
|
||||
id: "screen.record",
|
||||
command: "screen.record",
|
||||
params: { durationMs: 2_000, fps: 15, includeAudio: false },
|
||||
dangerous: true,
|
||||
timeoutMs: 30_000,
|
||||
},
|
||||
];
|
||||
|
||||
const run = tests.filter((t) => dangerous || !t.dangerous);
|
||||
|
||||
const results: Array<{
|
||||
id: string;
|
||||
ok: boolean;
|
||||
error?: unknown;
|
||||
payload?: unknown;
|
||||
}> = [];
|
||||
|
||||
for (const t of run) {
|
||||
const invokeRes = await request(
|
||||
"node.invoke",
|
||||
{
|
||||
nodeId: node.nodeId,
|
||||
command: t.command,
|
||||
params: t.params,
|
||||
timeoutMs: t.timeoutMs ?? 12_000,
|
||||
idempotencyKey: randomUUID(),
|
||||
},
|
||||
(t.timeoutMs ?? 12_000) + 2_000,
|
||||
).catch((err) => {
|
||||
results.push({ id: t.id, ok: false, error: formatErr(err) });
|
||||
return null;
|
||||
});
|
||||
|
||||
if (!invokeRes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!invokeRes.ok) {
|
||||
results.push({ id: t.id, ok: false, error: invokeRes.error });
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push({ id: t.id, ok: true, payload: invokeRes.payload });
|
||||
}
|
||||
|
||||
if (jsonOut) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
gateway: url.toString(),
|
||||
node: {
|
||||
nodeId: node.nodeId,
|
||||
displayName: node.displayName,
|
||||
platform: node.platform,
|
||||
},
|
||||
dangerous,
|
||||
results,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const pad = (s: string, n: number) => (s.length >= n ? s : s + " ".repeat(n - s.length));
|
||||
const rows = results.map((r) => ({
|
||||
cmd: r.id,
|
||||
ok: r.ok ? "ok" : "fail",
|
||||
note: r.ok ? "" : formatErr(r.error ?? "error"),
|
||||
}));
|
||||
const width = Math.min(64, Math.max(12, ...rows.map((r) => r.cmd.length)));
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`node: ${node.displayName ?? node.nodeId} (${node.platform ?? "unknown"})`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`dangerous: ${dangerous ? "on" : "off"}`);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("");
|
||||
for (const r of rows) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`${pad(r.cmd, width)} ${pad(r.ok, 4)} ${r.note}`);
|
||||
}
|
||||
}
|
||||
|
||||
const failed = results.filter((r) => !r.ok);
|
||||
ws.close();
|
||||
|
||||
if (failed.length > 0) {
|
||||
process.exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
await main();
|
||||
17
scripts/dev/ios-pull-gateway-log.sh
Executable file
17
scripts/dev/ios-pull-gateway-log.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
DEVICE_UDID="${1:-00008130-000630CE0146001C}"
|
||||
BUNDLE_ID="${2:-ai.openclaw.ios.dev.mariano.test}"
|
||||
DEST="${3:-/tmp/openclaw-gateway.log}"
|
||||
|
||||
xcrun devicectl device copy from \
|
||||
--device "$DEVICE_UDID" \
|
||||
--domain-type appDataContainer \
|
||||
--domain-identifier "$BUNDLE_ID" \
|
||||
--source Documents/openclaw-gateway.log \
|
||||
--destination "$DEST" >/dev/null
|
||||
|
||||
echo "Pulled to: $DEST"
|
||||
tail -n 200 "$DEST"
|
||||
|
||||
62
scripts/dev/test-device-pair-telegram.ts
Normal file
62
scripts/dev/test-device-pair-telegram.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { loadConfig } from "../../src/config/config.js";
|
||||
import { matchPluginCommand, executePluginCommand } from "../../src/plugins/commands.js";
|
||||
import { loadOpenClawPlugins } from "../../src/plugins/loader.js";
|
||||
import { sendMessageTelegram } from "../../src/telegram/send.js";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const getArg = (flag: string, short?: string) => {
|
||||
const idx = args.indexOf(flag);
|
||||
if (idx !== -1 && idx + 1 < args.length) {
|
||||
return args[idx + 1];
|
||||
}
|
||||
if (short) {
|
||||
const sidx = args.indexOf(short);
|
||||
if (sidx !== -1 && sidx + 1 < args.length) {
|
||||
return args[sidx + 1];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const chatId = getArg("--chat", "-c");
|
||||
const accountId = getArg("--account", "-a");
|
||||
if (!chatId) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Usage: bun scripts/dev/test-device-pair-telegram.ts --chat <telegram-chat-id> [--account <accountId>]",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const cfg = loadConfig();
|
||||
loadOpenClawPlugins({ config: cfg });
|
||||
|
||||
const match = matchPluginCommand("/pair");
|
||||
if (!match) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("/pair plugin command not registered.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const result = await executePluginCommand({
|
||||
command: match.command,
|
||||
args: match.args,
|
||||
senderId: chatId,
|
||||
channel: "telegram",
|
||||
channelId: "telegram",
|
||||
isAuthorizedSender: true,
|
||||
commandBody: "/pair",
|
||||
config: cfg,
|
||||
from: `telegram:${chatId}`,
|
||||
to: `telegram:${chatId}`,
|
||||
accountId: accountId,
|
||||
});
|
||||
|
||||
if (result.text) {
|
||||
await sendMessageTelegram(chatId, result.text, {
|
||||
accountId: accountId,
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Sent split /pair messages to", chatId, accountId ? `(${accountId})` : "");
|
||||
Reference in New Issue
Block a user