feat: v1.7.9 试用72h+100条额度+到期降级+24h优惠码;v1.7.8 移除图片回复模式
注:本提交由 Gitee v1.7.7 基线重建(原 GitHub 仓库账号暂停、源码包已丢),重建后经真机功能测试与 v1.7.9 行为一致。
This commit is contained in:
1 parent
fae84ff6c7
commit
540f506c78
8 files changed
+360
-436
No files matched your search
+39
-153
@@ -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');
|
||||
|
||||
Reference in new issue
Block a user