PTT Sites Redirection

Redirects PTT URLs to other sites

当前为 2023-06-27 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        PTT Sites Redirection
// @namespace   Userscript
// @version     0.1.2
// @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://disp.cc/b/*
// @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/*
//
// @match       https://www.ptt.cc/ask/over18?from=*
//
// @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');




https://illya.tw/data/ptt


window.m33t = 'a[href*="/data/ptt/"] >div >div:first-child';
window.ss3 = new Set();

window.n33g = (a)=>a.map(e=>e.textContent.trim()).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,

Gossiping,Stock,C_Chat,Baseball,NBA,basketballTW,Lifeismoney,HatePolitics,Military,car,Japan_Travel,PC_Shopping,movie,
DIABLO,sex,home-sale,KoreaStar,Tech_Job,LoL,mobilecomm,BabyMother,Beauty,WomenTalk,Boy-Girl,BaseballXXXX,Steam,
creditcard,Kaohsiung,iOS,japanavgirls,joke,PlayStation,AllTogether,SportLottery,KoreaDrama,marvel,HardwareSale,
Japandrama,nswitch,CFantasy,Marginalman,Tainan,Elephants,TaichungBun,e-shopping,Lakers,marriage,Bank_Service,biker,AC_In,
PuzzleDragon,CarShop,CVS,Drama-Ticket,Hsinchu,KR_Entertain,Gamesale,miHoYo,watch,Lions,MacShop,BeautySalon,China-Drama,
ToS,fastfood,Option,mobilesales,MuscleBeach,EAseries,Aviation,Badminton,Salary,BabyProducts,PokemonGO,Tennis,
PublicServan,Soft_Job,E-appliance,FATE_GO,MLB,cat,DigiCurrency,Headphone,Food,Key_Mou_Pad,nb-shopping,studyteacher,
MakeUp,TW_Entertain,DSLR,XBOX,BlueArchive,part-time,YuanChuang,WOW,ONE_PIECE,SuperBike,Wanted,Gov_owned,Broad_Band,
DMM_GAMES,give,KoreanPop,SakaTalk,Audiophile,Examination,FORMULA1,gay,Hip-Hop,Railway,DC_SALE,H-GAME,TaiwanDrama,
StupidClown,Taoyuan,AfterPhD,points,Storage_Zone,Hearthstone,e-coupon,Teacher,TY_Research,UmaMusume,facelift,FITNESS,
hypermall,Preschooler,Road_Running,Acad-Affairs,Accounting,Actuary,AKB48,A-Lin,ALLPOST,A-MEI,AmuroNamie,Android,Angela,
AnimalForest,Announce,Anti-ramp,AOA,AOE,ApexLegends,APINK,Aquarium,Aquarius,ArakawaCow,Arashi,ArenaOfValor,Argentina,
Arknights,Aromatherapy,Arsenal,ASTRO,Atom_Boyz,Ayu,AzurLane,BABYMETAL,BanG_Dream,Battlegirlhs,BattleRoyale,BB-Love,
BB_Online,BDSM,bicycle,bicycleshop,BigBanciao,BIGBANG,BigSanchung,BikerShop,Billiard,BioHazard,BLACKPINK,BLAZERS,
BlizzHeroes,BoardGame,book,Brand,Braves,BrawlStars,BrownDust,Browsers,BTOB,BTS,Bucks,Bundesliga,BuyTogether,camping,
Cancer,CareerPlan,car-pool,CATCH,C_BOO,C_ChatBM,Celtics,Certificate,CFP,ChangHua,Chan_Mou,Cheer,CheerGirlsTW,Chelsea,
Chiayi,China-Star,ChungLi,Civil,ClashRoyale,clmusic,Clothes,Club831,CNBLUE,CN_Entertain,CoCo,Coffee,Coldplay,ComGame-New,
Conan,cookclub,CosmosPeople,Covid-19exp,CPBL_ticket,Crowd,Deserts,Digitalhome,DirectSales,Divorce,Djokovic,Dodgers,dog,
DotA2,drawing,EarthDay2021,Eason,EatToDie,EdSheeran,einvoice,e-seller,EuropeTravel,Eurovision,EXID,EXILE,EXO,Facebook,
Falcom,Fallinlove,FAPL,FBaseball,FBG,FCBarcelona,FCBayern,Federer,feminine_sex,FFXIV,FigureSkate,Finance,FireEmblem,
Folklore,FoodDelivery,Football,ForeignEX,Foreign_Inv,forsale,Free_box,FTISLAND,FTV,Fund,fx,GBF,GemTang,GetMarry,GFRIEND,
GHIBLI,G_I-DLE,GirlsFront,GirlsPlanet,GO,GO_FATE,Golden-Award,GoodPregnan,Google,GOT7,Grad-ProbAsk,graduate,G-S-WARRIORS,
GTA,Guardians,GUNDAM,GunsNRoses,hairdo,HakuiKoyori,Hate,HBL,HelpBuy,Hey_Say_JUMP,HIGHLIGHT,Hiking,Hinatazaka46,HK-movie,
hololive,HomeTeach,Hong_Kong,HOT,Hotel,Hotspur,HsinChuang,HsinTien,Hualien,Hunter,ID_GetBack,IdolMaster,iKON,I-Lan,
Immigration,INFINITE,Instant_Mess,Insurance,Interior,Isayama,ITZY,IU,IVE_STARSHIP,IZONE,Jacky,Jam,Jay,Jeremy_Lin,Jing-Ru,
JinYong,JJ,JLPT,job,Jolin,JP_Entertain,J-PopStation,Juventus,JYPnation,KanColle,KANJANI8,KARA,KarenMok,kartrider,Keelung,
Keyakizaka46,Kings,kodomo,Koei,KOF,Korea_Travel,KR_Bands,LaClippers,LALABA,LArc-en-Ciel,LCD,Leo,lesbian,LE_SSERAFIM,
LGBT_SEX,Libra,LifeRecallBM,Lineage,lineageW,Liverpool,LivingGoods,Loan,Lottery,LoveLive_Sip,MAC,Magic,MAMAMOO,Mancare,
ManCity,ManUtd,MapleStory,Maroon5,Mavericks,MayDay,medstudent,MenTalk,MH,MiamiHeat,Miaoli,Militarylife,Mix_Match,
Mizuki_Nana,MMA,Mobile-game,MobilePay,MOD,model,Mo-Musume,money,Monkeys,Moto_GP,MrChildren,MRT,Nadal,Nantou,NARUTO,
NBA_Fantasy,NBA_Film,NCAA,nCoV2019,nCoVPicket,NCU_Talk,Neihu,Nets,NextTopModel,NFL,NIHONGO,NMIXX,nmsmusic,Nogizaka46,NTU,
NTUcourse,NU-EST,NY-Yankees,Old-Games,Olympics_ISG,ONE_OK_ROCK,OneRepublic,Onmyoji,OTT,outdoorgear,Oversea_Job,OverWatch,
PACERS,Palmar_Drama,Paradox,ParkBoGum,PathofExile,PCReDive,Perfume,Perfume_desu,Philippines,PHX-Suns,PingTung,PLAVE,
PokeMon,popmusic,Post,post-b.test,POWERSTATION,PRODUCE48,PRODUCEX101,Programming,ProjectSekai,PTT25_Game,Ptt25sign,
PttEarnMoney,PUBG,PVC-GK,QuestCenter,RailTimes,Rays,RealMadrid,RealmOfValor,RedSox,RedVelvet,RO,Rockets,SakuraMiko,
Sakurazaka46,SanFrancisco,Scorpio,SENIORHIGH,SET,SEVENTEEN,Shadowverse,S.H.E,SHINee,Shinhwa,shoes,ShoheiOhtani,ShuangHe,
Sijhih,Sixers,sky,SMSlife,Snooker,SNSD,Sodagreen,SongShan,soul,SpongeBob,SportsShop,Spurs,SRW,Starbucks,StarCraft,
stationery,story,streetfight,street_style,StrikeShoot,studyabroad,Suckcomic,SuperHeroes,SuperJunior,SYSOP,tabletennis,
Taitung,talk,Tanya,Taurus,tax,teeth_salon,ThaiDrama,Thailand,Theater,THSRshare,Thunder,TizzyBac,toberich,TOEIC,
TPC_Police,Transfer,TuCheng,TurtleSoup,TVXQ,TW-F-Tennis,TWICE,TXT,TypeMoon,Tyukaitiban,UEFA,underwear,US_STOCK,UTAH-JAZZ,
V6,VALORANT,VAPE,Vietnam,Virgo,VISA,Volleyball,VR,Vtuber,Waa,Wanhua,WannaOne,Warfare,WCDragons,wearefriends,WeiBird,
Wen-Shan,WesternMusic,Windows,WindowsPhone,w-inds,Wine,WINNER,WorkanTravel,WorkinChina,worldbasket,WorldCup,Wrestle,
WuBai_and_CB,X1,XXXXballpark,Yoga_Lin,YUGIOH,YuiAragaki,Yunlin,YuzuruHanyu,Zastrology,Zombie

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

    let lowerCaseMatcher = new Map();
    for (const board of fullBoards) board && typeof board === 'string' && board.length >= 1 && 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(1):
  
    - https://www.ptt.cc/bbs/C_Chat/M.1684501378.A.9F2.html
  
    supported(2):
    - https://www.pttweb.cc/bbs/C_Chat/M.1684501378.A.9F2
    - https://moptt.tw/p/C_Chat.M.1684501378.A.9F2
    - 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
  
    supported(3):
    - https://disp.cc/ptt/C_Chat/1acPSKqZ
    - https://hotptt.com/j3pu5u01ag
  
    supported(4):
    - https://disp.cc/b/ott/cpKM
    - https://disp.cc/b/Gossiping/dyaG
  
    supported(over18):
    - https://www.ptt.cc/ask/over18?from=%2Fbbs%2FGossiping%2FM.1645949964.A.1E4.html
  
    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 === '/ask/over18') {

        let s = /from=([^=\;\/\?]+)/.exec(uObject.search);
        if (s && s[1]) {
          let pathname2 = null;
          try {
            pathname2 = decodeURIComponent(s[1]);
          } catch (e) { }
          if (pathname2) {
            //  https://www.ptt.cc/ask/over18?from=%2Fbbs%2FGossiping%2FM.1645949964.A.1E4.html

            let s = pathname2.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' || hostname === 'pttlocal.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);
  }



})();