PTT Sites Redirection

Redirects PTT URLs to other sites

  1. // ==UserScript==
  2. // @name PTT Sites Redirection
  3. // @namespace Userscript
  4. // @version 0.1.11
  5. // @license MIT
  6. // @author CY Fung
  7. // @run-at document-start
  8. // @grant GM_registerMenuCommand
  9. // @grant GM.setValue
  10. // @grant GM.getValue
  11. // @grant GM.deleteValue
  12. // @grant GM_addValueChangeListener
  13. //
  14. // @match https://www.ptt.cc/bbs/*
  15. // @match https://www.pttweb.cc/*
  16. // @match https://disp.cc/ptt/*
  17. // @match https://disp.cc/b/*
  18. // @match https://hotptt.com/*
  19. // @match https://moptt.tw/p/*
  20. // @match https://ptthito.com/*
  21. // @match https://webptt.com/*
  22. // @match https://pttweb.tw/*
  23. // @match http://www.ucptt.com/*
  24. // @match https://www.ucptt.com/*
  25. //
  26. // @match https://pttent.com/*
  27. // @match https://pttgame.com/*
  28. // @match https://pttdigit.com/*
  29. // @match https://pttlocal.com/*
  30. // @match https://pttcomic.com/*
  31. //
  32. // @match https://webptt.findrate.tw/bbs/*
  33. //
  34. // @match https://www.ptt.cc/ask/over18?from=*
  35. //
  36. // @description Redirects PTT URLs to other sites
  37. //
  38. // ==/UserScript==
  39.  
  40. (() => {
  41.  
  42.  
  43. /**
  44. * To obtain the full list of boards.
  45.  
  46.  
  47.  
  48. https://webptt.com/Hot.html
  49.  
  50.  
  51. window.m33t = 'td:first-child a[href*="bbs/"]';
  52. window.ss3 = new Set();
  53.  
  54. window.n33g = (a)=>a.map(e=>e.textContent).filter(x=>/^[\x20-\xFF]+$/.test(x)).filter(x=>!/[\s\,\|]/.test(x))
  55.  
  56. setInterval(()=>{
  57. for(const t of window.n33g([...document.querySelectorAll(window.m33t)])) window.ss3.add(t);
  58. },1);
  59.  
  60. [...ss3.keys()];
  61.  
  62. [...ss3.keys()].join(',').replace(/(\b.{100,120}\b,)/g,'$1\n');
  63.  
  64.  
  65.  
  66.  
  67. https://illya.tw/data/ptt
  68.  
  69.  
  70. window.m33t = 'a[href*="/data/ptt/"] >div >div:first-child';
  71. window.ss3 = new Set();
  72.  
  73. window.n33g = (a)=>a.map(e=>e.textContent.trim()).filter(x=>/^[\x20-\xFF]+$/.test(x)).filter(x=>!/[\s\,\|]/.test(x))
  74.  
  75. setInterval(()=>{
  76. for(const t of window.n33g([...document.querySelectorAll(window.m33t)])) window.ss3.add(t);
  77. },1);
  78.  
  79. [...ss3.keys()];
  80.  
  81. [...ss3.keys()].join(',').replace(/(\b.{100,120}\b,)/g,'$1\n');
  82.  
  83.  
  84.  
  85. *
  86. * */
  87.  
  88. const NO_BUTTON_FOR_CACHER = true;
  89.  
  90. const lowerCaseMatcher = (() => {
  91.  
  92. const fullBoards = `
  93.  
  94. ALLPOST,AC_Sale,Accounting,Actuary,ai-photo,AllTogether,AnimalGoods,Anti-ramp,AntiVirus,Aquarium,aqua-shop,Aromatherapy,
  95. Ask,Babymother,bag,Bank_Service,barterbooks,Baseball,beauty,BeautyBody,BeautyMarket,BeautySalon,bicycle,biker,BikerShop,
  96. Boy-Girl,Brand,Browsers,BuyTogether,C_Chat,Cad_Cae,car,cat,CATCH,Chat82gether,chatskill,China-Drama,Clothes,CMWang,
  97. ComicHouse,CompBook,cookclub,creditcard,consumer,CVS,DC,dog,DPP,drama-ticket,DSLR,DV,EatToDie,Education,egg-exchange,
  98. equal_change,e-shopping,Examination,EZsoft,facelift,FamilyCircle,fastfood,feminine_sex,Finance,first-wife,Fitness,Food,
  99. forsale,Free_box,Fund,Gamesale,gardener,gay,GEPT,Getmarry,give,GoodShop,Gossiping,graduate,guitar,hairdo,handmade,
  100. HardwareSale,hate,HelpBuy,HK-drama,homemaker,home-sale,hypermall,IELTS,Insurance,Japan_Travel,japanavgirls,Japandrama,
  101. jeans,joke,joyinDIY,Key_Mou_Pad,Kids,KMT,KoreaDrama,KR_Entertain,L_BeautyCare,License,LicenseShop,Lifeismoney,LightNovel,
  102. Little-Games,LivingGoods,LoL,Lomo,Lottery,MacShop,MakeUp,Mancare,marriage,marvel,memento,MenTalk,Mind,Mix_Match,MJ,
  103. MobileComm,mobilesales,model,movie,multi-lovers,MuscleBeach,NailSalon,NBA,NBA_Film,nb-shopping,nightlife,TOEFL_iBT,
  104. Office,Old-Games,Option,P2PSoftWare,Palmar_Drama,pay_home,PC_Shopping,perfume,Perfume_Shop,pet,photo-buy,PhotoCritic,
  105. PhotoLink,Plant,PttEarnMoney,pttlifelaw,PuzzleDragon,Q_ary,rabbit,Railway,rent-exp,RIPE_gender,sex,share,Loan,sp_teacher,
  106. SportLottery,SportsShop,Stock,Storage_Zone,studyteacher,StupidClown,Rent_apart,Rent_tao,Rent_ya,TaiwanDrama,talk,tax,
  107. Teacher,teaching,Tech_Job,teeth_salon,Test,textbook,third-person,TOEIC,ToS,underwear,Wanted,WomenTalk,WoodworkDIY,
  108. jawawa,
  109.  
  110. Gossiping,Stock,C_Chat,Baseball,NBA,basketballTW,Lifeismoney,HatePolitics,Military,car,Japan_Travel,PC_Shopping,movie,
  111. DIABLO,sex,home-sale,KoreaStar,Tech_Job,LoL,mobilecomm,BabyMother,Beauty,WomenTalk,Boy-Girl,BaseballXXXX,Steam,
  112. creditcard,Kaohsiung,iOS,japanavgirls,joke,PlayStation,AllTogether,SportLottery,KoreaDrama,marvel,HardwareSale,
  113. Japandrama,nswitch,CFantasy,Marginalman,Tainan,Elephants,TaichungBun,e-shopping,Lakers,marriage,Bank_Service,biker,AC_In,
  114. PuzzleDragon,CarShop,CVS,Drama-Ticket,Hsinchu,KR_Entertain,Gamesale,miHoYo,watch,Lions,MacShop,BeautySalon,China-Drama,
  115. ToS,fastfood,Option,mobilesales,MuscleBeach,EAseries,Aviation,Badminton,Salary,BabyProducts,PokemonGO,Tennis,
  116. PublicServan,Soft_Job,E-appliance,FATE_GO,MLB,cat,DigiCurrency,Headphone,Food,Key_Mou_Pad,nb-shopping,studyteacher,
  117. MakeUp,TW_Entertain,DSLR,XBOX,BlueArchive,part-time,YuanChuang,WOW,ONE_PIECE,SuperBike,Wanted,Gov_owned,Broad_Band,
  118. DMM_GAMES,give,KoreanPop,SakaTalk,Audiophile,Examination,FORMULA1,gay,Hip-Hop,Railway,DC_SALE,H-GAME,TaiwanDrama,
  119. StupidClown,Taoyuan,AfterPhD,points,Storage_Zone,Hearthstone,e-coupon,Teacher,TY_Research,UmaMusume,facelift,FITNESS,
  120. hypermall,Preschooler,Road_Running,Acad-Affairs,Accounting,Actuary,AKB48,A-Lin,ALLPOST,A-MEI,AmuroNamie,Android,Angela,
  121. AnimalForest,Announce,Anti-ramp,AOA,AOE,ApexLegends,APINK,Aquarium,Aquarius,ArakawaCow,Arashi,ArenaOfValor,Argentina,
  122. Arknights,Aromatherapy,Arsenal,ASTRO,Atom_Boyz,Ayu,AzurLane,BABYMETAL,BanG_Dream,Battlegirlhs,BattleRoyale,BB-Love,
  123. BB_Online,BDSM,bicycle,bicycleshop,BigBanciao,BIGBANG,BigSanchung,BikerShop,Billiard,BioHazard,BLACKPINK,BLAZERS,
  124. BlizzHeroes,BoardGame,book,Brand,Braves,BrawlStars,BrownDust,Browsers,BTOB,BTS,Bucks,Bundesliga,BuyTogether,camping,
  125. Cancer,CareerPlan,car-pool,CATCH,C_BOO,C_ChatBM,Celtics,Certificate,CFP,ChangHua,Chan_Mou,Cheer,CheerGirlsTW,Chelsea,
  126. Chiayi,China-Star,ChungLi,Civil,ClashRoyale,clmusic,Clothes,Club831,CNBLUE,CN_Entertain,CoCo,Coffee,Coldplay,ComGame-New,
  127. Conan,cookclub,CosmosPeople,Covid-19exp,CPBL_ticket,Crowd,Deserts,Digitalhome,DirectSales,Divorce,Djokovic,Dodgers,dog,
  128. DotA2,drawing,EarthDay2021,Eason,EatToDie,EdSheeran,einvoice,e-seller,EuropeTravel,Eurovision,EXID,EXILE,EXO,Facebook,
  129. Falcom,Fallinlove,FAPL,FBaseball,FBG,FCBarcelona,FCBayern,Federer,feminine_sex,FFXIV,FigureSkate,Finance,FireEmblem,
  130. Folklore,FoodDelivery,Football,ForeignEX,Foreign_Inv,forsale,Free_box,FTISLAND,FTV,Fund,fx,GBF,GemTang,GetMarry,GFRIEND,
  131. GHIBLI,G_I-DLE,GirlsFront,GirlsPlanet,GO,GO_FATE,Golden-Award,GoodPregnan,Google,GOT7,Grad-ProbAsk,graduate,G-S-WARRIORS,
  132. GTA,Guardians,GUNDAM,GunsNRoses,hairdo,HakuiKoyori,Hate,HBL,HelpBuy,Hey_Say_JUMP,HIGHLIGHT,Hiking,Hinatazaka46,HK-movie,
  133. hololive,HomeTeach,Hong_Kong,HOT,Hotel,Hotspur,HsinChuang,HsinTien,Hualien,Hunter,ID_GetBack,IdolMaster,iKON,I-Lan,
  134. Immigration,INFINITE,Instant_Mess,Insurance,Interior,Isayama,ITZY,IU,IVE_STARSHIP,IZONE,Jacky,Jam,Jay,Jeremy_Lin,Jing-Ru,
  135. JinYong,JJ,JLPT,job,Jolin,JP_Entertain,J-PopStation,Juventus,JYPnation,KanColle,KANJANI8,KARA,KarenMok,kartrider,Keelung,
  136. Keyakizaka46,Kings,kodomo,Koei,KOF,Korea_Travel,KR_Bands,LaClippers,LALABA,LArc-en-Ciel,LCD,Leo,lesbian,LE_SSERAFIM,
  137. LGBT_SEX,Libra,LifeRecallBM,Lineage,lineageW,Liverpool,LivingGoods,Loan,Lottery,LoveLive_Sip,MAC,Magic,MAMAMOO,Mancare,
  138. ManCity,ManUtd,MapleStory,Maroon5,Mavericks,MayDay,medstudent,MenTalk,MH,MiamiHeat,Miaoli,Militarylife,Mix_Match,
  139. Mizuki_Nana,MMA,Mobile-game,MobilePay,MOD,model,Mo-Musume,money,Monkeys,Moto_GP,MrChildren,MRT,Nadal,Nantou,NARUTO,
  140. NBA_Fantasy,NBA_Film,NCAA,nCoV2019,nCoVPicket,NCU_Talk,Neihu,Nets,NextTopModel,NFL,NIHONGO,NMIXX,nmsmusic,Nogizaka46,NTU,
  141. NTUcourse,NU-EST,NY-Yankees,Old-Games,Olympics_ISG,ONE_OK_ROCK,OneRepublic,Onmyoji,OTT,outdoorgear,Oversea_Job,OverWatch,
  142. PACERS,Palmar_Drama,Paradox,ParkBoGum,PathofExile,PCReDive,Perfume,Perfume_desu,Philippines,PHX-Suns,PingTung,PLAVE,
  143. PokeMon,popmusic,Post,post-b.test,POWERSTATION,PRODUCE48,PRODUCEX101,Programming,ProjectSekai,PTT25_Game,Ptt25sign,
  144. PttEarnMoney,PUBG,PVC-GK,QuestCenter,RailTimes,Rays,RealMadrid,RealmOfValor,RedSox,RedVelvet,RO,Rockets,SakuraMiko,
  145. Sakurazaka46,SanFrancisco,Scorpio,SENIORHIGH,SET,SEVENTEEN,Shadowverse,S.H.E,SHINee,Shinhwa,shoes,ShoheiOhtani,ShuangHe,
  146. Sijhih,Sixers,sky,SMSlife,Snooker,SNSD,Sodagreen,SongShan,soul,SpongeBob,SportsShop,Spurs,SRW,Starbucks,StarCraft,
  147. stationery,story,streetfight,street_style,StrikeShoot,studyabroad,Suckcomic,SuperHeroes,SuperJunior,SYSOP,tabletennis,
  148. Taitung,talk,Tanya,Taurus,tax,teeth_salon,ThaiDrama,Thailand,Theater,THSRshare,Thunder,TizzyBac,toberich,TOEIC,
  149. TPC_Police,Transfer,TuCheng,TurtleSoup,TVXQ,TW-F-Tennis,TWICE,TXT,TypeMoon,Tyukaitiban,UEFA,underwear,US_STOCK,UTAH-JAZZ,
  150. V6,VALORANT,VAPE,Vietnam,Virgo,VISA,Volleyball,VR,Vtuber,Waa,Wanhua,WannaOne,Warfare,WCDragons,wearefriends,WeiBird,
  151. Wen-Shan,WesternMusic,Windows,WindowsPhone,w-inds,Wine,WINNER,WorkanTravel,WorkinChina,worldbasket,WorldCup,Wrestle,
  152. WuBai_and_CB,X1,XXXXballpark,Yoga_Lin,YUGIOH,YuiAragaki,Yunlin,YuzuruHanyu,Zastrology,Zombie
  153.  
  154. `.replace(/\s/g, '').split(',');
  155.  
  156. let lowerCaseMatcher = new Map();
  157. for (const board of fullBoards) board && typeof board === 'string' && board.length >= 1 && lowerCaseMatcher.set(board.toLowerCase(), board);
  158.  
  159. return lowerCaseMatcher;
  160.  
  161.  
  162. })();
  163.  
  164.  
  165. const cacheKey = 'nJ0wg';
  166.  
  167.  
  168. // Extract the current hostname
  169. let currentHostname = window.location.hostname;
  170.  
  171.  
  172. const redirections = [
  173. {
  174. hostname: "www.ptt.cc",
  175. redirect: (objectVariables) => "https://www.ptt.cc/bbs/" + (objectVariables.board + "/" + objectVariables.article) + ".html"
  176. },
  177.  
  178. {
  179. hostname: "www.pttweb.cc",
  180. redirect: (objectVariables) => "https://www.pttweb.cc/bbs/" + objectVariables.board + "/" + objectVariables.article
  181. },
  182.  
  183. {
  184. hostname: "moptt.tw",
  185. redirect: (objectVariables) => "https://moptt.tw/p/" + objectVariables.board + "." + objectVariables.article
  186. },
  187.  
  188. {
  189. hostname: "ptthito.com",
  190. redirect: (objectVariables) => "https://ptthito.com/" + (objectVariables.board + "/" + objectVariables.article.replace(/\./g, '-') + "/").toLowerCase()
  191. },
  192.  
  193.  
  194. {
  195. hostname: "webptt.com",
  196. redirect: (objectVariables) => `https://webptt.com/m.aspx?n=bbs/${objectVariables.board}/${objectVariables.article}.html`
  197. },
  198.  
  199. {
  200. hostname: "pttweb.tw",
  201. redirect: (objectVariables) => `https://pttweb.tw/${objectVariables.board}/${objectVariables.article}`
  202. },
  203.  
  204. {
  205. hostname: "www.ucptt.com",
  206. redirect: (objectVariables) => {
  207. let p = /M\.([^\s.,|\/\\]+)\.A\.([^\s.,|\/\\]+)/.exec(objectVariables.article);
  208. return p ? `http://www.ucptt.com/article/${objectVariables.board}/${p[1]}/${p[2]}` : '';
  209. }
  210. },
  211.  
  212.  
  213.  
  214. // not all posts can read
  215. // https://www.ptt.cc/bbs/Lifeismoney/M.1687762841.A.5E8.html
  216. {
  217. hostname: "pttent.com",
  218. disabled: NO_BUTTON_FOR_CACHER,
  219. redirect: (objectVariables) => `https://pttent.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`
  220. },
  221. {
  222. hostname: "pttgame.com",
  223. disabled: NO_BUTTON_FOR_CACHER,
  224. redirect: (objectVariables) => `https://pttgame.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`
  225. },
  226. {
  227. hostname: "pttdigit.com",
  228. disabled: NO_BUTTON_FOR_CACHER,
  229. redirect: (objectVariables) => `https://pttdigit.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`
  230. },
  231. {
  232. hostname: "pttlocal.com",
  233. disabled: NO_BUTTON_FOR_CACHER,
  234. redirect: (objectVariables) => `https://pttlocal.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`
  235. },
  236. {
  237. hostname: "pttcomic.com",
  238. disabled: NO_BUTTON_FOR_CACHER,
  239. redirect: (objectVariables) => `https://pttcomic.com/${objectVariables.board.toLowerCase()}/${objectVariables.article}.html`
  240. },
  241.  
  242. {
  243. hostname: "webptt.findrate.tw",
  244. disabled: NO_BUTTON_FOR_CACHER,
  245. redirect: (objectVariables) => "https://webptt.findrate.tw/bbs/" + (objectVariables.board + "/" + objectVariables.article) + ".html"
  246. }
  247.  
  248. ];
  249.  
  250.  
  251. // Function to handle menu command redirection
  252. async function redirectToPTT(url, replace) {
  253. await GM.setValue(cacheKey, `${new URL(url).hostname}|${Date.now()}`);
  254. if (replace) {
  255. window.location.replace(url)
  256. } else {
  257. window.location.href = url;
  258. }
  259. }
  260.  
  261.  
  262.  
  263. function extractionCore(uObject) {
  264.  
  265. // Extract parameters
  266.  
  267.  
  268. /*
  269. *
  270.  
  271. supported(1):
  272.  
  273. - https://www.ptt.cc/bbs/C_Chat/M.1684501378.A.9F2.html
  274.  
  275. supported(2):
  276. - https://www.pttweb.cc/bbs/C_Chat/M.1684501378.A.9F2
  277. - https://moptt.tw/p/C_Chat.M.1684501378.A.9F2
  278. - https://ptthito.com/c_chat/m-1684501378-a-9f2/
  279. - https://webptt.com/m.aspx?n=bbs/C_Chat/M.1684501378.A.9F2.html
  280. - https://pttweb.tw/C_Chat/M.1684501378.A.9F2
  281. - http://www.ucptt.com/article/C_Chat/1684501378/9F2
  282.  
  283. supported(3):
  284. - https://disp.cc/ptt/C_Chat/1acPSKqZ
  285. - https://hotptt.com/j3pu5u01ag
  286.  
  287. supported(4):
  288. - https://disp.cc/b/ott/cpKM
  289. - https://disp.cc/b/Gossiping/dyaG
  290.  
  291. supported(over18):
  292. - https://www.ptt.cc/ask/over18?from=%2Fbbs%2FGossiping%2FM.1645949964.A.1E4.html
  293.  
  294. supported (no menu option):
  295.  
  296. - https://pttent.com/movie/M.1548573736.A.DD4.html
  297. - https://pttgame.com/lol/M.1476373373.A.239.html
  298. - https://pttdigit.com/macshop/M.1551692551.A.8EA.html
  299. - https://pttlocal.com/tainan/M.1551683492.A.63D.html
  300.  
  301. not supported:
  302.  
  303. - https://ptt.reviews/HatePolitics/E.w1rS970orKqI
  304. - https://www.ptt666.com/M.1551680529.A.759.html
  305.  
  306.  
  307. */
  308.  
  309.  
  310.  
  311. /*
  312.  
  313. const f = (board) => {
  314. // lowerCased-board
  315. if (!board) return board;
  316. if (board !== board.toLowerCase()) return board;
  317. return lowerCaseMatcher.get(board) || board.replace(/_/g, '-').replace(/\b([a-z])([a-z0-9]*)\b/g, (_, a, b) => a.toUpperCase() + (b || '')).replace(/-/g, '_');
  318. }
  319. */
  320.  
  321. const f = (board) => {
  322. // lowerCased-board
  323. return board;
  324. }
  325.  
  326. const { pathname, hostname } = uObject
  327.  
  328.  
  329. if (pathname.startsWith('/bbs/')) {
  330. let s = pathname.split('/');
  331.  
  332. return {
  333. board: s[2],
  334. article: s[3].replace('.html', '')
  335. }
  336. } else if (pathname === '/ask/over18') {
  337.  
  338. let s = /from=([^=\;\/\?]+)/.exec(uObject.search);
  339. if (s && s[1]) {
  340. let pathname2 = null;
  341. try {
  342. pathname2 = decodeURIComponent(s[1]);
  343. } catch (e) { }
  344. if (pathname2) {
  345. // https://www.ptt.cc/ask/over18?from=%2Fbbs%2FGossiping%2FM.1645949964.A.1E4.html
  346.  
  347. let s = pathname2.split('/');
  348.  
  349. return {
  350. board: s[2],
  351. article: s[3].replace('.html', '')
  352. }
  353.  
  354. }
  355. }
  356.  
  357. } else if (pathname.startsWith('/p/')) {
  358. let d = pathname.substring(3);
  359. let i = d.indexOf('.');
  360. if (i > 0) {
  361. return {
  362. board: d.substring(0, i),
  363. article: d.substring(i + 1)
  364. }
  365. }
  366. } else if (hostname === 'ptthito.com') { // just in case
  367.  
  368. let s = pathname.split('/');
  369.  
  370. return {
  371. board: f(s[1]),
  372. article: s[2].replace(/-/g, '.').toUpperCase()
  373. }
  374. } else if (hostname === 'disp.cc' && pathname.startsWith('/b/')) { // just in case
  375.  
  376. let s = pathname.split('/');
  377.  
  378. return {
  379. board: f(s[2]),
  380. article: null
  381. }
  382. } else if (hostname === 'webptt.com') {
  383.  
  384. let s = location.search.split('/');
  385.  
  386. return {
  387. board: s[1],
  388. article: s[2].replace('.html', '')
  389. }
  390.  
  391. } else if (hostname === 'pttweb.tw') {
  392.  
  393. let s = pathname.split('/');
  394.  
  395. return {
  396. board: s[1],
  397. article: s[2].replace('.html', '')
  398. }
  399. } else if (hostname === 'www.ucptt.com') {
  400.  
  401. let s = pathname.split('/');
  402. if (s[1] === 'article' && s.length === 5) {
  403.  
  404. return {
  405. board: s[2],
  406. article: `M.${s[3]}.A.${s[4]}`
  407. }
  408. }
  409.  
  410. } else if (hostname === 'pttent.com' || hostname === 'pttgame.com' || hostname === 'pttdigit.com' || hostname === 'pttlocal.com' || hostname === 'pttcomic.com') {
  411.  
  412. let s = pathname.split('/');
  413.  
  414. return {
  415. board: f(s[1]),
  416. article: s[2].replace('.html', '')
  417. }
  418. }
  419.  
  420. return null;
  421. }
  422.  
  423.  
  424.  
  425. function readySetup(readyFn) {
  426.  
  427.  
  428. if (document.readyState != 'loading') {
  429. readyFn();
  430. } else {
  431. window.addEventListener("DOMContentLoaded", readyFn, false);
  432. }
  433.  
  434.  
  435. }
  436.  
  437.  
  438. (async () => {
  439.  
  440.  
  441. const mVars = await new Promise(process => {
  442.  
  443.  
  444. let mVars = null;
  445. if (currentHostname === 'hotptt.com') {
  446.  
  447. } else if (currentHostname === "disp.cc") {
  448.  
  449. } else {
  450. mVars = extractionCore(window.location) // try
  451. if (!mVars.board) mVars = null;
  452. if (mVars) {
  453.  
  454. if (currentHostname === "ptthito.com") {
  455. let board = lowerCaseMatcher.get(mVars.board.toLowerCase());
  456. mVars.fuzzyMatch = board ? false : true;
  457. if (board) mVars.board = board;
  458. } else if (currentHostname === 'pttent.com' || currentHostname === 'pttgame.com' || currentHostname === 'pttdigit.com' || currentHostname === 'pttlocal.com') {
  459. let board = lowerCaseMatcher.get(mVars.board.toLowerCase());
  460. mVars.fuzzyMatch = board ? false : true;
  461. if (board) mVars.board = board;
  462. }
  463.  
  464. }
  465. }
  466.  
  467.  
  468. if (mVars && mVars.board && !mVars.fuzzyMatch) {
  469. process(mVars);
  470. } else if (currentHostname === "disp.cc" || currentHostname === "ptthito.com" || currentHostname === 'pttent.com' || currentHostname === 'pttgame.com' || currentHostname === 'pttdigit.com' || currentHostname === 'pttlocal.com') {
  471.  
  472. readySetup(() => {
  473.  
  474. let mVars = null;
  475.  
  476. let pttLink = document.evaluate("//span[contains(text(),'※ 文章網址: ')]/a", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  477. if (pttLink && !pttLink.href.startsWith("https://www.ptt.cc/bbs/")) pttLink = null;
  478. if (!pttLink) {
  479. pttLink = document.querySelector('a[href^="https://www.ptt.cc/bbs/"]');
  480. }
  481. if (pttLink) {
  482. let pttURL = pttLink.href;
  483. if (pttURL && pttURL.startsWith("https://www.ptt.cc/bbs/")) {
  484. mVars = extractionCore(new URL(pttURL));
  485. }
  486. }
  487.  
  488. process(mVars);
  489. });
  490.  
  491.  
  492. } else {
  493. process(null);
  494. }
  495.  
  496.  
  497.  
  498. });
  499.  
  500.  
  501. let activeCaching = false;
  502.  
  503. let __cache__ = null;
  504.  
  505. if (mVars && mVars.board && !mVars.fuzzyMatch) {
  506.  
  507.  
  508. if (!mVars || !mVars.board || !mVars.article) return;
  509.  
  510. lowerCaseMatcher.set(mVars.board.toLowerCase(), mVars.board);
  511.  
  512. __cache__ = await GM.getValue(cacheKey, null);
  513. GM_addValueChangeListener(cacheKey, (key, oldValue, newValue, remote) => {
  514. if (key !== cacheKey) return;
  515. if (typeof newValue !== 'string') newValue = null;
  516. if (!newValue) {
  517. activeCaching = false;
  518. __cache__ = null;
  519. } else {
  520.  
  521. if (remote) activeCaching = false; else activeCaching = true;
  522.  
  523. __cache__ = newValue;
  524. }
  525.  
  526. })
  527. let cached = __cache__;
  528. let cachedS = cached ? cached.split('|') : null;
  529. let redirectionC = null;
  530. if (cachedS && cachedS[0] !== currentHostname && (+Date.now()) - (+cachedS[1]) < 20000) {
  531. let cachedD = cachedS[0];
  532.  
  533. for (const redirection of redirections) {
  534. if (redirection.hostname === cachedD) {
  535. if (typeof redirection.redirect === 'function') {
  536. redirectionC = redirection;
  537. }
  538. break;
  539. }
  540. }
  541. } else if (location.pathname === '/ask/over18' && false) {
  542.  
  543. for (const redirection of redirections) {
  544. if (redirection.hostname === location.hostname) {
  545. if (typeof redirection.redirect === 'function') {
  546. redirectionC = redirection;
  547. }
  548. break;
  549. }
  550. }
  551.  
  552. }
  553.  
  554. if (redirectionC && typeof redirectionC.redirect === 'function' && !window.p6tvU) {
  555. let url = redirectionC.redirect(mVars);
  556. if (url) redirectToPTT(url, true);
  557. } else {
  558. redirectionC = null;
  559.  
  560. const addMenuOption = async (redirection) => {
  561. GM_registerMenuCommand("Redirect to " + redirection.hostname, function () {
  562. let url = redirection.redirect(mVars);
  563. if (url) {
  564. window.p6tvU = 1;
  565. redirectToPTT(url, false);
  566. }
  567. });
  568. }
  569.  
  570. for (const redirection of redirections) {
  571. if (currentHostname !== redirection.hostname && !redirection.disabled) {
  572. addMenuOption(redirection);
  573. }
  574. }
  575.  
  576. if (cachedS && cachedS[0] !== currentHostname) {
  577. GM.deleteValue(cacheKey);
  578. } else if (cachedS && cachedS[0] === currentHostname) {
  579.  
  580. if ((+Date.now()) - (+cachedS[1]) < 20000) {
  581.  
  582. activeCaching = true;
  583. console.log('PTT Sites Redirection: setInterval');
  584.  
  585. let repeatFn = async () => {
  586. if (activeCaching) {
  587. console.log('PTT Sites Redirection: activeCaching', currentHostname)
  588. let p = __cache__;
  589. let s = p ? p.split('|') : null;
  590.  
  591. if (s && s[0] === currentHostname && (+Date.now()) - (+s[1]) < 20000) {
  592. GM.setValue(cacheKey, `${currentHostname}|${Date.now()}`);
  593. }
  594. }
  595.  
  596. };
  597.  
  598. setInterval(repeatFn, 10000);
  599. repeatFn();
  600.  
  601. } else {
  602. GM.deleteValue(cacheKey);
  603. }
  604.  
  605. }
  606.  
  607. }
  608.  
  609.  
  610. }
  611.  
  612. })();
  613.  
  614.  
  615. })();
  616.