其他
需要构建、配置或二次开发 Bouncer…
适用于使用或改造 Bouncer 浏览器扩展,通过本地 WebGPU 或云端 AI 后端按自然语言过滤器隐藏 Twitter/X 不想要帖子的场景。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 40/100 ×0.15
适用场景
需要构建、配置或二次开发 Bouncer特定
解决思路
- 按 Bouncer/ 目录结构定位文件:src/background、src/content、src/popup、src/adapters、src/models、src/utils,以及 manifest.json、package.json、tsconfig.json。
- 从源码安装 Chrome/Edge 版:git clone https://github.com/imbue-ai/bouncer.git;cd bouncer/Bouncer;npm install;npm run build。
- 在 Chrome 加载扩展:打开 chrome://extensions;启用 Developer mode;点击 Load unpacked 并选择 Bouncer/ 文件夹;访问 twitter.com 或 x.com。
- 开发构建:cd Bouncer;npm run dev 进入 watch mode 并热重建。
- 生产构建:npm run build,输出到 Bouncer/dist 或 inline。
- 在扩展 popup 的 Settings 面板配置 AI 后端。
- 选择 Local WebGPU 后端时,可用模型 Qwen3-4B、Qwen3.5-4B、Qwen3.5-4B Vision,无需鉴权。
- 选择 OpenAI 后端时,可用模型 GPT-5 Nano、gpt-oss-20b,需要 API key。
- 选择 Google Gemini 后端时,可用模型 2.5 Flash Lite、2.5 Flash、3 Flash Preview,需要 API key。
- 选择 Anthropic 后端时,可用模型 Claude Haiku 4.5,需要 API key。
- 选择 OpenRouter 后端时,可用模型 Nemotron Nano 12B VL、Ministral 3B,需要 account token。
- 选择 Imbue 后端时使用默认内置模型,无需鉴权。
- API key 存入 chrome.storage.local,绝不硬编码。
- 内容脚本用 MutationObserver 监听 document.body,配置 childList: true、subtree: true。
- 对 mutation.addedNodes 中属于 HTMLElement 的节点调用 extractPost,成功则调用 classifyAndFilter。
- 在 Twitter adapter 中定义 ExtractedPost,字段包括 id、text、authorHandle、imageUrls、element。
- extractPost 中查找 article[data-testid="tweet"],找不到则返回 null。
- 从 [data-testid="tweetText"] 取 textContent 作为 tweetText;从 [data-testid="User-Name"] a 的 href 取作者 handle,并去掉开头的 /。
- 用 img[src*="pbs.twimg.com/media"] 收集 imageUrls。
- 用 article.closest('[data-testid]') 的 data-testid 或 crypto.randomUUID() 生成 id,并把 article 作为 element 返回。
- 定义 ClassificationResult:filtered、matchedCategory、reasoning。
- classifyPost 先调用 buildClassificationPrompt,再调用 model.provider.complete(prompt),最后 parseClassificationResponse。
- buildClassificationPrompt 把 filters、post text、图片数量放入提示词,并要求返回 JSON:filtered、matchedCategory、reasoning。
- hidePost:先把 element.style.transition 设为 opacity 0.3s ease-out,再把 opacity 设为 0,300ms 后 display none,并设置 dataset.bouncerFiltered='true'、dataset.bouncerReason=reason。
- showFilteredIndicator:更新 id 为 bouncer-filtered-count 的元素的文本为 `${count} filtered`。
- 新增 AI provider:在 src/models/providers/myProvider.ts 实现 ModelProvider 的 complete 方法。
- provider 的 complete 用 fetch 请求 endpoint,例如 https://api.myprovider.com/v1/chat/completions,headers 带 Authorization: Bearer apiKey 和 Content-Type: application/json。
- provider 请求体包含 model、messages: [{ role: 'user', content: prompt }]、max_tokens: 256。
- 从 data.choices[0].message.content 取文本,并返回 usage。
- 在 src/models/registry.ts 的 createProvider switch 中注册新 provider,例如 case 'my-provider'。
- 结果缓存:使用 CACHE_KEY='bouncer-post-cache',通过 chrome.storage.local 存取。
- getCachedResult 从 chrome.storage.local.get(CACHE_KEY) 读取 cache,返回 cache[postId] 或 null。
- cacheResult 读取现有 cache,写入 cache[postId]=result;当 keys.length>1000 时删除 keys[0] 以限制大小,再 chrome.storage.local.set。
- Filter 管理:getFilters 从 chrome.storage.sync.get('bouncerFilters') 读取,默认返回空数组。
- addFilter 先 getFilters,若未包含 topic 则 set [...filters, topic]。
- removeFilter 先 getFilters,再 set filters.filter(f => f !== topic)。
- 本地 WebGPU 模型:从 @mlc-ai/web-llm 导入 CreateMLCEngine 和 MLCEngine。
- loadLocalModel 调用 CreateMLCEngine(modelId, { initProgressCallback: report => onProgress?.(report.progress) }) 并保存 engine。
- localComplete 在 engine 为空时抛错,否则用 engine.chat.completions.create({ messages: [{ role: 'user', content: prompt }], max_tokens: 256 }) 返回 content。
- manifest.json 使用 manifest_version: 3。
- permissions 包含 storage、activeTab、scripting。
- host_permissions 包含 https://twitter.com/* 和 https://x.com/*。
- background 使用 service_worker: background.js。
- content_scripts 匹配 twitter/x,注入 content.js,run_at: document_idle。
- 排查扩展未加载:确认 npm run build 成功,并重新加载 unpacked extension。
- 排查帖子未过滤:检查 popup 中 filter 已保存;在 x.com 打开 DevTools 查看 console 错误。
- 排查 API key 错误:确认 key 通过 Settings 面板存储而非硬编码;检查 provider dashboard 配额。
- 排查本地模型不加载:浏览器需支持 WebGPU(chrome://flags/#enable-unsafe-webgpu);首次加载会下载约 2-4GB 模型。
- 排查 filtered count 不更新:MutationObserver 可能已脱离,刷新页面。
- 排查 TypeScript 构建错误:运行 npm install 确保类型完整;检查 tsconfig.json target 为 ES2020+。
- 通用分类过滤流程:classifyAndFilter 先检查 post.element.dataset.bouncerProcessed,已处理则 return;否则设 'true'。
- 再查缓存,若 cached 存在且 filtered,则 hidePost(post.element, cached.reasoning)。
- 然后 getFilters,若为空则 return。
- 再 getModelConfig,调用 classifyPost(post, filters, config)。
- 将结果 cacheResult(post.id, result),若 result.filtered 则 hidePost(post.element, result.reasoning)。
- 安全保存 API key:saveApiKey 用 chrome.storage.local.set({ [`${provider}_api_key`]: key });getApiKey 用 chrome.storage.local.get(`${provider}_api_key`) 读取并默认空字符串;不要 log 或暴露 key。
适用边界 · 注意事项
- 不适用于非 Twitter/X 页面或非 Chrome/Edge 扩展环境;要过滤其他网站需另写 adapter 和 content script。
- 不想引入 Bouncer 扩展、chrome.storage 与 AI 分类调用时,不适合使用此方案。
- 若只能使用本地 WebGPU 模型且浏览器不支持 WebGPU 或无法下载约 2-4GB 模型,则本地方案不适用,可改用云端 provider。