Files
openclaw/tools/outgoing_validator.js
Clawd Bot ca9b510922 chore: align with upstream openclaw/openclaw and overlay local additions
- Reset master to upstream/main (16,697 commits)
- Overlay 2,271 local-only files (skills, tools, workspace, configs, apps)
- Restore IDENTITY.md and USER.md templates
- Build verified, gateway running, Discord working

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 07:40:46 +01:00

171 lines
4.6 KiB
JavaScript

#!/usr/bin/env node
'use strict';
// outgoing_validator.js
// Usage:
// 1) echo '{"channel":"discord","message":"hello"}' | ./outgoing_validator.js
// 2) ./outgoing_validator.js --channel discord --message "hi"
// Options:
// --send : actually run `openclaw message send --channel ... --message ...` (needs openclaw CLI in PATH)
const { spawnSync } = require('child_process');
function usageAndExit() {
console.error('Usage: outgoing_validator.js [--channel <channel>] --message <message> [--send]');
process.exit(2);
}
function readStdinSync() {
try {
const fs = require('fs');
const stat = fs.fstatSync(0);
if (stat.size > 0) {
return fs.readFileSync(0, 'utf8');
}
} catch (e) {
// ignore
}
return null;
}
function cleanObject(o) {
if (Array.isArray(o)) return o.map(cleanObject);
if (o && typeof o === 'object') {
const rv = {};
for (const k of Object.keys(o)) {
const v = o[k];
if (v === null || v === undefined) continue;
const cv = cleanObject(v);
// skip empty strings? keep but trim
if (typeof cv === 'string' && cv.trim() === '') continue;
rv[k] = cv;
}
return rv;
}
if (typeof o === 'string') {
// Trim, collapse weird control chars, limit length
let s = o.trim();
// remove null bytes and vertical tabs
s = s.replace(/[\x00\x0B\x0C]/g, '');
// collapse repeated whitespace
s = s.replace(/\s{2,}/g, ' ');
return s;
}
return o;
}
function limitString(s, max) {
if (s.length <= max) return s;
return s.slice(0, max);
}
// Parse args
const argv = process.argv.slice(2);
let channel = null;
let message = null;
let doSend = false;
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === '--channel' && argv[i+1]) { channel = argv[++i]; continue; }
if (a === '--message' && argv[i+1]) { message = argv[++i]; continue; }
if (a === '--send') { doSend = true; continue; }
if (a === '--help' || a === '-h') usageAndExit();
}
// If no message provided via args, try stdin as JSON or plain text
if (!message) {
const stdin = readStdinSync();
if (stdin) {
// Try parse JSON
try {
const parsed = JSON.parse(stdin);
if (parsed) {
channel = channel || parsed.channel || parsed.to || parsed.channelName || 'discord';
message = parsed.message || parsed.text || parsed.body || JSON.stringify(parsed);
}
} catch (e) {
// treat stdin as plain text
message = stdin.trim();
}
}
}
if (!message) {
console.error('Aborted: empty message after reading args/stdin');
process.exit(2);
}
channel = channel || 'discord';
// Build payload
let payload = { channel, message };
// Clean
payload = cleanObject(payload);
if (!payload || !payload.message) {
console.error('Aborted: message cleaned to empty');
process.exit(2);
}
// Truncate long messages (OpenClaw/Discord limits)
const MAX_LEN = 1900;
if (typeof payload.message === 'string') {
if (payload.message.length > MAX_LEN) {
payload.message = limitString(payload.message, MAX_LEN);
payload.message += '\n\n[...trimmed]';
}
}
// Validate final JSON
let jsonOut;
try {
jsonOut = JSON.stringify(payload);
} catch (e) {
console.error('Failed to serialize payload JSON:', e.message);
process.exit(3);
}
// Log repaired payload locally
try {
const fs = require('fs');
const logDir = '/home/alex/clawd/logs';
try { fs.mkdirSync(logDir, { recursive: true }); } catch (e) {}
const ts = new Date().toISOString();
fs.appendFileSync(logDir + '/outgoing_validator.log', ts + ' ' + jsonOut + '\n');
} catch (e) {
// ignore logging errors
}
// Optional: increment Redis counter if redis-cli available and REDIS_KEY env set
if (process.env.REDIS_KEY) {
try {
const r = spawnSync('redis-cli', ['INCR', process.env.REDIS_KEY]);
if (r.error) {
// redis-cli not available or failed - ignore
}
} catch (e) {}
}
// Output repaired JSON
console.log(jsonOut);
if (doSend) {
// Use openclaw CLI to send (if available). Don't fail loudly if not installed.
try {
const ocArgs = ['message', 'send', '--channel', payload.channel, '--message'];
// Use JSON string for message argument safely
const msgArg = payload.message;
const cmd = ['openclaw', ...ocArgs, msgArg];
const r = spawnSync('openclaw', ['message', 'send', '--channel', payload.channel, '--message', payload.message], { encoding: 'utf8' });
if (r.error) {
console.error('OpenClaw send failed to start:', r.error.message);
process.exit(4);
}
console.log('send result:', r.stdout || r.stderr);
} catch (e) {
console.error('Send attempt failed:', e.message);
process.exit(4);
}
}
process.exit(0);