Skip to content

Instantly share code, notes, and snippets.

@shelllee
Last active June 26, 2025 07:17
Show Gist options
  • Save shelllee/370ee8d78edbd8fc5ca638576463190f to your computer and use it in GitHub Desktop.
Save shelllee/370ee8d78edbd8fc5ca638576463190f to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Wechat Official Accounts Assistant
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Auto load original images and copy information from Wechat Official Accounts articles
// @author Cline
// @match https://mp.weixin.qq.com/s*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
let isLoading = false;
let initialScrollPosition = 0;
let hasCopied = false;
function smoothScrollTo(position) {
return new Promise(resolve => {
window.scrollTo({
top: position,
behavior: 'smooth'
});
const checkScrollEnd = setInterval(() => {
if (Math.abs(window.scrollY - position) < 10) {
clearInterval(checkScrollEnd);
resolve();
}
}, 16);
setTimeout(() => {
clearInterval(checkScrollEnd);
resolve();
}, 1000);
});
}
async function smoothScroll() {
if (isLoading) return;
isLoading = true;
initialScrollPosition = window.scrollY;
const maxScroll = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight
);
window.scrollTo(0, 0);
await new Promise(resolve => setTimeout(resolve, 100));
await smoothScrollTo(maxScroll);
await new Promise(resolve => setTimeout(resolve, 100));
processImages();
await smoothScrollTo(initialScrollPosition);
isLoading = false;
}
function getOriginalImageUrl(url) {
let cleanUrl = url.split('?')[0];
cleanUrl = cleanUrl.replace(/\/\d+$/, '/0');
const wxFmtMatch = url.match(/wx_fmt=([^&]+)/);
const fromMatch = url.match(/[?&]from=([^&]+)/);
const originalFormat = wxFmtMatch ? wxFmtMatch[1] : 'png';
const fromParam = fromMatch ? `&from=${fromMatch[1]}` : '';
return `${cleanUrl}?wx_fmt=${originalFormat}${fromParam}`;
}
function processImages() {
const allImages = document.getElementsByTagName('img');
Array.from(allImages).forEach(img => {
if(img.dataset.processed) {
return;
}
img.dataset.processed = 'true';
const getClosestData = (el, attr) => el.closest(`[${attr}]`)?.getAttribute(attr);
let targetSrc = img.getAttribute('data-croporisrc');
let isFromClosest = false;
if (!targetSrc) {
targetSrc = getClosestData(img, 'data-croporisrc');
if (targetSrc) isFromClosest = true;
}
if (targetSrc) {
const noWatermarkUrl = getOriginalImageUrl(targetSrc);
if (isFromClosest) {
img.closest('[data-croporisrc]').setAttribute('data-croporisrc', noWatermarkUrl);
} else {
img.setAttribute('data-croporisrc', noWatermarkUrl);
}
if (img.hasAttribute('data-src')) {
img.setAttribute('data-src', noWatermarkUrl);
}
img.src = noWatermarkUrl;
} else {
targetSrc = img.getAttribute('data-src');
isFromClosest = false;
if (!targetSrc) {
targetSrc = getClosestData(img, 'data-src');
if (targetSrc) isFromClosest = true;
}
if (targetSrc) {
const noWatermarkUrl = getOriginalImageUrl(targetSrc);
if (isFromClosest) {
img.closest('[data-src]').setAttribute('data-src', noWatermarkUrl);
} else {
img.setAttribute('data-src', noWatermarkUrl);
}
img.src = noWatermarkUrl;
} else if (img.src) {
img.src = getOriginalImageUrl(img.src);
}
}
});
}
function parseDate(dateStr) {
dateStr = dateStr.replace(/\s+/g, '');
const patterns = [
/(\d{4})[-/](\d{1,2})[-/](\d{1,2})/,
/(\d{4})年(\d{1,2})月(\d{1,2})日/,
/(\d{1,2})[-/](\d{1,2})[-/](\d{4})/
];
for (const pattern of patterns) {
const match = dateStr.match(pattern);
if (match) {
let year, month, day;
if (match[1].length === 4) {
[, year, month, day] = match;
} else {
[, month, day, year] = match;
}
month = String(month).padStart(2, '0');
day = String(day).padStart(2, '0');
return `${year}.${month}.${day}`;
}
}
const now = new Date();
return `${now.getFullYear()}.${String(now.getMonth() + 1).padStart(2, '0')}.${String(now.getDate()).padStart(2, '0')}`;
}
// Keep all text content:
// 1. Basic CJK: \u4e00-\u9fff
// 2. CJK Extension A: \u3400-\u4dbf
// 3. CJK Punctuation: \u3000-\u303f
function cleanText(text) {
if (!text) return '';
return text
.trim()
.replace(/[^\u4e00-\u9fff\u3400-\u4dbf\u3000-\u303fA-Za-z0-9\[\]【】\.-]/g, '_')
.replace(/^_+|_+$/g, '')
.replace(/_+/g, '_');
}
function getArticleInfo() {
// Try SSR data first
if (window.__QMTPL_SSR_DATA__) {
const { nick_name, title, publishTime } = window.__QMTPL_SSR_DATA__;
if (nick_name && title) {
return {
accountName: nick_name,
title: title,
publishTime: publishTime || ''
};
}
}
// Fallback to DOM elements
return {
accountName: document.querySelector('#js_name, .profile_nickname')?.textContent.trim() || '',
title: document.querySelector('#activity-name, .rich_media_title')?.textContent.trim() || '',
publishTime: document.querySelector('#publish_time')?.textContent.trim() || ''
};
}
function tryToCopy() {
if (hasCopied) return;
const { accountName, title, publishTime } = getArticleInfo();
if (!accountName || !title) return;
const formattedDate = parseDate(publishTime);
const result = cleanText(`${accountName}_${title}_${formattedDate}`);
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(result).then(() => {
hasCopied = true;
});
}
}
// Initialize
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(smoothScroll, 100));
} else {
setTimeout(smoothScroll, 100);
}
// Check and copy text regularly
setInterval(tryToCopy, 100);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment