insCNM
// ==UserScript==
// @name insCNM
// @namespace http://tampermonkey.net/
// @version 6.1.14
// @description 获取INS数据!
// @author
// @match https://www.instagram.com/*
// @match https://*/*
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/toastify-js/1.12.0/toastify.min.js
// @resource TOASTIFY_CSS https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css
// @icon https://iili.io/29TPCR1.jpg
// ==/UserScript==
(function () {
'use strict';
// 添加 Toastify 的 CSS 样式
GM_addStyle(GM_getResourceText("TOASTIFY_CSS"));
// 默认设置:在页面加载时显示界面
let showOnLoad = GM_getValue('showOnLoad', true);
// 用于存储菜单命令的 ID
let menuCommandId;
// 更新菜单项的文本
function updateMenuCommand() {
// 注销之前的菜单命令
if (menuCommandId !== undefined) {
GM_unregisterMenuCommand(menuCommandId);
menuCommandId = undefined;
}
// 仅在 Instagram 帖子页面上注册菜单命令
if (isInstagramPostPage()) {
menuCommandId = GM_registerMenuCommand(
(showOnLoad ? '🔴 关闭' : '🟢 打开') + '界面自动显示',
toggleShowOnLoad
);
}
}
function isInstagramPostPage() {
return /^https:\/\/www\.instagram\.com\/(?:[a-zA-Z0-9._]+\/)?(p|reel)\/[a-zA-Z0-9_-]+\/$/.test(window.location.href);
}
// 切换在页面加载时是否显示界面的函数
function toggleShowOnLoad() {
showOnLoad = !showOnLoad;
GM_setValue('showOnLoad', showOnLoad);
updateMenuCommand();
alert('已' + (showOnLoad ? '开启' : '关闭') + '界面自动显示');
}
// 主逻辑
$(document).ready(async function () {
// 初始化菜单命令
updateMenuCommand();
// 如果当前是 Instagram 帖子页面
if (isInstagramPostPage()) {
// 如果用户设置为在页面加载时显示界面
if (showOnLoad) {
initializeInterface();
}
}
// 监听快捷键 Alt+N 来手动打开或关闭界面
document.addEventListener('keydown', function (e) {
if (e.altKey && e.key === 'n') {
const container = $('#instagram-fetcher-ui');
if (container.length) {
container.css({
transition: 'all 0.5s ease',
filter: 'blur(10px)',
opacity: '0'
});
setTimeout(() => container.remove(), 500);
} else {
initializeInterface(true); // 传入参数表示是用户手动触发
}
}
});
async function initializeInterface(triggeredByUser = false) {
if (isInstagramPostPage()) {
const mediaInfo = await fetchMediaInfoByURL(window.location.href);
if (mediaInfo) {
const idValue = mediaInfo.items[0].owner.id;
console.log(`user ID 为:${idValue}`);
await fetchAndDisplayUserInfo(mediaInfo, idValue);
} else {
showMediaInfoUI('<p>获取媒体信息失败。</p>');
}
} else if (triggeredByUser) {
// 如果不是帖子页面,但用户手动触发了界面
showMediaInfoUI('<p>输入网址后按 Enter 搜索。</p>');
}
}
async function fetchAndDisplayUserInfo(mediaInfo, idValue) {
const { taken_at, comment_count, like_count } = mediaInfo.items[0];
const userInfo = await getUserInfo(idValue);
if (userInfo) {
const username = userInfo.user.username;
const followerCount = userInfo.user.follower_count;
const followingCount = userInfo.user.following_count;
const fullName = userInfo.user.full_name;
const profilepicUrl = userInfo.user.hd_profile_pic_url_info.url;
// 使用 GM_xmlhttpRequest 获取头像数据并转换为 Blob URL
let profilepicBlobUrl = '';
try {
profilepicBlobUrl = await getProfilePicBlobUrl(profilepicUrl);
} catch (error) {
console.error('获取头像失败:', error);
// 可以在这里设置一个默认的头像 URL
profilepicBlobUrl = '默认头像的 URL';
}
const mediaInfoHtml = generateMediaInfoHtml({
taken_at,
comment_count,
like_count,
username,
fullName,
followerCount,
followingCount,
profilepicBlobUrl
});
showMediaInfoUI(mediaInfoHtml);
} else {
showMediaInfoUI('<p>获取用户信息失败。</p>');
}
}
function generateMediaInfoHtml({ taken_at, comment_count, like_count, username, fullName, followerCount, followingCount, profilepicBlobUrl }) {
const copyButtonSvg = `
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368" style="vertical-align: middle;">
<path d="M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360Zm0-80h360v-480H360v480ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Zm160-240v-480 480Z"/>
</svg>
`;
return `
<h1 style="text-align: center; color: black; font-size:15px">---帖子信息---</h1>
<p style="margin: 1px;">发布时间: ${new Date(taken_at * 1000).toLocaleString()} <button class="copy-btn" data-copy="${new Date(taken_at * 1000).toLocaleString()}" style="background:none; border:none; cursor:pointer; vertical-align: middle;">${copyButtonSvg}</button></p>
<p style="margin: 1px;">评论数: ${comment_count} <button class="copy-btn" data-copy="${comment_count}" style="background:none; border:none; cursor:pointer; vertical-align: middle;">${copyButtonSvg}</button></p>
<p style="margin: 1px;">点赞数: ${like_count} <button class="copy-btn" data-copy="${like_count}" style="background:none; border:none; cursor:pointer; vertical-align: middle;">${copyButtonSvg}</button></p>
<h1 style="text-align: center; color: black; font-size:15px">---用户信息---</h1>
<p style="margin: 1px;">用户名: ${username} <button class="copy-btn" data-copy="${username}" style="background:none; border:none; cursor:pointer; vertical-align: middle;">${copyButtonSvg}</button></p>
<p style="margin: 1px;">全名: ${fullName} <button class="copy-btn" data-copy="${fullName}" style="background:none; border:none; cursor:pointer; vertical-align: middle;">${copyButtonSvg}</button></p>
<p style="margin: 1px;">粉丝数: ${followerCount} <button class="copy-btn" data-copy="${followerCount}" style="background:none; border:none; cursor:pointer; vertical-align: middle;">${copyButtonSvg}</button></p>
<p style="margin: 1px;">关注数: ${followingCount} <button class="copy-btn" data-copy="${followingCount}" style="background:none; border:none; cursor:pointer; vertical-align: middle;">${copyButtonSvg}</button></p>
<img src="${profilepicBlobUrl}" alt="${username}的头像" style="max-width:100%; border-radius:10px; display: block; margin:10px auto; margin-top:10px; box-shadow: 0px 9px 20px rgba(72, 72, 72, 0.3);" />
`;
}
function showMediaInfoUI(mediaInfoHtml) {
// 检查是否已经存在菜单界面
if ($('#instagram-fetcher-ui').length) {
// 如果存在,更新内容即可
$('#media-info-output').html(mediaInfoHtml);
return;
}
const container = $(
`<div id="instagram-fetcher-ui" style="
position:fixed;
top:20px;
right:-350px;
background: rgba(255, 255, 255, 0.7);
color:black;
border:1px solid #ccc;
padding:5px 10px;
z-index:10000;
width:300px;
border-radius:10px;
backdrop-filter: blur(10px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
">
<div id="drag-handle" style="display: flex; justify-content: center; align-items: center; cursor: move; height: 24px">
<!-- 嵌入 SVG 图标代码 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" width="24px" height="24px" fill="#5f6368">
<path d="M200-360q-17 0-28.5-11.5T160-400q0-17 11.5-28.5T200-440h560q17 0 28.5 11.5T800-400q0 17-11.5 28.5T760-360H200Zm0-160q-17 0-28.5-11.5T160-560q0-17 11.5-28.5T200-600h560q17 0 28.5 11.5T800-560q0 17-11.5 28.5T760-520H200Z"/>
</svg>
</div>
<div id="input-area" style="margin-top:0px; width: 100%;">
<input type="text" id="instagram-url-input" placeholder="输入网址" style="
width: 100%;
padding:5px 10px;
margin: 0;
border: none;
border-radius:5px;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.8);
transition: all 0.3s ease;
outline: none;
color: black;
box-shadow: 0px 8px 15px rgba(72, 72, 72, 0.1);
" />
</div>
<div id="media-info-output" style="
margin-top:5px;
font-size:16px;
text-align: left;
width: 100%;
">${mediaInfoHtml}</div>
</div>`
);
$('body').append(container);
$('#instagram-fetcher-ui').animate({ right: '20px' }, 400);
// 调用函数使菜单可拖动,只针对标题区域
dragElement(document.getElementById("instagram-fetcher-ui"), document.getElementById("drag-handle"));
$('#instagram-url-input').on('mouseenter focus', function () {
$(this).css({
'box-shadow': '0px 9px 20px rgba(72, 72, 72, 0.3)'
});
});
$('#instagram-url-input').on('mouseleave', function () {
if (!$(this).is(':focus')) {
$(this).css({
'box-shadow': '0px 8px 15px rgba(72, 72, 72, 0.1)'
});
}
});
$('#instagram-url-input').on('blur', function () {
$(this).css({
'box-shadow': '0px 8px 15px rgba(72, 72, 72, 0.1)'
});
});
$('#instagram-url-input').on('keyup', async function (e) {
if (e.which === 13) { // 按下回车键
const url = $('#instagram-url-input').val();
console.log("用户输入的URL:", url);
if (url) {
$('#media-info-output').slideUp(200, async function () {
$('#media-info-output').text('正在获取媒体信息...');
$(this).slideDown(200);
});
const mediaInfo = await fetchMediaInfoByURL(url);
if (mediaInfo) {
const idValue = mediaInfo.items[0].owner.id;
console.log(`user ID 为:${idValue}`);
await fetchAndDisplayUserInfo(mediaInfo, idValue);
} else {
$('#media-info-output').slideUp(200, function () {
$('#media-info-output').text('获取媒体信息失败。').slideDown(400);
});
}
}
}
});
$(document).on('click', '.copy-btn', function () {
const textToCopy = $(this).data('copy');
navigator.clipboard.writeText(textToCopy).then(() => {
showNotification("已复制到剪贴板");
}).catch(err => {
console.error('复制失败:', err);
});
});
}
function showNotification(message) {
Toastify({
text: message,
duration: 3000,
close: true,
gravity: 'top',
position: 'center',
style: {
background: getRandomGradientColor(),
color: '#FFFFFF',
borderRadius: '2px',
fontSize: '16px'
},
stopOnFocus: true,
}).showToast();
}
function getRandomGradientColor() {
const gradients = [
'linear-gradient(to right, rgb(0, 176, 155), rgb(150, 201, 61))',
'linear-gradient(to right, rgb(255,95,109), rgb(255,195,133))',
'linear-gradient(135deg, #73a5ff, #5477fs)'
];
return gradients[Math.floor(Math.random() * gradients.length)];
}
async function fetchMediaInfoByURL(inputUrl) {
try {
// 获取页面内容
const pageContent = await fetchPageContent(inputUrl);
// 从页面内容中提取 APP_ID 和 media_id
const appId = getAppID(pageContent);
const mediaId = getMediaIDFromContent(pageContent);
if (!appId || !mediaId) {
console.error("获取 APP_ID 或 Media ID 失败。");
$('#media-info-output').text('获取 APP_ID 或 Media ID 失败,请稍后重试。');
return null;
}
const mediaInfo = await getMediaInfo(mediaId, appId);
console.log("媒体信息:", mediaInfo);
return mediaInfo;
} catch (error) {
console.error("检索媒体信息时出错:", error);
return null;
}
}
// 获取页面内容
async function fetchPageContent(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
headers: {
"User-Agent": window.navigator.userAgent,
"Accept": "text/html"
},
onload: function (response) {
if (response.status === 200) {
resolve(response.responseText);
} else {
reject("获取页面内容失败。");
}
},
onerror: function (err) {
reject(err);
}
});
});
}
// 从页面响应中获取 APP_ID
function getAppID(pageContent) {
const regex = /"APP_ID":"([0-9]+)"/i;
const match = pageContent.match(regex);
if (match && match[1]) {
return match[1];
}
return null;
}
// 获取 media_id
function getMediaIDFromContent(pageContent) {
const regex = /"media_id":"([\w]+)"/;
const match = pageContent.match(regex);
if (match && match[1]) {
return match[1];
}
return null;
}
// 获取媒体信息
async function getMediaInfo(mediaId, appId) {
return new Promise((resolve, reject) => {
let getURL = `https://i.instagram.com/api/v1/media/${mediaId}/info/`;
if (mediaId == null) {
console.error("由于媒体 ID 无效,无法调用媒体 API。");
reject("由于媒体 ID 无效,无法调用媒体 API。");
return;
}
GM_xmlhttpRequest({
method: "GET",
url: getURL,
headers: {
"User-Agent": window.navigator.userAgent,
"Accept": "application/json",
'X-IG-App-ID': appId
},
onload: function (response) {
try {
if (response.finalUrl == getURL) {
let obj = JSON.parse(response.responseText);
resolve(obj);
} else {
let finalURL = new URL(response.finalUrl);
if (finalURL.pathname.startsWith('/accounts/login')) {
console.error("必须登录账户才能访问媒体 API。");
} else {
console.error('无法检索内容,因为 API 被重定向到 "' + response.finalUrl + '"');
}
reject(-1);
}
} catch (error) {
console.error("解析 JSON 响应时出错:", error);
reject(error);
}
},
onerror: function (err) {
reject(err);
}
});
});
}
// 获取用户信息
async function getUserInfo(userId) {
return new Promise((resolve, reject) => {
let getURL = `https://i.instagram.com/api/v1/users/${userId}/info/`;
GM_xmlhttpRequest({
method: "GET",
url: getURL,
headers: {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; Pixel 7 XL) Build/RP1A.20845.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0 Chrome/117.0.5938.60 Mobile Safari/537.36 Instagram 307.0.0.34.111'
},
onload: function (response) {
if (response.status === 200) {
try {
let obj = JSON.parse(response.responseText);
resolve(obj);
} catch (e) {
reject(e);
}
} else {
reject("获取用户信息失败,状态码:" + response.status);
}
},
onerror: function (err) {
reject(err);
}
});
});
}
// 定义 getProfilePicBlobUrl 函数
function getProfilePicBlobUrl(profilepicUrl) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: profilepicUrl,
responseType: 'blob',
onload: function (response) {
const blobUrl = URL.createObjectURL(response.response);
resolve(blobUrl);
},
onerror: function (error) {
reject(error);
}
});
});
}
// 使元素可拖动的函数,只针对特定的 handle
function dragElement(elmnt, handle) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (handle) {
handle.onmousedown = dragMouseDown;
} else {
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// 获取鼠标的初始位置
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// 计算鼠标移动的距离
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// 设置元素的新位置
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// 停止拖动时清除事件监听
document.onmouseup = null;
document.onmousemove = null;
}
}
});
})();