PTT Sites Redirection

Redirects PTT URLs to other sites

目前為 2023-06-27 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        PTT Sites Redirection
// @namespace   Userscript
// @version     0.1.0
// @license     MIT
// @author      CY Fung
// @run-at      document-start
// @grant       GM_registerMenuCommand
//
// @match       https://www.ptt.cc/bbs/*
// @match       https://www.pttweb.cc/*
// @match       https://disp.cc/ptt/*
// @match       https://hotptt.com/*
// @match       https://moptt.tw/p/*
// @match       https://ptthito.com/*
// @match       https://webptt.com/*
// @match       https://pttweb.tw/*
// @match       http://www.ucptt.com/*
// @match       https://www.ucptt.com/*
//
// @match       https://pttent.com/*
// @match       https://pttgame.com/*
// @match       https://pttdigit.com/*
// @match       https://pttlocal.com/*
//
// @description Redirects PTT URLs to other sites
//
// ==/UserScript==

(() => {

  /**
   * To obtain the full list of boards.

https://webptt.com/Hot.html

window.m33t = 'td:first-child a[href*="bbs/"]';
window.ss3 = new Set();

window.n33g = (a)=>a.map(e=>e.textContent).filter(x=>/^[\x20-\xFF]+$/.test(x)).filter(x=>!/[\s\,\|]/.test(x))

setInterval(()=>{
for(const t of window.n33g([...document.querySelectorAll(window.m33t)])) window.ss3.add(t);
},1);

[...ss3.keys()]

[...ss3.keys()].join(',').replace(/(\b.{100,120}\b,)/g,'$1\n')

   *
   * */

  const NO_BUTTON_FOR_CACHER = true;

  const lowerCaseMatcher = (() => {

    const fullBoards = `

ALLPOST,AC_Sale,Accounting,Actuary,ai-photo,AllTogether,AnimalGoods,Anti-ramp,AntiVirus,Aquarium,aqua-shop,Aromatherapy,
Ask,Babymother,bag,Bank_Service,barterbooks,Baseball,beauty,BeautyBody,BeautyMarket,BeautySalon,bicycle,biker,BikerShop,
Boy-Girl,Brand,Browsers,BuyTogether,C_Chat,Cad_Cae,car,cat,CATCH,Chat82gether,chatskill,China-Drama,Clothes,CMWang,
ComicHouse,CompBook,cookclub,creditcard,consumer,CVS,DC,dog,DPP,drama-ticket,DSLR,DV,EatToDie,Education,egg-exchange,
equal_change,e-shopping,Examination,EZsoft,facelift,FamilyCircle,fastfood,feminine_sex,Finance,first-wife,Fitness,Food,
forsale,Free_box,Fund,Gamesale,gardener,gay,GEPT,Getmarry,give,GoodShop,Gossiping,graduate,guitar,hairdo,handmade,
HardwareSale,hate,HelpBuy,HK-drama,homemaker,home-sale,hypermall,IELTS,Insurance,Japan_Travel,japanavgirls,Japandrama,
jeans,joke,joyinDIY,Key_Mou_Pad,Kids,KMT,KoreaDrama,KR_Entertain,L_BeautyCare,License,LicenseShop,Lifeismoney,LightNovel,
Little-Games,LivingGoods,LoL,Lomo,Lottery,MacShop,MakeUp,Mancare,marriage,marvel,memento,MenTalk,Mind,Mix_Match,MJ,
MobileComm,mobilesales,model,movie,multi-lovers,MuscleBeach,NailSalon,NBA,NBA_Film,nb-shopping,nightlife,TOEFL_iBT,
Office,Old-Games,Option,P2PSoftWare,Palmar_Drama,pay_home,PC_Shopping,perfume,Perfume_Shop,pet,photo-buy,PhotoCritic,
PhotoLink,Plant,PttEarnMoney,pttlifelaw,PuzzleDragon,Q_ary,rabbit,Railway,rent-exp,RIPE_gender,sex,share,Loan,sp_teacher,
SportLottery,SportsShop,Stock,Storage_Zone,studyteacher,StupidClown,Rent_apart,Rent_tao,Rent_ya,TaiwanDrama,talk,tax,
Teacher,teaching,Tech_Job,teeth_salon,Test,textbook,third-person,TOEIC,ToS,underwear,Wanted,WomenTalk,WoodworkDIY,
jawawa

  `.replace(/\s/g, '').split(',');

    let lowerCaseMatcher = new Map();
    for (const board of fullBoards) lowerCaseMatcher.set(board.toLowerCase(), board);

    return lowerCaseMatcher;


  })();


  const cacheKey = 'nJ0wg';

  // let m = sessionStorage.setItem(cacheKey


  function onReady() {


    // Extract the current hostname
    let currentHostname = window.location.hostname;

    // Function to handle menu command redirection
    function redirectToPTT(url) {
      sessionStorage.setItem(cacheKey, new URL(url).hostname);
      window.location.href = url;
    }
    function extraction(uObject) {

      // Extract parameters


      /*
       *
  
  supported:
  
      https://www.pttweb.cc/bbs/C_Chat/M.1684501378.A.9F2
  https://moptt.tw/p/C_Chat.M.1684501378.A.9F2
  https://www.ptt.cc/bbs/C_Chat/M.1684501378.A.9F2.html
  https://disp.cc/ptt/C_Chat/1acPSKqZ
  https://ptthito.com/c_chat/m-1684501378-a-9f2/
  https://webptt.com/m.aspx?n=bbs/C_Chat/M.1684501378.A.9F2.html
  https://pttweb.tw/C_Chat/M.1684501378.A.9F2
  http://www.ucptt.com/article/C_Chat/1684501378/9F2
  https://hotptt.com/j3pu5u01ag
  
  supported (no menu option):
  
  https://pttent.com/movie/M.1548573736.A.DD4.html
  https://pttgame.com/lol/M.1476373373.A.239.html
  https://pttdigit.com/macshop/M.1551692551.A.8EA.html
  https://pttlocal.com/tainan/M.1551683492.A.63D.html
  
  not supported:
  
  https://ptt.reviews/HatePolitics/E.w1rS970orKqI
  https://www.ptt666.com/M.1551680529.A.759.html
  
  
  */

      const f = (board) => {
        // lowerCased-board
        if (!board) return board;
        if (board !== board.toLowerCase()) return board;
        return lowerCaseMatcher.get(board) || board.replace(/_/g, '-').replace(/\b([a-z])([a-z0-9]*)\b/g, (_, a, b) => a.toUpperCase() + (b || '')).replace(/-/g, '_');
      }

      const { pathname, hostname } = uObject

      if (pathname.startsWith('/bbs/')) {
        let s = pathname.split('/');

        return {
          board: s[2],
          article: s[3].replace('.html', '')
        }
      } else if (pathname.startsWith('/p/')) {
        let d = pathname.substring(3);
        let i = d.indexOf('.');
        if (i > 0) {
          return {
            board: d.substring(0, i),
            article: d.substring(i + 1)
          }
        }
      } else if (hostname === 'ptthito.com') { // just in case

        let s = pathname.split('/');

        return {
          board: f(s[1]),
          article: s[2].replace(/-/g, '.').toUpperCase()
        }
      } else if (hostname === 'webptt.com') {

        let s = location.search.split('/');

        return {
          board: s[1],
          article: s[2].replace('.html', '')
        }

      } else if (hostname === 'pttweb.tw') {

        let s = pathname.split('/');

        return {
          board: s[1],
          article: s[2].replace('.html', '')
        }
      } else if (hostname === 'www.ucptt.com') {

        let s = pathname.split('/');
        if (s[1] === 'article' && s.length === 5) {

          return {
            board: s[2],
            article: `M.${s[3]}.A.${s[4]}`
          }
        }
      } else if (hostname === 'pttent.com' || hostname === 'pttgame.com' || hostname === 'pttdigit.com') {

        let s = pathname.split('/');

        return {
          board: f(s[1]),
          article: s[2].replace('.html', '')
        }
      }

      return null;
    }


    let objectVariables = null;
    // Handle disp.cc URL conversion
    if (currentHostname === "disp.cc" /*|| currentHostname === "ptthito.com"*/ || currentHostname === 'hotptt.com') {
      let pttLink = document.evaluate("//span[contains(text(),'※ 文章網址: ')]/a", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
      if (pttLink && !pttLink.href.startsWith("https://www.ptt.cc/bbs/")) pttLink = null;
      if (!pttLink) {
        pttLink = document.querySelector('a[href^="https://www.ptt.cc/bbs/"]');
      }
      if (pttLink) {
        let pttURL = pttLink.href;
        if (pttURL && pttURL.startsWith("https://www.ptt.cc/bbs/")) {
          objectVariables = extraction(new URL(pttURL));
        }
      }

    } else {
      objectVariables = extraction(window.location)
    }

    console.log(objectVariables)
    if (!objectVariables || !objectVariables.board || !objectVariables.article) return;
    lowerCaseMatcher.set(objectVariables.board.toLowerCase(), objectVariables.board);


    if (currentHostname !== "www.ptt.cc") {
      GM_registerMenuCommand("Redirect to www.ptt.cc", function () {
        redirectToPTT("https://www.ptt.cc/bbs/" + (objectVariables.board + "/" + objectVariables.article) + ".html");
      });
    }
    if (currentHostname !== "www.pttweb.cc") {
      GM_registerMenuCommand("Redirect to www.pttweb.cc", function () {
        redirectToPTT("https://www.pttweb.cc/bbs/" + objectVariables.board + "/" + objectVariables.article);
      });
    }


    /*
    if (currentHostname !== "disp.cc") {
      GM_registerMenuCommand("Redirect to disp.cc", function () {
        redirectToPTT("https://disp.cc/ptt/" + (objectVariables.board + "/" + objectVariables.article));
      });
    }
    */


    if (currentHostname !== "moptt.tw") {
      GM_registerMenuCommand("Redirect to moptt.tw", function () {
        redirectToPTT("https://moptt.tw/p/" + objectVariables.board + "." + objectVariables.article);
      });
    }

    if (currentHostname !== "ptthito.com") {
      GM_registerMenuCommand("Redirect to ptthito.tw", function () {
        redirectToPTT("https://ptthito.com/" + (objectVariables.board + "/" + objectVariables.article.replace(/\./g, '-') + "/").toLowerCase());
      });

    }

    if (currentHostname !== "webptt.com") {


      GM_registerMenuCommand("Redirect to webptt.com", function () {
        redirectToPTT(`https://webptt.com/m.aspx?n=bbs/${objectVariables.board}/${objectVariables.article}.html`);
      });



    }


    if (currentHostname !== "pttweb.tw") {


      GM_registerMenuCommand("Redirect to pttweb.tw", function () {
        redirectToPTT(`https://pttweb.tw/${objectVariables.board}/${objectVariables.article}`);
      });



    }

    if (currentHostname !== 'www.ucptt.com') {




      GM_registerMenuCommand("Redirect to www.ucptt.com", function () {
        let p = /M\.([^\s.,|\/\\]+)\.A\.([^\s.,|\/\\]+)/.exec(objectVariables.article);
        if (p) {

          redirectToPTT(`http://www.ucptt.com/article/${objectVariables.board}/${p[1]}/${p[2]}`);
        }
      });


    }

    if (currentHostname !== 'pttent.com' && !NO_BUTTON_FOR_CACHER) {


      // not all posts can read
      // https://www.ptt.cc/bbs/Lifeismoney/M.1687762841.A.5E8.html

      GM_registerMenuCommand("Redirect to pttent.com", function () {
        redirectToPTT(`https://pttent.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`);
      });



    }



    if (currentHostname !== 'pttgame.com' && !NO_BUTTON_FOR_CACHER) {

      // not all posts can read
      // https://www.ptt.cc/bbs/Lifeismoney/M.1687762841.A.5E8.html

      GM_registerMenuCommand("Redirect to pttgame.com", function () {
        redirectToPTT(`https://pttgame.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`);
      });



    }


    if (currentHostname !== 'pttdigit.com' && !NO_BUTTON_FOR_CACHER) {

      // not all posts can read
      // https://www.ptt.cc/bbs/Lifeismoney/M.1687762841.A.5E8.html

      GM_registerMenuCommand("Redirect to pttdigit.com", function () {
        redirectToPTT(`https://pttdigit.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`);
      });



    }

    if (currentHostname !== 'pttlocal.com' && !NO_BUTTON_FOR_CACHER) {

      // not all posts can read
      // https://www.ptt.cc/bbs/Lifeismoney/M.1687762841.A.5E8.html

      GM_registerMenuCommand("Redirect to pttlocal.com", function () {
        redirectToPTT(`https://pttlocal.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`);
      });



    }



  }



  if (document.readyState != 'loading') {
    onReady();
  } else {
    window.addEventListener("DOMContentLoaded", onReady, false);
  }



})();