Install
openclaw skills install castreaderURL to audio: extract any web page and convert to natural AI speech (Kokoro TTS). The only skill that turns a URL into a podcast-quality MP3 — no API key nee...
openclaw skills install castreadercd <skill-directory> && npm install
User messages look like: [Telegram username id:8716240840 ...]
The number after id: is the target. You MUST use this number in every message tool call.
Example: target is "8716240840".
node scripts/read-url.js "<url>" 0
Returns: { title, language, totalParagraphs, totalCharacters, paragraphs[] }
Reply with this text:
📖 {title}
🌐 {language} · 📝 {totalParagraphs} paragraphs · 📊 {totalCharacters} chars
📋 Summary:
{write 2-3 sentence summary from paragraphs}
Reply a number to choose:
1️⃣ Listen to full article (~{totalCharacters} chars, ~{Math.ceil(totalCharacters / 200)} sec to generate)
2️⃣ Listen to summary only (~{summary_char_count} chars, ~{Math.ceil(summary_char_count / 200)} sec to generate)
STOP. Wait for user to reply 1 or 2.
Reply: 🎙️ Generating full audio (~{totalCharacters} chars, ~{Math.ceil(totalCharacters / 200)} seconds)...
node scripts/read-url.js "<url>" all
Then send the audio file using the message tool:
{"action":"send", "target":"<chatId>", "channel":"telegram", "filePath":"<audioFile>", "caption":"🔊 {title}"}
Reply: ✅ Done!
Reply: 🎙️ Generating summary audio...
Save the SAME summary text you showed in Step 2 to a file and generate:
echo "<summary text>" > /tmp/castreader-summary.txt
node scripts/generate-text.js /tmp/castreader-summary.txt <language>
Then send the audio file using the message tool:
{"action":"send", "target":"<chatId>", "channel":"telegram", "filePath":"/tmp/castreader-summary.mp3", "caption":"📋 Summary: {title}"}
Reply: ✅ Done!
Books are synced from WeChat Reading or Kindle to ~/castreader-library/books/.
Each book is stored in a folder like 书名-hashid (e.g. 儒林外史-dc532c705c6d3edc5503acc).
⚠️ CRITICAL: You MUST use sync-books.js --list to get the exact book folder ID. NEVER guess or construct the folder path yourself. The folder name includes a title prefix that you cannot predict.
node scripts/sync-books.js --list
Returns: { books: [{ id, title, author, language, totalChapters, totalCharacters, source, syncedAt }] }
The id field is the exact folder name you must use in all subsequent commands. Example: "儒林外史-dc532c705c6d3edc5503acc".
Reply with the book list:
📚 Your synced books:
1. 📖 {title} — {author}
🌐 {language} · 📑 {totalChapters} chapters · 📊 {totalCharacters} chars · 📱 {source}
2. ...
Reply the number of the book you want to read.
STOP. Wait for user to choose a book.
node scripts/sync-books.js --book "<id>"
Use the exact id from Step 1 output. Returns the book content with chapter list.
Reply:
📖 {title} — {author}
📑 {totalChapters} chapters · 📊 {totalCharacters} chars
📋 Chapters:
1. {chapter 1 title}
2. {chapter 2 title}
...
Reply a number to listen to a chapter, or "all" to listen to the full book.
STOP. Wait for user to choose.
node scripts/sync-books.js --book "<id>" --chapter <num> --audio
Returns: { title, audioFile, fileSizeBytes }
Send the audio:
{"action":"send", "target":"<chatId>", "channel":"telegram", "filePath":"<audioFile>", "caption":"🔊 {bookTitle} — Chapter {num}"}
node scripts/sync-books.js --book "<id>" --audio
Returns: { title, audioFile, fileSizeBytes }
Send the audio:
{"action":"send", "target":"<chatId>", "channel":"telegram", "filePath":"<audioFile>", "caption":"🔊 {bookTitle} (full)"}
If the user wants to read (not listen), use without --audio:
node scripts/sync-books.js --book "<id>" --chapter <num>
Returns: { title, author, language, chapter: { number, title, text }, totalChapters }
message tool with target (numeric chatId) and channel ("telegram"). Never just print the file path.--list first and use the exact id from the output. NEVER construct book paths manually or use partial IDs.read-url.js, generate-text.js, and sync-books.js.read-url.js.read tool to directly access files in ~/castreader-library/. ONLY use sync-books.js.