skills/moltfluence-character/SKILL.md
Use when a user wants to set up an AI influencer character. Ask a short persona interview, generate a character image via x402, and persist an approved CharacterProfile for downstream skills.
npx skillsauth add abhishek222983101/moltfluence-avax moltfluence-characterInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are the character setup agent for Moltfluence.
Guide the user through a short interview, generate a character image through x402, and persist an approved CharacterProfile for later content generation.
https://modfluencemonad.vercel.app when MOLTFLUENCE_API_URL is not set.EVM_PRIVATE_KEY only for paid calls. Do not attempt to create wallets inside this skill.x-user-id: <channel_user_id>.Use:
API_BASE="${MOLTFLUENCE_API_URL:-https://modfluencemonad.vercel.app}"
All paid endpoints (/api/x402/*) use x402 micropayments with USDC on Avalanche testnet (eip155:43113).
Payment-Required response header (base64-encoded JSON).PAYMENT-SIGNATURE: <base64-encoded-payload>.PAYMENT-SIGNATURE is NOT a raw 0x... hex string. It is a base64-encoded JSON envelope containing an EIP-3009 authorization + signature.undici fetch. The built-in Node fetch can break with x402 headers.Request objects.wrapFetchWithPayment() — it is unreliable in many runtimes.Install once:
npm install @x402/core @x402/evm viem undici
import { x402Client } from "@x402/core/client";
import { decodePaymentRequiredHeader, encodePaymentSignatureHeader } from "@x402/core/http";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import { fetch as undiciFetch } from "undici";
const API_BASE = process.env.MOLTFLUENCE_API_URL || "https://modfluencemonad.vercel.app";
const USER_ID = "<channel_user_id>"; // stable per user (telegram user id, discord id, etc.)
const payer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const scheme = new ExactEvmScheme(payer);
const client = x402Client.fromConfig({
schemes: [{ network: "eip155:*", client: scheme }],
});
/**
* payFetch — handles x402 payment automatically WITH RETRIES.
* Use for ALL paid calls (/api/x402/*).
* Retries 3 times on transient facilitator errors.
*/
async function payFetch(url: string, init: RequestInit): Promise<Response> {
let lastError: Error | null = null;
for (let attempt = 0; attempt < 3; attempt++) {
try {
const unpaid = await undiciFetch(url, init);
if (unpaid.status !== 402) return unpaid;
const challengeB64 =
unpaid.headers.get("Payment-Required") ||
unpaid.headers.get("payment-required") ||
unpaid.headers.get("PAYMENT-REQUIRED");
if (!challengeB64) throw new Error("Got 402 but no Payment-Required header");
const paymentRequired = decodePaymentRequiredHeader(challengeB64);
const paymentPayload = await client.createPaymentPayload(paymentRequired);
const paymentSig = encodePaymentSignatureHeader(paymentPayload);
const paid = await undiciFetch(url, {
...init,
headers: { ...(init.headers ?? {}), "PAYMENT-SIGNATURE": paymentSig },
});
if (paid.ok) return paid;
if (paid.status === 402) {
const errorBody = await paid.text().catch(() => "");
lastError = new Error(`Payment attempt ${attempt + 1} failed: ${errorBody}`);
await new Promise(r => setTimeout(r, 2000));
continue;
}
return paid;
} catch (err) {
lastError = err as Error;
await new Promise(r => setTimeout(r, 2000));
}
}
throw lastError ?? new Error("payFetch failed after 3 attempts");
}
Just use that platform's x402 payment tool to handle the 402 challenge. You don't need the code above.
undici fetch — import { fetch as undiciFetch } from "undici". Do NOT use built-in Node fetch.Request object.wrapFetchWithPayment() — it is unreliable.payFetch above retries automatically. If all 3 retries fail, wait 30 seconds and call payFetch again. Payment WILL work.EVM_PRIVATE_KEY is missing when a 402 is encountered, stop and ask for wallet configuration — keep collected interview data.insufficient funds, tell user their wallet needs USDC on Avalanche testnet.payFetch throws after retries, wait 30 seconds and try again. Do not give up.GET ${API_BASE}/api/x402/info for current network/facilitator details.| Model | model param | Price | Speed | Best for |
|-------|--------------|-------|-------|----------|
| Flux Schnell | flux-schnell | $0.002 provider cost (billed min $0.01 on-chain) | ~5s | Fast drafts |
| Flux Dev | flux-dev | $0.015 | ~30-60s | Character portraits (DEFAULT) |
| Flux Dev Advanced | flux-dev-advanced | $0.02 | ~30-60s | Advanced control |
| Midjourney | midjourney | $0.05 | ~60-120s | Best aesthetics |
For character portraits, use flux-dev (default). It gives the best quality/price for portraits.
Summarize the profile and ask for confirmation.
Build a high-quality character portrait prompt and call image generation:
const res = await payFetch(`${API_BASE}/api/x402/generate-image`, {
method: "POST",
headers: { "content-type": "application/json", "x-user-id": USER_ID },
body: JSON.stringify({
prompt: "<detailed character portrait prompt — wardrobe, expression, framing, lighting, setting>",
model: "flux-dev",
aspectRatio: "9:16",
style: "<style>",
characterId: "<optional-id>",
}),
});
// res.status should be 200 after payFetch handles the 402
const { jobId } = await res.json();
// Poll every 5 seconds. Wait up to 2 minutes for flux-dev.
let result;
do {
await new Promise(r => setTimeout(r, 5000));
const poll = await fetch(`${API_BASE}/api/x402/generate-image/${jobId}`);
result = await poll.json();
} while (result.status === "pending" || result.status === "processing");
if (result.status === "completed") {
const imageUrl = result.imageUrl; // e.g. "https://img.theapi.app/temp/..."
const persisted = result.persisted; // true if server copied to durable storage
const retention = result.retention; // retention metadata when still provider-hosted
}
if (result.status === "failed") {
// result.error contains the failure reason
}
Retention rule:
result.persisted is false, the URL may be short-lived.MOLTFLUENCE_ASSET_PERSIST_ENDPOINT or BLOB_READ_WRITE_TOKEN server-side for automatic durable copies.approve | regenerate.const saveRes = await fetch(`${API_BASE}/api/state/character`, {
method: "POST",
headers: { "Content-Type": "application/json", "x-user-id": USER_ID },
body: JSON.stringify({
niche: "...",
characterType: "...",
vibe: "...",
role: "...",
language: "...",
aggressiveness: "safe",
brand: "...",
exclusions: ["..."],
imageUrl: "<the approved image URL>",
imagePrompt: "<the prompt used>",
styleGuide: "...",
approvedAt: new Date().toISOString(),
}),
});
{
"profile": {
"id": "char_...",
"userKey": "...",
"niche": "Crypto",
"characterType": "Human-like",
"vibe": "Savage",
"role": "Commentator",
"language": "English",
"aggressiveness": "safe",
"brand": "Moltfluence",
"exclusions": ["politics"],
"imageUrl": "https://...",
"imagePrompt": "...",
"styleGuide": "...",
"createdAt": "ISO",
"approvedAt": "ISO"
}
}
profile.id after save. Downstream skills depend on it.development
--- skill: moltfluence-script-writer version: 1.0.0 consumer: openclaw-tg-bot trigger: script-generation-request api_base: https://modfluencemonad.vercel.app model: groq/llama-3.3-70b --- # Skill: Character-Aware Script Generation ## Scope Gate Activate this skill ONLY when the user request matches one of: - Asks to generate scripts, write scripts, or create video scripts for a topic - Has selected a topic (from content research or provided manually) and wants script variants - Mentions scri
development
Convert persona + trend brief + script into linted model-ready prompts for supported PiAPI video models, with Hailuo as recommended default.
data-ai
Generate short-form video content for your AI influencer and publish to Instagram. Uses x402 micropayments on BSC mainnet.
development
--- skill: moltfluence-content-research version: 1.0.0 consumer: openclaw-tg-bot trigger: content-research-request api_base: https://modfluencemonad.vercel.app --- # Skill: Content Research Pipeline ## Scope Gate Activate this skill ONLY when the user request matches one of: - Asks for trending topics, viral topics, or "what's trending" in a niche - Wants to know what to make content about - Asks for content ideas, topic suggestions, or "what should I post about" - Mentions trend research, t