Omnivore Everything

save all browsing history to Omnivore

当前为 2024-06-30 提交的版本,查看 最新版本

// ==UserScript==
// @name         Omnivore Everything
// @namespace    Violentmonkey Scripts
// @version      0.7
// @description  save all browsing history to Omnivore
// @author       fankaidev
// @match        *://*/*
// @exclude      *://omnivore.app/*
// @exclude      *://cubox.pro/*
// @exclude      *://readwise.io/*
// @exclude      *://localhost:*/*
// @exclude      *://127.0.0.1:*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @require      https://cdn.jsdelivr.net/npm/[email protected]/crypto-js.js
// @license MIT
// ==/UserScript==
 
(function () {
  "use strict";
  function uuid() {
    return CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Hex);
  }
 
  let hrefHistory = [];
 
  const omnivoreSchema = {
    saveUrl: `
      mutation SaveUrl($input: SaveUrlInput!) {
        saveUrl(input: $input) {
            ... on SaveSuccess {
                url
                clientRequestId
            }
            ... on SaveError {
                errorCodes
                message
            }
        }
      }`,
    savePage: `
      mutation SavePage($input: SavePageInput!) {
        savePage(input: $input) {
            ... on SaveSuccess {
                url
                clientRequestId
            }
            ... on SaveError {
                errorCodes
                message
            }
        }
      }`,
  };
 
  // Function to get or initialize global state
  function getGlobalState(key, defaultValue) {
    return GM_getValue(key, defaultValue);
  }
 
  // Function to update global state
  function updateGlobalState(key, value) {
    GM_setValue(key, value);
  }
 
  function getApiKey() {
    let apiKey = getGlobalState("omnivoreApiKey", null);
    if (!apiKey) {
      apiKey = prompt("[Omni] Please enter Omnivore API key:", "");
      if (apiKey) {
        updateGlobalState("omnivoreApiKey", apiKey);
      } else {
        console.error("[Omni] No API key provided. Script will not function correctly.");
      }
    }
    return apiKey;
  }
 
  function changeEndpoint() {
    let newApiKey = prompt("[Omni] Enter new Omnivore API key:", getGlobalState("omnivoreApiKey", ""));
    if (newApiKey) {
      updateGlobalState("omnivoreApiKey", newApiKey);
      console.log("[Omni] API key updated to", newApiKey);
    }
  }
 
  GM_registerMenuCommand("Change Omnivore API key", changeEndpoint);
 
 
  function savePage(url) {
    const variables = {
      input: {
        url,
        title: document.title,
        originalContent: document.documentElement.outerHTML,
        source: "chrome",
        clientRequestId: uuid(),
      },
    };
    const apiKey = getApiKey();
    const apiUrl = "https://api-prod.omnivore.app/api/graphql";
    if (!apiKey || !apiUrl) {
      return;
    }
    GM_xmlhttpRequest({
      method: "POST",
      url: apiUrl,
      headers: {
        "Content-Type": "application/json",
        Authorization: apiKey,
      },
      data: JSON.stringify({ query: omnivoreSchema.savePage, variables }),
      onload: function (response) {
        if (response.status === 200) {
          console.log("[Omni] saved page to omnivore");
        } else {
          console.error("[Omni] Failed to save to omnivore", response.responseText);
        }
      },
      onerror: function (error) {
        console.error("Request failed:", error);
      },
    });
  }
 
  function saveUrl(url) {
    const variables = {
      input: {
        url,
        source: "chrome",
        clientRequestId: uuid(),
      },
    };
    const apiKey = getApiKey();
    const apiUrl = "https://api-prod.omnivore.app/api/graphql";
    if (!apiKey || !apiUrl) {
      return;
    }
    GM_xmlhttpRequest({
      method: "POST",
      url: apiUrl,
      headers: {
        "Content-Type": "application/json",
        Authorization: apiKey,
      },
      data: JSON.stringify({ query: omnivoreSchema.saveUrl, variables }),
      onload: function (response) {
        if (response.status === 200) {
          console.log("[Omni] saved url to omnivore");
        } else {
          console.error("[Omni] Failed to save to omnivore", response.responseText);
        }
      },
      onerror: function (error) {
        console.error("Request failed:", error);
      },
    });
  }
 
  function process() {
    const url = window.location.href.split("#")[0];
    if (hrefHistory.includes(url)) {
      console.log("[Omni] skip processed url", url);
      return;
    }
    console.log("[Omni] processing url", url);
    hrefHistory.push(url);
 
    if (document.contentType === 'application/pdf') {
      saveUrl(url)
    } else {
      savePage(url)
    }
  }
 
  function scheduleProcess() {
    if (window.self === window.top) {
      console.log(`[Omni] current href is`, window.location.href);
      setTimeout(() => {
        process();
      }, 5000);
    }
  }
 
  // Intercept pushState and replaceState
  const originalPushState = history.pushState;
  const originalReplaceState = history.replaceState;
  history.pushState = function () {
    originalPushState.apply(this, arguments);
    scheduleProcess();
  };
 
  history.replaceState = function () {
    originalReplaceState.apply(this, arguments);
    scheduleProcess();
  };
  window.addEventListener("load", function () {
    scheduleProcess();
  });
  window.addEventListener("popstate", function (event) {
    scheduleProcess();
  });
 
})();