如何更优雅、更有趣的刷推特?教你用 AI 做个刷推翻译学习机, 量身定制各种稀奇古怪需求。

推特是 AI 信息源头

推特是获取 AI 资讯的一手渠道。

相关 AI 资讯被翻译,被国内搬运,往往有1-3天左右的延迟间隔。

如果有条件访问推特(现在的 X 平台),建议直接关注 AI 圈国内外大神。

之前整理的国内外 AI 圈 KOL 清单如下:

访问地址: xiangyangqiaomu.feishu.cn/wiki/SQ13wr...

如何更优雅的刷推特

翻译问题

X 面向全球,什么语言都有,虽然自带谷歌翻译。

但一直都没升级为大模型翻译,肉眼可见的错误极多。

如 AI 资讯里,LLM仍被翻译为"法学硕士"。

翻译需要繁琐操作步骤:X 列表页-> 帖子详情页 -> 谷歌翻译 -> 查看翻译结果。

列表内容不翻译,短的帖子页也不给翻译。

如何解决

1. 沉浸式翻译插件

由好友Owen Young开发,是一款口碑极佳的浏览器翻译插件。

下载安装后,针对 X 开启"总是翻译该网站",AI专家选 Twitter翻译增强。

翻译服务选任意免费 LLM 翻译,或自己配置模型 API,或直接开 VIP,效果如下:

沉浸式翻译Chrome插件安装(需魔法) chromewebstore.google.com/detail/%E6%...

2. 自己写一个翻译脚本

听起来好像很难,但现在有 Claude sonnet 3.7 和Grok3 等牛逼AI编程工具。

理想照进现实,不懂编程也能搞,且能完全根据自己需求定制功能:

比如每条推都是一份鲜活的学习语料,让 AI 提取有用的单词和词汇 学英语。

再比如,让 AI 生成帖子回复,无论赞美楼主、发表犀利点评、抑或是吐槽。

分分钟,在AI助力下,社恐变社牛。

看看实际效果:

每个英文帖子插入一个模块,显示中文翻译,回复话术和英语词汇。

如何实现?

要点: 让 AI 写一个油猴脚本,动态读取帖子,调用大模型翻译。

油猴脚本(Tampermonkey)是一个流行的浏览器扩展程序,它允许用户在网页上运行自定义JavaScript代码,从而增强或修改网页功能。

建议用它完全开源的版本,名字也很接近,叫暴力猴(Violentmonkey)

Chrome插件下载地址: chromewebstore.google.com/detail/%E6%...

下载安装后,点击插件图标:

  1. 打开任何网站,查找是否有针对它的有趣脚本。
  2. 点击 + 号,可以创建一个自己的脚本。
  3. 我已经写好的脚本。

脚本开始于跟Grok3的一句对话:

如何写一个油猴脚本,调用Gemini模型翻译当前浏览的X帖子和列表

Grok3的好处,模型能力ok,支持联网,用DeepSearch模式,找到正确的大模型调用方法

拿到把初版代码,再交给 Claude Sonnet 3.7,目前世界上最强的编程 AI。

重述一遍需求背景和已有代码,让它发现问题,优化改进。

之后就是不断地测试,修改,直至让 AI 完成,达到你想要的效果。

比如,实现自定义Prompt,更灵活。

比如,为了让卡片排版美观,实现支持Markdown语法。

再比如,测试发现Gemini API 对并发有限制。

让 AI 参考火山引擎实现代码,把大模型换成 Deepseek。

说到这里,让我也好点薅羊毛。

如果你注册火山引擎,一定填写我的邀请码:3CT2B6KQ

这样,各得 15块 Token 代金券, 因为 AI 编程太消耗Token了。

关于如何用火山引擎接入Deepseek拿API,网上教程很多。

比如可以参考这篇 zhuanlan.zhihu.com/p/248491998...

以下是火山引擎Markdown版本的脚本。

如果不方便复制,可以关注公众号,回复 "脚本",获取所有版本(Gemini和Deepseek)的脚本下载地址。

还有迭代过程产生的各种脚本文件。

javascript 复制代码
// ==UserScript==
// @name         Translate X Post with Volces API (Markdown Support)
// @namespace    http://tampermonkey.net/
// @version      2.6
// @description  Dynamically translate X posts with Markdown support
// @author       向阳乔木
// @match        https://x.com/*
// @grant        GM.xmlHttpRequest
// @grant        GM_addStyle
// @run-at       document-idle
// @require      https://cdn.jsdelivr.net/npm/marked@5.1.2/marked.min.js
// ==/UserScript==

console.log('Script loaded and running on:', window.location.href);

// 样式配置(便于修改)
const STYLES = {
    TRANSLATION_CONTAINER: {
        margin: '10px 0',
        padding: '10px',
        backgroundColor: '#f0f0f0',
        border: '2px solid #ccc',
        borderRadius: '8px', // 圆弧
        fontFamily: 'Arial, sans-serif', // 字体
        fontSize: '16px', // 字号
        lineHeight: '1.5', // 行高
        color: '#000000' // 字体颜色
    }
};

// 添加 Markdown 样式
GM_addStyle(`
    .translation-container h1, .translation-container h2, .translation-container h3,
    .translation-container h4, .translation-container h5, .translation-container h6 {
        margin-top: 10px;
        margin-bottom: 5px;
        font-weight: bold;
    }
    .translation-container h1 { font-size: 1.4em; }
    .translation-container h2 { font-size: 1.3em; }
    .translation-container h3 { font-size: 1.2em; }
    .translation-container h4 { font-size: 1.1em; }
    .translation-container h5, .translation-container h6 { font-size: 1em; }
    .translation-container p { margin: 8px 0; }
    .translation-container ul, .translation-container ol { padding-left: 20px; margin: 8px 0; }
    .translation-container li { margin: 3px 0; }
    .translation-container code { background-color: #e8e8e8; padding: 2px 4px; border-radius: 3px; font-family: monospace; }
    .translation-container pre { background-color: #e8e8e8; padding: 8px; border-radius: 5px; overflow: auto; }
    .translation-container pre code { background-color: transparent; padding: 0; }
    .translation-container blockquote { border-left: 3px solid #ccc; margin: 8px 0; padding-left: 10px; color: #555; }
    .translation-container a { color: #0366d6; text-decoration: none; }
    .translation-container a:hover { text-decoration: underline; }
    .translation-container table { border-collapse: collapse; width: 100%; margin: 10px 0; }
    .translation-container th, .translation-container td { border: 1px solid #ddd; padding: 6px; }
    .translation-container th { background-color: #e0e0e0; }
    .translation-container img { max-width: 100%; height: auto; }
`);

// Prompt 配置(便于优化调整)
const SYSTEM_PROMPT = `你是个超级人工智能助手`;

const USER_PROMPT = `
处理说明:
1. 如果文本不是中文则重写为简体中文,并只返回翻译结果,不包含任何解释或额外信息。
2. 文本是中文,模拟客观友好挖掘亮点的回复,限制50字。
3. 如文本是英文,除翻译外,提炼3个值得学习的单词或词汇,给出每个单词的翻译和解释,限制50字。
要求:分三步处理下面的文本,支持Markdown:

输出格式:
## 🤖 翻译
[翻译内容]

## 🗣️ 回复
[回复内容]

## 📖 词汇
[词汇内容]

处理文本:`;

// API 配置
const API_ENDPOINT = 'https://ark.cn-beijing.volces.com/api/v3/chat/completions';
const API_KEY = '换成你的API';
const MODEL = 'ep-20250222222029-sx6sd';

// 测试 GM.xmlHttpRequest 的独立功能
console.log('Testing GM.xmlHttpRequest');
GM.xmlHttpRequest({
    method: 'GET',
    url: 'https://api.ipify.org?format=json', // 公开的测试 API
    onload: function(response) {
        console.log('GM.xmlHttpRequest test response status:', response.status);
        console.log('GM.xmlHttpRequest test response:', response.responseText);
    },
    onerror: function(error) {
        console.error('GM.xmlHttpRequest test error:', error);
    }
});

// 工具函数:提取纯文本,处理嵌套和特殊字符
function getPlainText(element) {
    if (!element) return '';
    return Array.from(element.childNodes)
        .map(node => {
            if (node.nodeType === Node.TEXT_NODE) return node.textContent.trim();
            if (node.nodeName === 'SPAN' || node.nodeName === 'DIV' || node.nodeName === 'A') return getPlainText(node);
            return '';
        })
        .join(' ')
        .replace(/\s+/g, ' ')
        .trim();
}

// 工具函数:设置翻译结果到元素,支持 Markdown
function setTranslation({ element, translatedText }) {
    if (!element || !translatedText || translatedText === 'Translation failed') return;

    // 检查是否已插入翻译,避免重复
    const existingTranslation = element.nextSibling;
    if (existingTranslation && existingTranslation.className === 'translation-container') {
        console.log('Translation already exists, skipping...');
        return;
    }

    const translationContainer = document.createElement('div');
    translationContainer.className = 'translation-container'; // 添加类名便于识别
    translationContainer.style.cssText = `
        margin: ${STYLES.TRANSLATION_CONTAINER.margin};
        padding: ${STYLES.TRANSLATION_CONTAINER.padding};
        background-color: ${STYLES.TRANSLATION_CONTAINER.backgroundColor};
        border: ${STYLES.TRANSLATION_CONTAINER.border};
        border-radius: ${STYLES.TRANSLATION_CONTAINER.borderRadius};
        font-family: ${STYLES.TRANSLATION_CONTAINER.fontFamily};
        font-size: ${STYLES.TRANSLATION_CONTAINER.fontSize};
        line-height: ${STYLES.TRANSLATION_CONTAINER.lineHeight};
        color: ${STYLES.TRANSLATION_CONTAINER.color};
    `;

    try {
        // 使用 marked 库将 Markdown 转换为 HTML
        if (typeof marked !== 'undefined') {
            // 配置 marked 选项
            marked.setOptions({
                breaks: true, // 启用换行符转换为 <br>
                gfm: true,    // 启用 GitHub 风格的 Markdown
                headerIds: false, // 禁用标题 ID 以避免干扰页面
                mangle: false,    // 禁用电子邮件地址混淆
                sanitize: false,  // 不需要,marked v1.0.0+ 已移除此选项
            });

            // 将 Markdown 转换为 HTML
            translationContainer.innerHTML = marked.parse(translatedText);
        } else {
            // 如果 marked 库未加载,回退到基本的换行处理
            console.warn('Marked library not loaded, falling back to basic formatting');
            translationContainer.innerHTML = translatedText.replace(/\n/g, '<br>');
        }
    } catch (e) {
        console.error('Error rendering Markdown:', e);
        // 出错时回退到基本的换行处理
        translationContainer.innerHTML = translatedText.replace(/\n/g, '<br>');
    }

    // 容错插入逻辑
    const parent = element.parentNode;
    if (parent) {
        const nextSibling = element.nextSibling;
        if (nextSibling) {
            parent.insertBefore(translationContainer, nextSibling);
        } else {
            parent.appendChild(translationContainer);
        }
    } else {
        console.warn('No parent node found for translation insertion');
    }
}

function getPostElements() {
    return document.querySelectorAll('article[data-testid="tweet"], div[data-testid="tweet"]'); // 扩展选择器,兼容可能的变化
}

function getTweetTextElement(tweetElement) {
    const selectors = [
        'div[data-testid="tweetText"]',
        'div.css-146c3p1.r-bcqeeo.r-1ttztb7',
        'span[data-testid="tweet-text"]',
        'div[data-testid="newTweetText"]'
    ];
    for (const selector of selectors) {
        const textElement = tweetElement.querySelector(selector);
        if (textElement) {
            const text = getPlainText(textElement);
            if (text && (text.match(/[a-zA-Z@]/) || text.length > 5)) { // 宽松验证
                console.log('Found tweet text with selector:', selector, 'Text:', text, 'Element:', textElement);
                return { text, element: textElement };
            }
        }
    }
    return { text: 'No post text found', element: null };
}

function translateText(text, originalElement) {
    console.log('Starting translation for text:', text);
    if (!text || text === 'No post text found') {
        console.warn('No valid text to translate');
        return;
    }

    // 组合输入文本
    const combinedInput = `${USER_PROMPT}${text}`;

    // 构建请求体
    const requestBody = {
        model: MODEL,
        messages: [
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": combinedInput}
        ]
    };

    console.log('Sending GM.xmlHttpRequest to:', API_ENDPOINT, 'with body:', JSON.stringify(requestBody));
    GM.xmlHttpRequest({
        method: 'POST',
        url: API_ENDPOINT,
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${API_KEY}`
        },
        data: JSON.stringify(requestBody),
        timeout: 10000, // 保持10秒超时,优化性能
        onload: function(response) {
            console.log('GM.xmlHttpRequest response status:', response.status);
            console.log('Raw response:', response.responseText);
            if (response.status === 200) {
                try {
                    const responseJson = JSON.parse(response.responseText);
                    // 从响应中提取 AI 生成的文本 (适配 Volces API 的返回格式)
                    const translatedText = responseJson.choices?.[0]?.message?.content || 'Translation failed';
                    console.log('Parsed translated text:', translatedText);
                    setTranslation({ element: originalElement, translatedText });
                } catch (e) {
                    console.error('Failed to parse response:', e, 'Raw response:', response.responseText);
                }
            } else {
                console.error('API request failed with status: ' + response.status + ', response: ' + response.responseText);
            }
        },
        onerror: function(error) {
            console.error('GM.xmlHttpRequest error: ', error);
        },
        onabort: function() {
            console.error('GM.xmlHttpRequest aborted');
        },
        ontimeout: function() {
            console.error('GM.xmlHttpRequest timed out');
        }
    });
    console.log('GM.xmlHttpRequest initiated');
}

// 使用 MutationObserver 监听 DOM 变化
function observeTweets() {
    const targetNode = document.querySelector('main') || document.body; // 更具体的目标
    if (!targetNode) {
        console.warn('No main or body element found, observing document.body');
    }

    const observer = new MutationObserver((mutations) => {
        mutations.forEach(mutation => {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE && (node.matches('article[data-testid="tweet"]') || node.matches('div[data-testid="tweet"]'))) {
                        processTweet(node);
                    } else if (node.querySelector) {
                        const tweets = node.querySelectorAll('article[data-testid="tweet"], div[data-testid="tweet"]');
                        tweets.forEach(tweet => processTweet(tweet));
                    }
                });
            }
        });
    });

    observer.observe(targetNode, {
        childList: true,
        subtree: true
    }); // 移除 attributes 和 characterData,减少性能开销

    // 初始处理现有帖子
    console.log('Processing existing tweets...');
    getPostElements().forEach(tweet => {
        setTimeout(() => processTweet(tweet), 500); // 减少延迟到0.5秒,优化加载速度
    });
}

function processTweet(tweetElement, attempt = 0) {
    if (attempt > 2) { // 减少重试次数到2次,优化性能
        console.error('Failed to process tweet after 2 retries');
        return;
    }

    const { text, element } = getTweetTextElement(tweetElement);
    console.log('Processing tweet:', text, 'Attempt:', attempt);

    if (text && element && text !== 'No post text found') {
        translateText(text, element);
    } else {
        console.warn('No valid tweet text found, retrying in 0.5s...', tweetElement);
        setTimeout(() => processTweet(tweetElement, attempt + 1), 500); // 减少重试延迟到0.5秒
    }
}

(function() {
    'use strict';
    console.log('Script starting on:', window.location.href);
    setTimeout(() => {
        console.log('Starting tweet observation after delay, URL:', window.location.href);
        observeTweets();
    }, 1000); // 减少初始延迟到1秒,优化加载速度
})();

感谢支持,写教程不易,欢迎一键三连转发。

跟乔木一起践行学习 AI 时代最需要学的三个技能:编程、英语、写作。

一个人走的块,一群人走的远。

相关推荐
TGITCIC2 小时前
大模型为何无法达到AGI?
ai·大模型·aigc·agi·大模型发展·大模型未来·ai与人
LeeZhao@2 小时前
【AGI】智谱开源2025:一场AI技术民主化的革命正在到来
人工智能·开源·aigc·语音识别·agi
EdisonZhou3 小时前
基于Microsoft.Extensions.AI核心库实现RAG应用
llm·aigc·.net core
牛奶4 小时前
前端学AI:基于Node.js的LangChain开发-知识概念
前端·人工智能·aigc
同学小张5 小时前
Ollama有安全漏洞! 国家网络安全通报中心紧急通报
人工智能·gpt·学习·安全·web安全·aigc·agi
晨航5 小时前
北京大学第四弹:《DeepSeek原理和落地应用》
人工智能·ai·prompt·aigc
Rolei_zl5 小时前
AIGC(生成式AI)试用 26 -- 跟着清华教程学习 - 个人理解
aigc
向阳乔木9 小时前
用扣子Coze搭一个听故事学英语智能体,用上Deepseek R1满血工具版
aigc
紫雾凌寒10 小时前
计算机视觉|从0到1揭秘Diffusion:图像生成领域的新革命
深度学习·计算机视觉·stable diffusion·aigc·文生图·图像分割·diffusion