feat: v1.7.9 试用72h+100条额度+到期降级+24h优惠码;v1.7.8 移除图片回复模式

注:本提交由 Gitee v1.7.7 基线重建(原 GitHub 仓库账号暂停、源码包已丢),重建后经真机功能测试与 v1.7.9 行为一致。
This commit is contained in:
personal82555 committed 2026-10-17 22:01:13 +08:00
1 parent fae84ff6c7
commit 540f506c78
8 files changed
+360 -436

No files matched your search

+39 -153
View File
@@ -17,11 +17,9 @@ class XHSNoteSaver {
this.memoryThreshold = 50 * 1024 * 1024; // 内存阈值(50MB)
// 评论模式配置
this.commentMode = 'custom'; // 'custom' / 'text' / 'image' / 'ai'
this.commentMode = 'custom'; // 'custom' / 'text' / 'ai'
this.customComments = []; // 固定评论池
this.currentCommentIndex = 0; // 当前评论索引
this.currentImageIndex = 0; // 当前图片索引(图片回复模式)
this.imageCommentWarned = false; // 图片失败提示只弹一次
this.init();
}
@@ -81,7 +79,7 @@ class XHSNoteSaver {
// 侧边栏修改评论模式时立即生效(无需刷新页面)
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace !== 'local' || !changes.commentMode) return;
const mode = changes.commentMode.newValue;
const mode = changes.commentMode.newValue === 'image' ? 'custom' : changes.commentMode.newValue;
if (mode && mode !== this.commentMode) {
this.commentMode = mode;
console.log('评论模式已同步:', mode);
@@ -96,7 +94,7 @@ class XHSNoteSaver {
try {
const result = await chrome.storage.local.get(['commentMode']);
if (result.commentMode) {
this.commentMode = result.commentMode;
this.commentMode = result.commentMode === 'image' ? 'custom' : result.commentMode;
console.log(`评论模式已恢复: ${this.commentMode}`);
}
} catch (error) {
@@ -1600,27 +1598,9 @@ class XHSNoteSaver {
return;
}
// 4. 输入评论(图片模式改为上传图片)
let sentComment = '';
let sentImage = null;
if (this.commentMode === 'image') {
try {
sentImage = await this.uploadCommentImage();
sentComment = '[图片]';
console.log('评论图片已添加');
} catch (error) {
console.error('图片评论失败:', error);
// 只提示一次,避免每篇笔记都弹
if (!this.imageCommentWarned) {
this.imageCommentWarned = true;
this.showToast('⚠️ 图片评论发送失败:' + error.message + ';建议切换「自定义文字」或「AI」模式');
}
return; // 跳过本篇
}
} else {
sentComment = await this.inputComment();
console.log('评论已输入');
}
// 4. 输入评论
const sentComment = await this.inputComment();
console.log('评论已输入');
// 检查是否被停止
if (!this.isAutoClicking) {
@@ -1633,9 +1613,11 @@ class XHSNoteSaver {
console.log('评论已提交');
// 6. 校验是否发送成功并记录
const posted = await this.verifyCommentPosted(sentComment, this.commentMode === 'image', sentImage);
const posted = await this.verifyCommentPosted(sentComment);
if (posted) {
await this.logCommentSuccess(sentComment, this.commentMode === 'image', sentImage);
await this.logCommentSuccess(sentComment);
// 试用额度计数(授权用户不计);额度用完自动停止
await this.checkUsageQuota();
} else {
console.warn('评论可能未发送成功,不计入记录');
this.showToast('⚠️ 评论可能未发送成功,本篇未记录');
@@ -1860,107 +1842,17 @@ class XHSNoteSaver {
const labels = {
custom: '固定评论池',
text: '自定义文字',
image: '图片回复',
ai: 'AI按笔记生成'
};
return labels[mode] || mode;
}
// 上传评论图片(image模式,多张图片循环发送)
async uploadCommentImage() {
const result = await chrome.storage.local.get(['commentImages']);
const images = result.commentImages || [];
if (images.length === 0) {
throw new Error('尚未在侧边栏上传评论图片');
}
const image = images[this.currentImageIndex % images.length];
this.currentImageIndex++;
console.log(`使用评论图片 (${this.currentImageIndex}/${images.length}):`, image.name || image.id);
// dataURL 转 File
const blob = await (await fetch(image.dataUrl)).blob();
const file = new File([blob], image.name || `comment-${Date.now()}.jpg`, {
type: blob.type || 'image/jpeg'
});
// 查找评论区的图片上传入口
let fileInput = this.findCommentFileInput();
if (!fileInput) {
fileInput = await this.revealCommentFileInput();
}
if (!fileInput) {
throw new Error('网页版评论框没有图片上传入口(小红书仅手机App支持图片评论)');
}
// 注入文件并触发 change 事件
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
fileInput.dispatchEvent(new Event('input', { bubbles: true }));
// 等待图片预览出现
await this.sleep(1500);
console.log('评论图片已注入');
return image;
}
// 在评论区查找文件上传 input
findCommentFileInput() {
const engageBar = document.querySelector('#noteContainer .engage-bar') || document.querySelector('.engage-bar');
if (engageBar) {
const scoped = engageBar.querySelector('input[type="file"]');
if (scoped) return scoped;
}
return document.querySelector('#noteContainer input[type="file"]')
|| document.querySelector('input[type="file"][accept*="image"]');
}
// 点击图片图标按钮,尝试唤出隐藏的 file input
async revealCommentFileInput() {
const engageBar = document.querySelector('#noteContainer .engage-bar') || document.querySelector('.engage-bar');
if (!engageBar) return null;
const candidates = engageBar.querySelectorAll(
'[class*="image"], [class*="img"], [class*="picture"], [class*="upload"], svg[class*="image"], svg[class*="img"]'
);
console.log(`尝试 ${candidates.length} 个候选图片按钮`);
for (const candidate of candidates) {
try {
// 点击图标外层可点击容器(通常 icon 的父级才有事件)
const clickable = candidate.closest('span, div, button, [role="button"]') || candidate;
clickable.click();
await this.sleep(600);
const input = this.findCommentFileInput();
if (input) {
console.log('已唤出文件上传入口:', clickable.className || clickable.tagName);
return input;
}
} catch (error) {
console.log('点击图片按钮失败:', error);
}
}
return null;
}
// 校验评论是否发送成功(提交后延迟检查)
async verifyCommentPosted(commentText, isImage) {
async verifyCommentPosted(commentText) {
await this.sleep(1500);
try {
if (isImage) {
// 图片模式:提交成功后输入区的图片预览会被清空
const preview = document.querySelector(
'#noteContainer .input-box img, .engage-bar .input-box [class*="upload"] img'
);
return !preview;
}
// 文字模式:评论框清空 或 评论区出现该文字,都算成功
// 评论框清空 或 评论区出现该文字,都算成功
const textarea = document.querySelector('#content-textarea');
const textareaEmpty = !textarea || !(textarea.textContent || '').trim();
@@ -1975,8 +1867,31 @@ class XHSNoteSaver {
}
}
// 试用额度:每成功一条让后台计数;allowed=false 时立即停止本轮任务
async checkUsageQuota() {
try {
const res = await chrome.runtime.sendMessage({ action: 'usageTick' });
const d = res && res.data;
if (!d) return;
if (d.allowed === false) {
this.isAutoClicking = false;
this.cleanup();
this.showToast('⛔ ' + (d.message || '体验额度已用完,已自动停止'));
console.log('额度用完,已停止:', d);
return;
}
if (d.state === 'trial' && typeof d.quotaLeft === 'number' && d.quotaLeft > 0 && d.quotaLeft <= 10) {
this.showToast(`试用额度还剩 ${d.quotaLeft} 条`);
} else if (d.state === 'grace' && typeof d.dayLeft === 'number' && d.dayLeft > 0) {
console.log(`体验模式:今日剩余 ${d.dayLeft} 条`);
}
} catch (error) {
console.log('额度检查失败(不阻断):', error);
}
}
// 记录一条成功的评论(写入 storage,侧边栏展示统计与列表)
async logCommentSuccess(commentText, isImage = false, image = null) {
async logCommentSuccess(commentText) {
try {
let url = '';
if (this.currentNoteData && this.currentNoteData.url) {
@@ -2001,20 +1916,10 @@ class XHSNoteSaver {
url,
title: title.slice(0, 100),
comment: (commentText || '').slice(0, 200),
isImage: !!isImage,
mode: this.commentMode,
timestamp: Date.now()
};
// 图片模式:存一张极小的缩略图方便侧边栏展示
if (isImage && image && image.dataUrl) {
try {
record.thumb = await this.makeImageThumb(image.dataUrl, 72);
} catch (error) {
console.log('生成评论缩略图失败:', error);
}
}
const result = await chrome.storage.local.get(['commentLogs']);
const logs = result.commentLogs || [];
logs.unshift(record);
@@ -2026,25 +1931,6 @@ class XHSNoteSaver {
}
}
// 生成小尺寸缩略图(dataURL)
async makeImageThumb(dataUrl, size = 72) {
const img = await new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(image);
image.onerror = reject;
image.src = dataUrl;
});
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
const scale = Math.max(size / img.width, size / img.height);
const w = img.width * scale;
const h = img.height * scale;
ctx.drawImage(img, (size - w) / 2, (size - h) / 2, w, h);
return canvas.toDataURL('image/jpeg', 0.6);
}
// 生成AI评论(简化版,避免重复代码)
async generateAiComment() {
try {
@@ -2359,9 +2245,9 @@ class XHSNoteSaver {
console.log('========================');
}
// 切换评论模式(固定评论池 → 自定义文字 → 图片回复 → AI → 循环)
// 切换评论模式(固定评论池 → 自定义文字 → AI → 循环)
async toggleCommentMode() {
const modes = ['custom', 'text', 'image', 'ai'];
const modes = ['custom', 'text', 'ai'];
const currentIndex = modes.indexOf(this.commentMode);
this.commentMode = modes[(currentIndex + 1) % modes.length];
console.log(`切换评论模式为: ${this.commentMode}`);
@@ -2382,7 +2268,7 @@ class XHSNoteSaver {
updateCommentModeButton() {
const commentModeBtn = document.getElementById('xhs-comment-mode');
if (commentModeBtn) {
const icons = { custom: '📋', text: '📝', image: '🖼️', ai: '🤖' };
const icons = { custom: '📋', text: '📝', ai: '🤖' };
commentModeBtn.textContent = `${icons[this.commentMode] || '💬'} ${this.getCommentModeLabel()}`;
if (this.commentMode === 'ai') {
commentModeBtn.classList.remove('xhs-secondary-btn');