diff --git a/README.md b/README.md index 1a2ea21..d99e3dc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > 一键搜索 · 自动点击 · 智能评论 —— 小红书运营引流的效率神器 -[![版本](https://img.shields.io/badge/版本-1.7.7-ff2442.svg)](https://dl.88531.cn/xhs.html) +[![版本](https://img.shields.io/badge/版本-1.7.9-ff2442.svg)](https://dl.88531.cn/xhs.html) [![平台](https://img.shields.io/badge/平台-Chrome%20%7C%20Edge-4caf50.svg)](#安装方法) [![语言](https://img.shields.io/badge/语言-纯%20JavaScript-2196f3.svg)](#开发说明) @@ -15,12 +15,11 @@ | 亮点 | 说明 | |---|---| | 🚀 **全流程自动化** | 输入关键词 → 自动开搜 → 自动点开每篇笔记 → 自动写评论 → 自动提交,一条龙跑完 | -| 🤖 **4 种评论方式** | 固定评论池 / 自定义文字库 / **图片回复** / **AI 按笔记生成**,随心切换 | +| 🤖 **3 种评论方式** | 固定评论池 / 自定义文字库 / **AI 按笔记生成**,随心切换 | | 🧠 **AI 智能评论** | 接入任意 OpenAI 兼容接口,每篇笔记结合内容生成不同评论,告别复读机 | -| 🖼️ **图片评论** | 上传表情包/图片,每篇笔记自动发一张,多张循环,互动更自然 | | 📍 **按地区搜索** | 选「上海」「杭州西湖区」自动拼进关键词,精准触达同城流量 | | 📊 **评论数据统计** | 今日/本月/总计成功数一目了然,每条记录带标题+评论内容,**点击直达笔记** | -| 🔐 **免费试用 1 天** | 装完即用,满意再买;插件内支付宝/微信购买,**支付成功自动发卡自动激活** | +| 🔐 **免费试用 3 天 · 100 条** | 72 小时 + 100 条评论额度,先到先停;到期**不锁死**:每天还能体验 3 条、记录全保留,24 小时内下单享限时优惠 | | 📢 **公告推送** | 官方公告实时下发(弹窗/列表),运营消息不错过 | | 🛡️ **稳定可靠** | 元素检测+备用方案、失败自动重试、内存自动防护、页面结构变化可快速适配 | | 🔒 **数据本地存储** | 评论库、图片、记录全部存在你自己的浏览器里,不上传第三方 | @@ -32,10 +31,9 @@ - **💬 评论方式**: - 固定评论池:循环发预设短评(👍 赞!/💯 太棒了!…) - 自定义文字:侧边栏文字库一行一条,循环发送 - - 图片回复:上传图片自动压缩存本地,预览/删除管理(⚠️ 小红书网页版评论不支持图片,仅手机 App 支持,此模式仅供参考) - AI 按笔记生成:提示词可自定义(`{{content}}` 占位符),失败自动降级 - **📊 评论记录**:成功评论自动记录,今日/本月/总计统计,点击记录打开对应笔记 -- **🔐 授权体系**:1 天试用(倒计时精确到秒)、卡密激活、插件内购买自动发卡 +- **🔐 授权体系**:72 小时试用 + 100 条额度(先到先停)、到期降级每天 3 条、卡密激活、插件内购买自动发卡 - **🧪 元素检测**:一键测试页面元素识别情况,方便小红书改版后快速排查 ## 🚀 安装方法 @@ -53,8 +51,8 @@ - 再点一次图标可收起 ### 授权与购买 -1. 安装后自动进入 **1 天免费试用**(侧边栏顶部显示精确到秒的倒计时) -2. 试用结束后需激活:输入卡密点「激活」,或点「购买授权」 +1. 安装后自动进入 **72 小时免费试用**(含 100 条评论额度,侧边栏顶部倒计时精确到秒、额度实时显示) +2. 试用结束后**不硬锁**:自动转为体验模式(评论记录与配置全保留,每天 3 条额度,用完次日恢复);此时点「购买授权」下单自动带 24 小时限时优惠码(`XHS24H`,8 折),也可输入卡密点「激活」 3. 购买流程:点购买 → 选支付宝/微信 → 新页面完成支付 → **支付成功自动发卡并激活生效**,无需手动输卡密 4. 已购用户也可在 [购买中心](https://auth.88531.cn/shop) 用订单号查询卡密,回来手动激活 5. 授权绑定本机浏览器,换设备请联系管理员重置 @@ -66,10 +64,9 @@ > 说明:小红书网页版没有原生地区筛选,地区以关键词形式参与搜索(实际搜索词为「关键词 地区」)。 -### 评论方式(四选一,改完立即生效) +### 评论方式(三选一,改完立即生效) - **固定评论池**:循环发预设短评,不调用模型 - **自定义文字**:文字库一行一条,循环发送 -- **图片回复**:每篇笔记发一张图片,多张循环 - **AI 按笔记生成**:每篇笔记调用模型按内容生成 未配置对应内容时(如选了文字模式但库是空的),选项下方会给出橙色提示。 @@ -107,10 +104,19 @@ ## 📜 版本历史 -当前版本:**1.7.7** +当前版本:**1.7.9**
-展开查看完整变更记录(v1.2.0 ~ v1.7.7) +展开查看完整变更记录(v1.2.0 ~ v1.7.9) + +### 1.7.9 +- **试用策略升级「72 小时 + 100 条」**:时间与条数先到先停——拉长试用必须配用量上限,避免长时间白嫖评论量 +- **到期不再硬锁,改降级体验**:自动转为体验模式,评论记录/文字库/模型配置全部保留,每天仍可发 3 条(用完次日恢复) +- **24 小时限时优惠**:到期后侧边栏出现限时优惠条(码 `XHS24H`,8 折,倒计时精确到秒),一键复制;点「购买授权」下单自动抵扣,优惠码异常时自动按原价下单、绝不挡购买 +- 试用倒计时新增「额度剩 N 条」实时显示;剩余 ≤10 条时页面会提示 + +### 1.7.8 +- **移除图片回复模式**:小红书网页版评论框本身不支持发图片(仅手机 App 支持),该模式一直无法真正发送,已从侧边栏选项、配置面板与发送逻辑中整体移除;评论方式精简为「固定评论池 / 自定义文字 / AI 按笔记生成」三种,旧版本残留的 image 模式会自动回退为固定评论池 ### 1.7.7 - **明确图片评论限制**:小红书网页版评论框不支持发图片(仅手机 App 支持)。图片模式发送失败时页面弹出明确提示(只弹一次),侧边栏选项下方同步橙色警告,建议改用「自定义文字」或「AI」模式 diff --git a/background.js b/background.js index be42fe9..96e1560 100644 --- a/background.js +++ b/background.js @@ -16,8 +16,33 @@ const DEFAULT_AI = { const AUTH_BASE = 'https://auth.88531.cn'; const AUTH_PROJECT_ID = 3; const AUTH_API_KEY = 'ak_bbf83b76df8219388d7f60b9e67c7db2'; -const CLIENT_VERSION = '1.7.7'; -const TRIAL_MS = 24 * 60 * 60 * 1000; // 未授权试用1天 +const CLIENT_VERSION = '1.7.9'; +const TRIAL_MS = 72 * 60 * 60 * 1000; // 试用 72 小时(3 天) +const TRIAL_QUOTA = 100; // 试用期最多 100 条评论(时间/条数先到先停,防止白嫖薅量) +const GRACE_DAILY = 3; // 试用到期不硬锁:每天保留 3 条体验额度 +const PROMO = { code: 'XHS24H', percent: 20, windowMs: 24 * 60 * 60 * 1000 }; // 到期后 24 小时限时优惠(8 折) + +// ===== 试用用量统计(时间/条数额度 + 到期降级都读这里) ===== +function todayStr() { + const d = new Date(); + return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`; +} + +async function getUsage() { + const { trialUsage } = await chrome.storage.local.get(['trialUsage']); + const u = trialUsage && typeof trialUsage === 'object' ? trialUsage : {}; + return { + total: Number(u.total) || 0, + day: u.day || '', + dayCount: Number(u.dayCount) || 0, + quotaHitAt: Number(u.quotaHitAt) || 0, + trialEndedAt: Number(u.trialEndedAt) || 0 + }; +} + +async function saveUsage(u) { + await chrome.storage.local.set({ trialUsage: u }); +} // 把用户填写的地址规范化为完整的 chat/completions 端点 function buildChatCompletionsUrl(baseUrl) { @@ -156,7 +181,7 @@ async function authOnInstalled() { const result = await chrome.storage.local.get(['trialStartedAt']); if (!result.trialStartedAt) { await chrome.storage.local.set({ trialStartedAt: Date.now() }); - console.log('试用期开始(1天)'); + console.log('试用期开始(3 天 · 上限 ' + TRIAL_QUOTA + ' 条)'); } } catch (error) { console.error('初始化试用期失败:', error); @@ -318,7 +343,7 @@ async function authGetAnnouncements() { return { ok: false, message: res.message || '拉取公告失败' }; } -// 读取授权状态(valid授权 > 1天试用 > 锁定) +// 读取授权状态(valid授权 > 72 小时试用 > 到期降级体验,不硬锁) async function authGetStatus() { const { licenseState, trialStartedAt } = await chrome.storage.local.get(['licenseState', 'trialStartedAt']); @@ -338,16 +363,99 @@ async function authGetStatus() { if (!trialStartedAt) { await chrome.storage.local.set({ trialStartedAt: start }); } - const remainingMs = start + TRIAL_MS - Date.now(); - if (remainingMs > 0) { + const usage = await getUsage(); + const trialEndAt = start + TRIAL_MS; + const timeUp = Date.now() >= trialEndAt; + const quotaReached = usage.quotaHitAt > 0; + + // 试用期内:时间没到 且 评论额度没用完 + if (!timeUp && !quotaReached) { return { state: 'trial', - remainingMs, - remainingHours: Math.max(1, Math.ceil(remainingMs / 3600000)), - trialEndAt: start + TRIAL_MS + remainingMs: trialEndAt - Date.now(), + remainingHours: Math.max(1, Math.ceil((trialEndAt - Date.now()) / 3600000)), + trialEndAt, + quotaTotal: TRIAL_QUOTA, + quotaUsed: usage.total, + quotaLeft: Math.max(0, TRIAL_QUOTA - usage.total) }; } - return { state: 'locked' }; + + // 到期(时间到 或 额度用完)→ 降级体验:不硬锁,保留数据 + 每天 3 条 + 24h 限时优惠 + if (!usage.trialEndedAt) { + usage.trialEndedAt = quotaReached && usage.quotaHitAt ? usage.quotaHitAt : trialEndAt; + await saveUsage(usage); + } + const today = todayStr(); + const dayCount = usage.day === today ? usage.dayCount : 0; + const dayLeft = Math.max(0, GRACE_DAILY - dayCount); + const promoLeftMs = Math.max(0, usage.trialEndedAt + PROMO.windowMs - Date.now()); + return { + state: 'grace', + trialEndedAt: usage.trialEndedAt, + graceDaily: GRACE_DAILY, + dayCount, + dayLeft, + quotaTotal: TRIAL_QUOTA, + quotaUsed: usage.total, + promo: { + code: PROMO.code, + percent: PROMO.percent, + promoLeftMs, + active: promoLeftMs > 0 + } + }; +} + +// 每成功一条评论调用一次:试用期扣 100 条额度;到期后扣每日 3 条;授权用户不计 +// 返回 allowed=false 时调用方应立即停止本轮任务 +async function usageTick() { + const { licenseState } = await chrome.storage.local.get(['licenseState']); + if (licenseState && licenseState.valid) { + return { allowed: true, state: 'authorized' }; + } + + const { trialStartedAt } = await chrome.storage.local.get(['trialStartedAt']); + const start = trialStartedAt || Date.now(); + const usage = await getUsage(); + const inTrial = Date.now() < start + TRIAL_MS && !usage.quotaHitAt; + + if (inTrial) { + usage.total += 1; + if (usage.total >= TRIAL_QUOTA && !usage.quotaHitAt) { + usage.quotaHitAt = Date.now(); + usage.trialEndedAt = usage.quotaHitAt; + } + await saveUsage(usage); + const quotaLeft = Math.max(0, TRIAL_QUOTA - usage.total); + return { + allowed: quotaLeft > 0, + state: quotaLeft > 0 ? 'trial' : 'grace', + quotaLeft, + message: quotaLeft > 0 ? '' : + `试用额度 ${TRIAL_QUOTA} 条已用完,已自动停止;到期后每天仍可体验 ${GRACE_DAILY} 条` + }; + } + + // 降级期:每日额度 + const today = todayStr(); + if (usage.day !== today) { + usage.day = today; + usage.dayCount = 0; + } + usage.dayCount += 1; + usage.total += 1; + await saveUsage(usage); + const dayLeft = Math.max(0, GRACE_DAILY - usage.dayCount); + return { + allowed: dayLeft > 0, + state: 'grace', + dayCount: usage.dayCount, + graceDaily: GRACE_DAILY, + dayLeft, + message: dayLeft > 0 ? '' : + `今日 ${GRACE_DAILY} 条体验额度已用完(明天恢复),购买授权解锁无限量` + }; } // 创建购买订单(contact_info 带机器码 → 支付成功后系统自动发卡绑定) @@ -362,26 +470,42 @@ async function authCreateOrder(payType) { const cardType = typesRes.data[0]; const machineId = await getDeviceId(); - const res = await authFetch('/api/public/orders', { - method: 'POST', - body: { - project_id: AUTH_PROJECT_ID, - card_type_id: cardType.id, - amount: Number(cardType.price), - pay_type: pay, - contact_info: `MACHINE:${machineId}` - } + const price = Number(cardType.price); + // 试用到期 24 小时内下单自动带限时优惠码(服务端按折扣重算实付金额) + const status = await authGetStatus(); + const useCoupon = !!(status.state === 'grace' && status.promo && status.promo.active); + const orderBody = () => ({ + project_id: AUTH_PROJECT_ID, + card_type_id: cardType.id, + amount: price, + pay_type: pay, + contact_info: `MACHINE:${machineId}`, + ...(useCoupon ? { coupon_code: PROMO.code } : {}) }); - + let res = await authFetch('/api/public/orders', { method: 'POST', body: orderBody() }); + let couponApplied = useCoupon; + if (!res.ok && useCoupon) { + // 优惠码异常也不能挡住下单:去掉优惠码重试一次 + console.log('优惠码下单失败,改用原价重试:', res.message); + const retryBody = orderBody(); + delete retryBody.coupon_code; + res = await authFetch('/api/public/orders', { method: 'POST', body: retryBody }); + couponApplied = false; + } + if (!res.ok || !res.data) { return { ok: false, message: res.message || '创建订单失败' }; } - // 记录进行中的订单,便于侧边栏恢复轮询 + // 记录进行中的订单,便于侧边栏恢复轮询(金额优先用服务端实付金额) + const serverPay = res.data.pay_amount != null ? Number(res.data.pay_amount) : null; + const fallbackPay = couponApplied ? Math.max(0.01, Math.round(price * (1 - PROMO.percent / 100) * 100) / 100) : price; const pendingOrder = { orderNo: res.data.order_no, payType: pay, - amount: Number(cardType.price), + amount: serverPay != null ? serverPay : fallbackPay, + originalPrice: price, + couponCode: couponApplied ? PROMO.code : '', packageName: cardType.name || '', createdAt: Date.now() }; @@ -451,6 +575,11 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { return true; } + if (request.action === 'usageTick') { + usageTick().then((data) => sendResponse({ ok: true, data })); + return true; + } + if (request.action === 'authActivate') { authVerify(request.cardKey).then((res) => sendResponse(res)); return true; diff --git a/content.js b/content.js index cf224e4..a314ba2 100644 --- a/content.js +++ b/content.js @@ -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'); diff --git a/manifest.json b/manifest.json index 9ea56eb..bad9533 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "小红书自动评论助手", - "version": "1.7.7", + "version": "1.7.9", "description": "右侧侧边栏常驻的自动评论助手,支持试用/卡密授权、固定池/自定义文字/图片/AI评论", "permissions": [ "storage", diff --git a/privacy.html b/privacy.html index d737579..0a11b91 100644 --- a/privacy.html +++ b/privacy.html @@ -16,7 +16,7 @@

小红书自动评论助手 · 隐私政策

-
生效日期:2026年10月10日 · 适用版本:v1.7.7 及以上
+
生效日期:2026年10月10日 · 适用版本:v1.7.9 及以上

感谢使用「小红书自动评论助手」浏览器扩展(以下简称"本插件")。我们重视你的隐私。本页面说明本插件收集哪些信息、如何使用以及如何保护。

@@ -32,7 +32,6 @@ 以下数据仅保存在你自己的浏览器中(chrome.storage.local),不会上传到任何服务器: