推特获取原图

推特在新标签页打开图片自动原图

설치하기 전에, Greasy Fork는 이 스크립트에 사용자가 아닌 스크립트 작성자의 이익을 위한 기능인 역기능이 포함되어 있음을 알려드립니다.

이 스크립트는 사용자의 브라우징 활동을 추적하는 코드를 포함합니다. 스크립트 작성자의 설명: 本脚本会将您下载的图片信息(图片URL、推文链接、时间)和用户名发送到作者服务器用于个人收藏管理,不会用于其他用途

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         推特获取原图
// @namespace    https://github.com/MuXia-0326/twitter-auto-original-picture
// @version      1.22
// @description  推特在新标签页打开图片自动原图
// @author       Mossia
// @icon         https://raw.githubusercontent.com/MuXia-0326/drawio/master/angri.png
// @match        https://pbs.twimg.com/*
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        GM_setClipboard
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @license      MIT
// @antifeature  tracking 本脚本会将您下载的图片信息(图片URL、推文链接、时间)和用户名发送到作者服务器用于个人收藏管理,不会用于其他用途
// ==/UserScript==

(function () {
  'use strict';

  // 配置项
  const CONFIG_KEY = 'twitter_auto_like';
  const CONFIG_COPY_KEY = 'twitter_copy_replace';
  const CONFIG_SHOW_BTN_KEY = 'twitter_show_settings_btn';
  let autoLike = GM_getValue(CONFIG_KEY, true); // 默认开启自动点赞
  let copyReplace = GM_getValue(CONFIG_COPY_KEY, true); // 默认开启复制链接替换
  let showSettingsBtn = GM_getValue(CONFIG_SHOW_BTN_KEY, true); // 默认显示设置按钮

  let share_url = '';

  let userName = '';

  //载入css样式
  const css = `/* From www.lingdaima.com */
  .twitter-Btn {
    position: relative;
    display: inline-block;
    padding: 10px;
    text-align: center;
    font-size: 18px;
    letter-spacing: 1px;
    text-decoration: none;
    color: rgb(29, 155, 240);
    background: transparent;
    cursor: pointer;
    transition: ease-out 0.5s;
    border: 2px solid rgb(29, 155, 240);
    border-radius: 10px;
    box-shadow: inset 0 0 0 0 rgb(29, 155, 240);
  }

  .twitter-Btn:hover {
    color: white;
    box-shadow: inset 0 -100px 0 0 rgb(29, 155, 240);
  }

  .twitter-Btn:active {
    transform: scale(0.9);
  }

  .twitter-Btn:hover svg {
    fill: white;
  }
  .twitter-Btn:active svg,
  .twitter-Btn svg {
    fill: rgb(29, 155, 240);
  }

  .Btn {
    position: absolute;
    top: 2px;
    right: 2px;
  }
  .svgClass {
    display: flex;
  }

  .share-btn {
    display:none;
  }

  /* 配置面板样式 */
  .config-modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 10000;
  }

  .config-panel {
    background: white;
    border-radius: 16px;
    padding: 24px;
    min-width: 400px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  }

  .config-header {
    font-size: 20px;
    font-weight: bold;
    margin-bottom: 20px;
    color: #0f1419;
  }

  .config-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 0;
    border-bottom: 1px solid #eff3f4;
  }

  .config-label {
    font-size: 15px;
    color: #0f1419;
  }

  .config-description {
    font-size: 13px;
    color: #536471;
    margin-top: 4px;
  }

  .toggle-switch {
    position: relative;
    width: 44px;
    height: 24px;
    background: #cfd9de;
    border-radius: 12px;
    cursor: pointer;
    transition: background 0.3s;
  }

  .toggle-switch.active {
    background: rgb(29, 155, 240);
  }

  .toggle-slider {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    background: white;
    border-radius: 50%;
    transition: transform 0.3s;
  }

  .toggle-switch.active .toggle-slider {
    transform: translateX(20px);
  }

  .config-buttons {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    margin-top: 20px;
  }

  .config-btn {
    padding: 8px 16px;
    border-radius: 20px;
    border: none;
    font-size: 15px;
    font-weight: bold;
    cursor: pointer;
    transition: background 0.2s;
  }

  .config-btn-primary {
    background: rgb(29, 155, 240);
    color: white;
  }

  .config-btn-primary:hover {
    background: rgb(26, 140, 216);
  }

  .config-btn-secondary {
    background: #eff3f4;
    color: #0f1419;
  }

  .config-btn-secondary:hover {
    background: #d7dbdc;
  }

  /* 设置按钮样式 */
  .settings-btn {
    position: fixed;
    bottom: 20px;
    left: 20px;
    width: 56px;
    height: 56px;
    background: rgb(29, 155, 240);
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    z-index: 9999;
    transition: transform 0.2s, box-shadow 0.2s;
  }

  .settings-btn:hover {
    transform: scale(1.1);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  }

  .settings-btn svg {
    fill: white;
  }
  `;

  let styleTag = document.createElement('style');
  styleTag.innerText = css;
  document.head.append(styleTag);

  // 创建配置面板
  function createConfigPanel() {
    const modal = document.createElement('div');
    modal.className = 'config-modal';
    modal.innerHTML = `
      <div class="config-panel">
        <div class="config-header">推特获取原图 - 设置</div>
        <div class="config-item">
          <div>
            <div class="config-label">下载时自动点赞</div>
            <div class="config-description">下载图片时自动为该推文点赞</div>
          </div>
          <div class="toggle-switch ${autoLike ? 'active' : ''}" id="autoLikeToggle">
            <div class="toggle-slider"></div>
          </div>
        </div>
        <div class="config-item">
          <div>
            <div class="config-label">复制链接时替换域名</div>
            <div class="config-description">复制推文链接时将 x.com 替换为 fixupx.com</div>
          </div>
          <div class="toggle-switch ${copyReplace ? 'active' : ''}" id="copyReplaceToggle">
            <div class="toggle-slider"></div>
          </div>
        </div>
        <div class="config-item">
          <div>
            <div class="config-label">显示设置按钮</div>
            <div class="config-description">在页面左下角显示设置按钮(关闭后可通过油猴菜单打开设置)</div>
          </div>
          <div class="toggle-switch ${showSettingsBtn ? 'active' : ''}" id="showBtnToggle">
            <div class="toggle-slider"></div>
          </div>
        </div>
        <div class="config-buttons">
          <button class="config-btn config-btn-secondary" id="configCancel">取消</button>
          <button class="config-btn config-btn-primary" id="configSave">保存</button>
        </div>
      </div>
    `;

    document.body.appendChild(modal);

    // 切换自动点赞开关
    const likeToggle = modal.querySelector('#autoLikeToggle');
    let tempAutoLike = autoLike;
    likeToggle.addEventListener('click', () => {
      tempAutoLike = !tempAutoLike;
      likeToggle.classList.toggle('active', tempAutoLike);
    });

    // 切换复制替换开关
    const copyToggle = modal.querySelector('#copyReplaceToggle');
    let tempCopyReplace = copyReplace;
    copyToggle.addEventListener('click', () => {
      tempCopyReplace = !tempCopyReplace;
      copyToggle.classList.toggle('active', tempCopyReplace);
    });

    // 切换显示按钮开关
    const showBtnToggle = modal.querySelector('#showBtnToggle');
    let tempShowSettingsBtn = showSettingsBtn;
    showBtnToggle.addEventListener('click', () => {
      tempShowSettingsBtn = !tempShowSettingsBtn;
      showBtnToggle.classList.toggle('active', tempShowSettingsBtn);
    });

    // 取消按钮
    modal.querySelector('#configCancel').addEventListener('click', () => {
      modal.remove();
    });

    // 保存按钮
    modal.querySelector('#configSave').addEventListener('click', () => {
      autoLike = tempAutoLike;
      copyReplace = tempCopyReplace;
      const oldShowSettingsBtn = showSettingsBtn;
      showSettingsBtn = tempShowSettingsBtn;

      GM_setValue(CONFIG_KEY, autoLike);
      GM_setValue(CONFIG_COPY_KEY, copyReplace);
      GM_setValue(CONFIG_SHOW_BTN_KEY, showSettingsBtn);

      // 如果设置按钮显示状态改变,更新按钮显示
      if (oldShowSettingsBtn !== showSettingsBtn) {
        const existingBtn = document.querySelector('.settings-btn');
        if (showSettingsBtn && !existingBtn) {
          createSettingsButton();
        } else if (!showSettingsBtn && existingBtn) {
          existingBtn.remove();
        }
      }

      alert('设置已保存!');
      modal.remove();
    });

    // 点击背景关闭
    modal.addEventListener('click', (e) => {
      if (e.target === modal) {
        modal.remove();
      }
    });
  }

  // 创建设置按钮
  function createSettingsButton() {
    const settingsBtn = document.createElement('div');
    settingsBtn.className = 'settings-btn';
    settingsBtn.innerHTML = `
      <svg width="24" height="24" viewBox="0 0 24 24">
        <path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/>
      </svg>
    `;
    settingsBtn.addEventListener('click', createConfigPanel);
    document.body.appendChild(settingsBtn);
  }

  // 注册油猴菜单命令
  GM_registerMenuCommand('⚙️ 设置', createConfigPanel);

  // 获取当前页面的URL
  if (window.location.hostname === 'pbs.twimg.com') {
    let newUrl = replaceImageSizeName(window.location.href);
    if (newUrl !== window.location.href) {
      window.location.href = newUrl;
    }
  } else if (window.location.hostname === 'twitter.com' || window.location.hostname === 'x.com') {
    document.js_nsfw = setInterval(main, 100);
    // 页面加载完成后根据配置决定是否添加设置按钮
    if (showSettingsBtn) {
      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createSettingsButton);
      } else {
        createSettingsButton();
      }
    }
  }

  let url = '';
  let createDate = '';

  function main() {
    if (userName === '') {
      getUserName();
    }

    tweetAdd();
    imageDetailsAdd();
    copy();
    tweetAltAdd();
  }

  function getUserName() {
    let divs = document.querySelectorAll('button[aria-label="账号菜单"]');

    if (divs.length === 0) {
      let mobileDivs = document.querySelectorAll('button[data-testid="DashButton_ProfileIcon_Link"]');
      for (let mobileDiv of mobileDivs) {
        let mobileName = mobileDiv.getAttribute('aria-label');
        userName = mobileName.split(' ')[1];
      }
    }

    for (let div of divs) {
      if (div.children.length === 3) {
        let secondDiv = div.children[1];
        if (secondDiv === null) {
          continue;
        }

        let oneDiv = secondDiv.children[0].children[0];
        let twoDiv = secondDiv.children[0].children[1];

        let likeName = oneDiv.children[0].children[0].children[0].textContent;
        let name = twoDiv.children[0].children[0].children[0].textContent;

        userName = likeName + '(' + name + ')';
      } else if (div.children.length === 1) {
        let oneDiv = div.children[0];
        if (oneDiv === null) {
          continue;
        }

        let nameDiv =
          oneDiv.children[0].children[1].children[0].children[1].children[0].children[0].children[2].children[0].children[1]
            .children[0];
        userName = nameDiv.getAttribute('aria-label');
      }
    }
  }

  function tweetAltAdd() {
    let tweets = document.querySelectorAll('[data-testid="cellInnerDiv"]');

    for (let tweet of tweets) {
      let className = 'div.css-175oi2r.r-rki7wi.r-u8s1d.r-14fd9ze';

      let div = tweet.querySelector(className);
      if (div === null) {
        continue;
      }

      let parent = div.parentNode;
      let links = parent.querySelector('a');
      if (links === null) {
        continue;
      }

      let imageDiv = links.querySelector('img.css-9pa8cd');
      if (imageDiv === null) {
        continue;
      }

      let temp = [...new Set(baseSelectorAlt(parent, 'img.css-9pa8cd'))];
      let like = queryLikeBtn(tweet);

      for (let i = 0; i < temp.length; i++) {
        setAltBtn([temp[i]], like);
      }
    }
  }

  function copy() {
    if (copyReplace) {
      // 替换复制按钮的url
      let firstChildDiv = document.querySelector('div[data-testid="Dropdown"] > div:first-child');

      // 确保第一个子元素是一个 div
      if (firstChildDiv) {
        firstChildDiv.addEventListener('click', function (e) {
          navigator.clipboard
            .readText()
            .then((text) => {
              // console.log('剪贴板的内容:', text);
              if (text.indexOf('fixupx') === -1) {
                // 修改剪贴板的内容
                GM_setClipboard(text.replace(/x/g, 'fixupx'), 'text');
              }
            })
            .catch((err) => {
              console.log('无法读取剪贴板的内容:', err);
            });
        });
      }
    }
  }

  function imageDetailsAdd() {
    // 图片详情页的按钮
    let classDetailsName = 'div[data-testid="swipe-to-dismiss"] div[aria-label="图像"]';

    let tempDetails = [...new Set(baseSelector(document, classDetailsName))];

    for (let i = 0; i < tempDetails.length; i++) {
      setDetailsBtn([tempDetails[i]]);
    }
  }

  function tweetAdd() {
    let tweets = document.querySelectorAll('[data-testid="cellInnerDiv"]');

    for (let tweet of tweets) {
      let time = tweet.querySelector('time');

      let className = 'div[data-testid="tweetPhoto"]';

      let imageDiv = tweet.querySelector(className);
      if (imageDiv === null) {
        continue;
      }

      let temp = [...new Set(baseSelector(tweet, className))];
      let like = queryLikeBtn(tweet);

      for (let i = 0; i < temp.length; i++) {
        setBtn([temp[i]], like, time);
      }
    }
  }

  function queryLikeBtn(tweet) {
    let like = null;
    if (tweet.querySelector('div.css-175oi2r.r-16y2uox.r-1wbh5a2.r-1ny4l3l')) {
      let divs = tweet.querySelector('div.css-175oi2r.r-16y2uox.r-1wbh5a2.r-1ny4l3l');

      let childCount = divs.children.length;
      if (childCount === 3) {
        let div = divs.children[2];
        let lastNum = 3;
        like = div.children[lastNum].querySelector('div').querySelector('div').children[2];
      } else if (childCount === 2) {
        let div = divs.children[1].children[1];
        if (div.children.length === 4) {
          like = div.children[3].querySelector('div').querySelector('div').children[2];
        } else if (div.children.length === 5) {
          like = div.children[4].querySelector('div').querySelector('div').children[2];
        }
      }
    } else if (tweet.querySelector('div.css-175oi2r.r-1iusvr4.r-16y2uox.r-1777fci.r-kzbkwu')) {
      like = tweet
        .querySelector('div.css-175oi2r.r-1iusvr4.r-16y2uox.r-1777fci.r-kzbkwu')
        .children[3].querySelector('div')
        .querySelector('div').children[2];
    }

    return like;
  }

  function baseSelector(parentEle, selector) {
    let items = parentEle.querySelectorAll(selector);
    return Array.from(items).filter((item) => {
      let node = getParentByNum(item, 5).querySelectorAll('div[data-nsfw]');
      return !(node && node.length > 0);
    });
  }

  function baseSelectorAlt(parentEle, selector) {
    let items = parentEle.querySelectorAll(selector);
    return Array.from(items).filter((item) => {
      let node = getParentByNum(item, 5).querySelectorAll('div[data-nsfw]');
      return !(node && node.length > 0);
    });
  }

  function setBtn(node, like, time) {
    for (let container of node) {
      let images = container.querySelectorAll('img');

      for (let image of images) {
        let imageUrl = image.getAttribute('src');
        let classText = image.getAttribute('class') + getRandomIntExclusive(10);

        let buttonHtml = getBtnHtml(classText);

        let parentElement = getParentByNum(image, 5);

        let newUrl = replaceImageSizeName(imageUrl);
        appendBtn(parentElement, newUrl, buttonHtml, classText, like, time);
      }
    }
  }

  function setAltBtn(node, like) {
    for (let images of node) {
      let imageUrl = images.getAttribute('src');
      let classText = images.getAttribute('class') + getRandomIntExclusive(10);

      let buttonHtml = getBtnHtml(classText);

      let parentElement = getParentByNum(images, 5);

      let newUrl = replaceImageSizeName(imageUrl);
      appendBtn(parentElement, newUrl, buttonHtml, classText, like);
    }
  }

  function setDetailsBtn(node) {
    for (let container of node) {
      let images = container.querySelectorAll('img');

      for (let image of images) {
        let imageUrl = image.getAttribute('src');
        let classText = image.getAttribute('class') + getRandomIntExclusive(10);

        let buttonHtml = getBtnHtml(classText);

        let like = getParentByNum(container, 4).nextElementSibling.querySelector('div').querySelector('div').querySelector('div')
          .children[2];

        let newUrl = replaceImageSizeName(imageUrl);
        appendBtn(container, newUrl, buttonHtml, classText, like);
      }
    }
  }

  function getBtnHtml(classText) {
    const buttonHtml = `<div class="Btn">
                <button class="twitter-Btn" id="copy-${classText}">
                    <div class="svgClass">
                        <svg t="1694962361717" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5412" width="20" height="20">
                            <path d="M761.088 715.3152a38.7072 38.7072 0 0 1 0-77.4144 37.4272 37.4272 0 0 0 37.4272-37.4272V265.0112a37.4272 37.4272 0 0 0-37.4272-37.4272H425.6256a37.4272 37.4272 0 0 0-37.4272 37.4272 38.7072 38.7072 0 1 1-77.4144 0 115.0976 115.0976 0 0 1 114.8416-114.8416h335.4624a115.0976 115.0976 0 0 1 114.8416 114.8416v335.4624a115.0976 115.0976 0 0 1-114.8416 114.8416z" p-id="5413" ></path>
                            <path d="M589.4656 883.0976H268.1856a121.1392 121.1392 0 0 1-121.2928-121.2928v-322.56a121.1392 121.1392 0 0 1 121.2928-121.344h321.28a121.1392 121.1392 0 0 1 121.2928 121.2928v322.56c1.28 67.1232-54.1696 121.344-121.2928 121.344zM268.1856 395.3152a43.52 43.52 0 0 0-43.8784 43.8784v322.56a43.52 43.52 0 0 0 43.8784 43.8784h321.28a43.52 43.52 0 0 0 43.8784-43.8784v-322.56a43.52 43.52 0 0 0-43.8784-43.8784z" p-id="5414" ></path>
                        </svg>
                    </div>
                </button>
                <button class="twitter-Btn" id="download-${classText}">
                    <div class="svgClass">
                        <svg t="1694962091616" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4129" id="mx_n_1694962091617" width="20" height="20">
                            <path d="M160 579.2a28.8 28.8 0 0 1 28.8 28.8v170.672c0 30.4 25.664 56.528 59.2 56.528h528c33.536 0 59.2-26.144 59.2-56.528V608a28.8 28.8 0 0 1 57.6 0v170.672c0 63.856-53.12 114.128-116.8 114.128h-528c-63.68 0-116.8-50.272-116.8-114.128V608a28.8 28.8 0 0 1 28.8-28.8z"  p-id="4130"></path><path d="M540.8 176l0 464a28.8 28.8 0 0 1-57.6 0L483.2 176a28.8 28.8 0 0 1 57.6 0z"  p-id="4131"></path>
                            <path d="M331.632 459.632a28.8 28.8 0 0 1 40.736 0l160 160a28.8 28.8 0 0 1-40.736 40.736l-160-160a28.8 28.8 0 0 1 0-40.736z" p-id="4132"></path><path d="M692.368 459.632a28.8 28.8 0 0 0-40.736 0l-160 160a28.8 28.8 0 0 0 40.736 40.736l160-160a28.8 28.8 0 0 0 0-40.736z" p-id="4133"></path>
                        </svg>
                    </div>
                </button>
                <button class="twitter-Btn share-btn" id="share-${classText}">
                    <div class="svgClass">
                        <svg t="1713618483987" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2790" width="20" height="20">
                            <path d="M720.020242 809.16812c0-49.233308 39.919175-89.151459 89.151459-89.151459s89.150436 39.918151 89.150436 89.151459c0 49.227168-39.918151 89.159646-89.150436 89.159646S720.020242 858.397335 720.020242 809.16812zM571.433112 214.824717c0-49.234331 39.919175-89.152483 89.151459-89.152483 49.234331 0 89.152483 39.918151 89.152483 89.152483 0 49.232285-39.918151 89.151459-89.152483 89.151459C611.352287 303.976176 571.433112 264.057001 571.433112 214.824717zM125.674792 675.441443c0-82.07018 66.530252-148.586107 148.585083-148.586107 82.056877 0 148.58713 66.515926 148.58713 148.586107 0 82.071204-66.531276 148.58713-148.58713 148.58713C192.205045 824.028573 125.674792 757.511623 125.674792 675.441443zM66.240145 675.441443c0 114.89375 93.142353 208.027917 208.019731 208.027917 81.402985 0 151.8525-46.752814 186.03809-114.870214l200.360284 35.288714-0.073678 5.28026c0 82.07632 66.531276 148.594293 148.58713 148.594293s148.586107-66.517973 148.586107-148.594293c0-82.072227-66.530252-148.586107-148.586107-148.586107-59.507302 0-110.815875 34.927487-134.554532 85.436858l-195.454554-34.403554c2.059915-11.738345 3.119037-23.839965 3.119037-36.174897 0-54.311976-20.822235-103.748922-54.892191-140.779304l180.740434-180.755784c16.309454 6.152117 33.983999 9.503445 52.454676 9.503445 82.056877 0 148.58713-66.514903 148.58713-148.586107 0-82.07018-66.530252-148.58713-148.58713-148.58713-82.055854 0-148.585083 66.51695-148.585083 148.58713 0 41.674145 17.165961 79.32772 44.792159 106.317421L381.013225 496.92056c-31.211862-18.71934-67.703985-29.499871-106.753349-29.499871C159.382499 467.421712 66.240145 560.549739 66.240145 675.441443z" p-id="2791"></path>
                        </svg>
                    </div>
                </button>
            </div>`;
    return buttonHtml;
  }

  function appendBtn(parentElement, newUrl, buttonHtml, classText, like, time) {
    // 创建按钮元素
    let button = document.createElement('div');
    button.setAttribute('data-nsfw', 'x');
    button.innerHTML = buttonHtml;

    // 按钮点击事件处理程序
    button.querySelector(`#copy-${classText}`).addEventListener('click', () => navigator.clipboard.writeText(newUrl));

    // 发起fetch请求获取图片内容
    button.querySelector(`#download-${classText}`).addEventListener('click', () => {
      // 根据配置决定是否自动点赞
      if (autoLike) {
        let likeDiv = like.querySelector('[data-testid="like"]');
        if (likeDiv) {
          likeDiv.click();
        }
      }

      let url = parentElement.querySelector('a').href.replace(/\/photo\/\d+$/, '');

      let date = new Date(time.getAttribute('datetime'));
      let formattedDate =
        date.getFullYear() +
        '-' +
        String(date.getMonth() + 1).padStart(2, '0') +
        '-' + // 月份从 0 开始,需要加 1
        String(date.getDate()).padStart(2, '0') +
        ' ' +
        String(date.getHours()).padStart(2, '0') +
        ':' +
        String(date.getMinutes()).padStart(2, '0') +
        ':' +
        String(date.getSeconds()).padStart(2, '0');

      fetch(newUrl)
        .then(function (response) {
          if (response.ok) {
            return response.blob(); // 以Blob形式解析响应内容
          } else {
            throw new Error('下载失败');
          }
        })
        .then(function (imageBlob) {
          // 创建一个Blob URL,用于保存图片内容
          var imageUrl = URL.createObjectURL(imageBlob);

          let urlParams = new URL(newUrl);
          // 创建一个下载链接
          var downloadLink = document.createElement('a');
          downloadLink.href = imageUrl;
          downloadLink.download =
            urlParams.pathname.substring(urlParams.pathname.lastIndexOf('/') + 1) + '.' + urlParams.searchParams.get('format');

          // 模拟用户点击下载链接
          downloadLink.click();

          // 释放Blob URL以节省内存
          URL.revokeObjectURL(imageUrl);
        })
        .catch(function (error) {
          console.error('下载失败:', error);
        });

      GM_xmlhttpRequest({
        method: 'POST',
        url: 'https://api.mossia.top/add/xPicture',
        headers: {
          'Content-Type': 'application/json',
        },
        data: JSON.stringify({
          url: url,
          pictureUrl: newUrl,
          xCreateDate: formattedDate,
          createBy: userName,
        }),
        onload: function (response) {
          let result = JSON.parse(response.responseText);
        },
        onerror: function (error) {
          console.error('Request failed:', error);
        },
      });
    });

    // 发起分享图片
    button.querySelector(`#share-${classText}`).addEventListener('click', () => {
      GM_xmlhttpRequest({
        method: 'POST',
        url: share_url, // 目标 URL
        headers: {
          'Content-Type': 'application/json',
        },
        data: JSON.stringify({
          type: 1,
          imageUrl: newUrl,
        }),
        onload: function (response) {
          var responseData = JSON.parse(response.responseText);
          console.log('Received response:', responseData);
        },
        onerror: function (error) {
          console.error('Request failed:', error);
        },
      });
    });

    parentElement.appendChild(button);
  }

  function getParentByNum(element, number) {
    let ancestor = element;
    for (let i = 0; i < number; i++) {
      if (ancestor.parentElement) {
        ancestor = ancestor.parentElement;
      } else {
        break;
      }
    }
    return ancestor;
  }

  /**
   * @param {string} urlString
   */
  function replaceImageSizeName(urlString) {
    // 替换name参数的值为"orig"
    const url = new URL(urlString);
    url.searchParams.set('name', 'orig');
    return url.toString();
  }

  /**
   * @param {number} max
   */
  function getRandomIntExclusive(max) {
    return Math.floor(Math.random() * max);
  }
})();