Greasy Fork - Analyze from posted scripts

Shows the total amount for each rating, total/daily installs, and scripts posted on any user profile and search pages.

< Feedback on Greasy Fork - Analyze from posted scripts

Review: Good - script works

§
Posted: 2024-06-23

You haven't checked if the current profile being accessed is the logged-in user's profile. As a result, when the user is logged in, the information of other users cannot be displayed.

Your script is very useful. I used GPT to modify it so that it can download all the scripts of the users. Thank you. Please download the following image and change the file extension to .js. I hope you can optimize it.

// ==UserScript==
// @name         Greasy Fork - 分析发布的脚本增强版本
// @namespace   
// @version      9
// @description  在任何用户资料页和搜索页面显示每种评分的总数、总安装数/每日安装数以及发布的脚本总数。
// @author        hacker09
// @match        https://greasyfork.org/*/users/*
// @match        https://greasyfork.org/*/scripts?q=*
// @match        https://greasyfork.org/*/scripts/by-site/*
// @match        https://greasyfork.org/zh-CN/scripts?set=*
// @icon         https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://greasyfork.org/&size=64
// @require   https://cdn.jsdelivr.net/npm/jszip@3.7.1/dist/jszip.min.js
// ==/UserScript==

(async function() {
  'use strict';
   let link;
if (location.href.match(/org\/.*\/scripts\?set=/)) {
  // 如果 URL 中包含 org/ 任意字符 /scripts?set= 的部分,则条件成立
  console.log('URL 匹配成功',document.querySelector('link[href*= ".json"]').href);
 link = document.querySelector('link[href*=".json"]').href;

} else {
  console.log('URL 匹配失败');
  link = document.querySelector('link[href$=".json"]').href;
}


  const data = await (await fetch(link)).json(); //
    console.log(data);

 var LatestCreated, LatestUpdated, ok, bad, good, DailyTotal, TotalInstalls, ScriptsArray, element, SignedIN = ''; // 创建新变量

  document.head.insertAdjacentHTML('beforeend', '<style>.list-option:not(.list-current) {display: flex; flex-direction: row; align-items: center;} .list-option:not(.list-current) > span {position: relative; left: -7px;} .list-option.list-current > span {position: relative; left: 5px;}</style>'); // 添加一个空间

if (location.href.match(/org\/.*\/scripts\?set=/)) {
element = ".width-constraint:nth-child(2)";
    ScriptsArray = data;

  }else{  if (location.href.match(/org\/.*\/scripts/)) {
    element = ".width-constraint:nth-child(2)";
    ScriptsArray = data;
  } else {
    element = "#user-script-sets-section, #user-script-list-section";
   if (document.querySelector(".sign-in-link") === null) {
    // 如果不存在 .sign-in-link 元素
    var userProfileLinkElement = document.querySelector(".user-profile-link");
     var userProfileLink = userProfileLinkElement.querySelector("a").href;
      var userIdFromLink = getUserIdFromUrl(userProfileLink);
       var userIdFromlocalhelf = getUserIdFromUrl(location.href);
        if ( userIdFromLink === userIdFromlocalhelf) {
        ScriptsArray = data.scripts;
    } else {
      ScriptsArray = data.all_listable_scripts;
    }

} else {
    // 如果存在 .sign-in-link 元素
    ScriptsArray = data.all_listable_scripts;
}
  }

  }
  // 根据当前页面是否是脚本搜索页面以及用户是否已登录,获取当前页面元素



  function getUserIdFromUrl(url) {
    var regex = /users\/(\d+)/;
    var match = regex.exec(url);
    return match ? match[1] : null;
}

  // 计算各项数据
  LatestCreated = new Date(Math.max(...ScriptsArray.map(obj => new Date(obj.created_at))));
  LatestUpdated = new Date(Math.max(...ScriptsArray.map(obj => new Date(obj.code_updated_at))));
  ok = ScriptsArray.map(obj => parseInt(obj.ok_ratings, 10)).reduce((acc, curr) => acc + curr, 0);
  bad = ScriptsArray.map(obj => parseInt(obj.bad_ratings, 10)).reduce((acc, curr) => acc + curr, 0);
  good = ScriptsArray.map(obj => parseInt(obj.good_ratings, 10)).reduce((acc, curr) => acc + curr, 0);
  DailyTotal = ScriptsArray.map(obj => parseInt(obj.daily_installs, 10)).reduce((acc, curr) => acc + curr, 0);
  TotalInstalls = ScriptsArray.map(obj => parseInt(obj.total_installs, 10)).reduce((acc, curr) => acc + curr, 0);

  // 创建数组来存储不同状态的脚本条目HTML
  let notDeletedScripts = [];
let notDeletedCSS = [];
let notDeletedLibs = [];
let deletedHTML = [];

// 生成每个脚本条目的HTML
ScriptsArray.forEach(script => {
  const createdTime = convertToShanghaiTime(script.created_at);
  const updatedTime = convertToShanghaiTime(script.code_updated_at);
  const scriptName = script.code_url.endsWith('.user.js')
    ? `<span style="color: ${script.deleted ? '' : '#FFC0CB'};">【JS】${script.name}</span>`
    : script.code_url.endsWith('.user.css')
    ? `<span style="color: ${script.deleted ? '' : '#0000FF'};">【CSS】${script.name}</span>`
    : `<span style="color: ${script.deleted ? '' : '#FF0000'};">【库】${script.name}</span>`;
  const scriptHTML =`<li>
  <a href="${script.url}" target="_blank" style="${script.deleted ? 'color: grey; text-decoration: line-through;' : ''}">
    <b>${scriptName}</b>
    <span>${script.deleted ? '已删除' : ''}</span>
    <span>创建: ${createdTime}</span>
    <span>更新: ${updatedTime}</span>
    <a href="${script.code_url}" target="_blank" style="color: green;">安装${script.version}</a>
    <a href="${script.code_url}" class="download-link" download="${script.name}">下载</a>
  </a>
</li>
  `;
  if (script.deleted) {
    deletedHTML.push(scriptHTML); // 将删除的脚本条目添加到删除数组
  } else {
    if (script.code_url.endsWith('.user.js')) {
      notDeletedScripts.push({
        html: scriptHTML,
        createdAt: script.created_at
      });
    } else if (script.code_url.endsWith('.user.css')) {
      notDeletedCSS.push({
        html: scriptHTML,
        createdAt: script.created_at
      });
    } else {
      notDeletedLibs.push({
        html: scriptHTML,
        createdAt: script.created_at
      });
    }
  }
});

// 对未删除的脚本按创建时间排序
notDeletedScripts.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
notDeletedCSS.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
notDeletedLibs.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));

// 合并所有脚本条目HTML
const allScriptsHTML = `
  <section>
    <header>
      <h3>所有脚本</h3>
    </header>
    <section class="text-content">
      <ul>
        ${notDeletedScripts.map(script => script.html).join('')}
        ${notDeletedCSS.map(script => script.html).join('')}
        ${notDeletedLibs.map(script => script.html).join('')}
        ${deletedHTML.join('')}
      </ul>
      <button id="downloadAllButton">一键下载所有脚本</button>
    </section>

  </section>
`;
  // 将总计部分和所有脚本条目信息合并在一个<section>中,并将其插入到页面
  document.querySelector(element).insertAdjacentHTML("afterbegin", `
    <section>
        <header>
            <h3>总计</h3>
        </header>
        <section class="text-content">
            <ul>
                <li><b>所有脚本</b>: ${ScriptsArray.length}</li>
                <li><b>每日安装数</b>: ${DailyTotal.toLocaleString()}</li>
                <li><b>总安装数</b>: ${TotalInstalls.toLocaleString()}</li>
                <li><b>总好评数</b>: ${ok.toLocaleString()}</li>
                <li><b>总差评数</b>: ${bad.toLocaleString()}</li>
                <li><b>总好评数</b>: ${good.toLocaleString()}</li>
                <li><b>最新创建时间</b>: ${convertToShanghaiTime(LatestCreated)}</li>
                <li><b>最新更新时间</b>: ${convertToShanghaiTime(LatestUpdated)}</li>
            </ul>
        </section>
        ${allScriptsHTML}
    </section>
  `);

  // 在侧边栏上添加每日安装总数、总安装总数和评分总数
  document.querySelector(".list-option").innerHTML += `<span>(${DailyTotal.toLocaleString()})</span>`;
  document.querySelector(".list-option:nth-child(2)").innerHTML += `<span>(${TotalInstalls.toLocaleString()})</span>`;
  document.querySelector(".list-option:nth-child(3)").innerHTML += `<span>(${parseInt(ok + bad + good).toLocaleString()})</span>`;

  // 转换时间为上海时间
  function convertToShanghaiTime(dateString) {
    const date = new Date(dateString);
    const options = { timeZone: 'Asia/Shanghai', hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' };
    return date.toLocaleString('zh-CN', options);
  }
document.querySelectorAll('.download-link').forEach(link => {
  link.addEventListener('click', function(event) {
    event.preventDefault(); // 阻止默认的点击行为
    const url = this.getAttribute('href');
    const name = this.getAttribute('download');
    downloadScript(url, name);
  });
});
function downloadScript(url, name, zipInstance) {
  fetch(url)
    .then(response => response.blob())
    .then(blob => {
      if (zipInstance) {
        // 如果传入了 zipInstance,则将 blob 添加到压缩包中
        zipInstance.file(name, blob);

        console.log(blob);
      } else {
        // 如果未传入 zipInstance,则直接下载该脚本
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = name; // 设置下载的文件名
        document.body.appendChild(link);
        link.click(); // 模拟点击下载链接
        document.body.removeChild(link); // 下载完成后移除链接元素
      }
    })
    .catch(error => {
      console.error('下载脚本时出错:', error);
    });
}

document.getElementById('downloadAllButton').addEventListener('click', function() {
  // 创建一个新的 JSZip 实例
  var zip = new JSZip();
  const downloadButton = document.getElementById('downloadAllButton');
  downloadButton.textContent = '正在下载...'; // 更改按钮标题
let downloadedCount = 0;
  // 收集所有脚本的下载链接的 Promise 数组
  const downloadPromises = [];

  // 遍历所有脚本
  ScriptsArray.forEach(script => {

    const url = script.code_url;
    const scriptName = script.name;
    let filename;

    // 确定文件名
    if (url.endsWith('.user.css')) {
      filename = `${scriptName}.user.css`;
    } else if (url.endsWith('.user.js')) {
      filename = `${scriptName}.user.js`;
    } else {
      filename = `${scriptName}.js`;
    }

    // 添加下载每个脚本的 Promise
    downloadPromises.push(
      fetch(url)
        .then(response => response.blob())
        .then(blob => {

          zip.file(filename, blob);
          console.log(`Added ${filename} to zip`);
           downloadedCount++;
          downloadButton.textContent = `${downloadedCount}/${ScriptsArray.length}`;
        })
    );
  });

  // 等待所有下载完成
  Promise.all(downloadPromises)
    .then(() => {
      // 所有脚本下载完成后,生成压缩包并下载
      return zip.generateAsync({ type: 'blob' }); // 生成压缩包的 Blob 对象
    })
    .then(blob => {
      // 创建一个虚拟的链接(a标签)
      const link = document.createElement('a');
      link.href = URL.createObjectURL(blob);
    link.download = `${data.name}_${ScriptsArray.length}scripts.zip`;
      document.body.appendChild(link);
      link.click(); // 模拟点击下载链接
      document.body.removeChild(link); // 下载完成后移除链接元素
       downloadButton.textContent = '下载完成'; // 更改按钮标题为下载完成
    })
    .catch(error => {
      console.error('一键下载所有脚本时出错:', error);
    });
});


})();
hacker09Author
§
Posted: 2024-06-25

Thanks for the bug report!

I've fixed that now.

Post reply

Sign in to post a reply.