自动下载Snapchat回忆的JS脚本报错求助:语法错误800A3EA
Snapchat回忆下载JS脚本报错排查与修复
问题重现
两年前从GitHub获取的Snapchat回忆自动下载脚本,此前运行正常,现突然失效,报错信息:
Error:
Line: 2
Char: 1
Error: Syntax error
Code: 800A3EA
无论调整代码顺序,始终提示首行首字符语法错误,原脚本代码如下:
// IMPORTS const https = require("https"); const fs = require("fs"); const Queue = require("./concurrency.js"); const Progress = require("./progress.js"); const moment = require("moment"); const utimes = require("utimes").utimes; const { Command } = require("commander"); // PARSE ARGUMENTS const program = new Command(); program .description("A script to download Snapchat Memories Example: node main.js -c 50 -f ./json/memories_history.json -o Downloads") .option("-c <number>", "Number of concurrent downloads", 30) .option("-f <path>", "Filepath to memories_history.json", "./json/memories_history.json") .option("-o <directory>", "Download directory", "Downloads"); program.parse(); const options = program.opts(); // CONFIG const outputDir = options.o; const maxConcurrentDownloads = options.c; const jsonFile = (!options.f.startsWith("/") && !options.f.startsWith("./")) ? "./" + options.f : options.f; const progressBarLength = 20; // INIT if (!jsonFile.startsWith("/") && !jsonFile.startsWith("./")) jsonFile = "./" + jsonFile var downloads = require("./" + jsonFile)["Saved Media"]; var queue = new Queue(maxConcurrentDownloads); var progress = new Progress(downloads.length, progressBarLength); function main() { // Create download directory if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir); // Start downloads for (let i = 0; i < downloads.length; i++) { let [url, body] = downloads[i]["Download Link"].split('?', 2); const fileTime = moment.utc(downloads[i]["Date"], "YYYY-MM-DD HH:mm:ss Z") let fileName = getFileName(downloads[i], fileTime); // First get CDN download link queue.enqueue(() => getDownloadLink(url, body, fileName, fileTime)) .then((result) => { progress.cdnLinkSucceeded(true); // Download the file queue.enqueue(() => downloadMemory(result[0], result[1], result[2])) .then((success) => { progress.downloadSucceeded(true); }) .catch((err) => { progress.downloadSucceeded(false); }) }) .catch((err) => { progress.cdnLinkSucceeded(false); }) } } function getFileName(download, fileTime) { var filename = fileTime.format("YYYY-MM-DD_HH-mm-ss") if (download["Media Type"] == "PHOTO") filename += ".jpg"; else if (download["Media Type"] == "VIDEO") filename += ".mp4"; return filename } const getDownloadLink = (url, body, filename, fileTime) => new Promise(resolve => { var parsedUrl = new URL(url); const options = { hostname: parsedUrl.hostname, path: parsedUrl.pathname, method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, }; var req = https.request(options, (res) => { data = ""; res.on("data", (chunk) => { data += chunk; }); res.on("error", function () { reject("request error"); }) res.on("end", function () { if (res.statusCode == 200) { resolve([data, filename, fileTime]); } else { reject("status error"); } }); }); req.write(body); req.end(); }); const downloadMemory = (downloadUrl, filename, fileTime) => new Promise(resolve => { // Check if there already exists a file with the same name/timestamp if (fs.existsSync(outputDir + "/" + filename)) { duplicates = 1; while (true) { var extensionPos = filename.lastIndexOf('.'); var newFilename = filename.substring(0, extensionPos) + " (" + duplicates + ")" + filename.substring(extensionPos, filename.length); if (fs.existsSync(outputDir + "/" + newFilename)) duplicates++; else { filename = newFilename; break; } } } // Create the file and write to it var file = fs.createWriteStream(outputDir + "/" + filename); https.get(downloadUrl, function (res) { res.pipe(file); res.on("error", function () { reject("request error"); }) res.on("end", function () { file.close(); if (res.statusCode == 200) { // Update the file creation date utimes(file.path, { btime: fileTime.valueOf(), // birthtime (Windows & Mac) mtime: fileTime.valueOf() // modified time (Windows, Mac, Linux) }).then(() => { resolve(true) }).catch(err => { reject("failed to set file creation date:", err) }) } else { reject("status error"); } }); }); }); main(); // Don"t look at this process.on("uncaughtException", function (err) { if (err.code == "ECONNRESET") progress.downloadSucceeded(false); });
错误原因
- 运行环境错误:错误代码
800A3EA是Windows脚本宿主(WSH)的语法错误,说明你用Windows默认的WScript/cscript执行Node.js脚本,而非Node.js运行环境。WSH无法识别const、require等Node.js语法,因此报首行错误。 - 脚本自身语法问题:即使改用Node.js运行,原脚本也存在多处错误:
description方法的字符串包含换行,但未使用模板字符串包裹,导致语法错误- 两个Promise函数中使用
reject但未在构造函数中声明该参数 - 多个变量未用
let/const声明,会成为全局变量 jsonFile被声明为const却被重新赋值,违反常量规则
修复步骤
1. 正确运行脚本
打开命令提示符,进入脚本所在目录,执行:
node main.js
禁止直接双击脚本,否则会默认用WSH打开。
2. 修正脚本语法问题
以下是修复后的完整代码:
// IMPORTS const https = require("https"); const fs = require("fs"); const Queue = require("./concurrency.js"); const Progress = require("./progress.js"); const moment = require("moment"); const utimes = require("utimes").utimes; const { Command } = require("commander"); // PARSE ARGUMENTS const program = new Command(); program .description(`A script to download Snapchat Memories Example: node main.js -c 50 -f ./json/memories_history.json -o Downloads`) .option("-c <number>", "Number of concurrent downloads", 30) .option("-f <path>", "Filepath to memories_history.json", "./json/memories_history.json") .option("-o <directory>", "Download directory", "Downloads"); program.parse(); const options = program.opts(); // CONFIG const outputDir = options.o; const maxConcurrentDownloads = options.c; let jsonFile = (!options.f.startsWith("/") && !options.f.startsWith("./")) ? "./" + options.f : options.f; const progressBarLength = 20; // INIT if (!jsonFile.startsWith("/") && !jsonFile.startsWith("./")) jsonFile = "./" + jsonFile; const downloads = require(jsonFile)["Saved Media"]; const queue = new Queue(maxConcurrentDownloads); const progress = new Progress(downloads.length, progressBarLength); function main() { // Create download directory if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir); // Start downloads for (let i = 0; i < downloads.length; i++) { const [url, body] = downloads[i]["Download Link"].split('?', 2); const fileTime = moment.utc(downloads[i]["Date"], "YYYY-MM-DD HH:mm:ss Z"); const fileName = getFileName(downloads[i], fileTime); // First get CDN download link queue.enqueue(() => getDownloadLink(url, body, fileName, fileTime)) .then((result) => { progress.cdnLinkSucceeded(true); // Download the file queue.enqueue(() => downloadMemory(result[0], result[1], result[2])) .then(() => progress.downloadSucceeded(true)) .catch(() => progress.downloadSucceeded(false)); }) .catch(() => progress.cdnLinkSucceeded(false)); } } function getFileName(download, fileTime) { let filename = fileTime.format("YYYY-MM-DD_HH-mm-ss"); if (download["Media Type"] === "PHOTO") filename += ".jpg"; else if (download["Media Type"] === "VIDEO") filename += ".mp4"; return filename; } const getDownloadLink = (url, body, filename, fileTime) => new Promise((resolve, reject) => { const parsedUrl = new URL(url); const options = { hostname: parsedUrl.hostname, path: parsedUrl.pathname, method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, }; const req = https.request(options, (res) => { let data = ""; res.on("data", (chunk) => { data += chunk; }); res.on("error", () => reject("request error")); res.on("end", () => { if (res.statusCode === 200) { resolve([data, filename, fileTime]); } else { reject("status error"); } }); }); req.write(body); req.end(); }); const downloadMemory = (downloadUrl, filename, fileTime) => new Promise((resolve, reject) => { // Check if there already exists a file with the same name/timestamp if (fs.existsSync(`${outputDir}/${filename}`)) { let duplicates = 1; while (true) { const extensionPos = filename.lastIndexOf('.'); const newFilename = `${filename.substring(0, extensionPos)} (${duplicates})${filename.substring(extensionPos)}`; if (fs.existsSync(`${outputDir}/${newFilename}`)) { duplicates++; } else { filename = newFilename; break; } } } // Create the file and write to it const file = fs.createWriteStream(`${outputDir}/${filename}`); https.get(downloadUrl, (res) => { res.pipe(file); res.on("error", () => reject("request error")); res.on("end", () => { file.close(); if (res.statusCode === 200) { // Update the file creation date utimes(file.path, { btime: fileTime.valueOf(), // birthtime (Windows & Mac) mtime: fileTime.valueOf() // modified time (Windows, Mac, Linux) }).then(() => resolve(true)) .catch(err => reject(`failed to set file creation date: ${err}`)); } else { reject("status error"); } }); }); }); main(); // Don't look at this process.on("uncaughtException", (err) => { if (err.code === "ECONNRESET") progress.downloadSucceeded(false); });
额外说明
- 确保已安装所有依赖包,执行
npm install commander moment utimes(核心依赖) - 确认
concurrency.js和progress.js文件存在于脚本同目录下
内容的提问来源于stack exchange,提问作者Fygar
相关产品推荐
相关产品推荐

