forked from cadence/breezewiki
Compare commits
22 commits
Author | SHA1 | Date | |
---|---|---|---|
443f1eecbc | |||
97c4e54f38 | |||
8db91d5e32 | |||
1e3451a990 | |||
778cf24957 | |||
49682b2320 | |||
8f0caa9132 | |||
14930f18dc | |||
1ef184547b | |||
5672f46886 | |||
755efe3cd6 | |||
47d92d3a37 | |||
2e0bd786ec | |||
0fd0efc3f2 | |||
d2765c2a78 | |||
7dff049ece | |||
6260ba809b | |||
2b3a8fe108 | |||
dcb8a8a590 | |||
f5399524b1 | |||
ead6896818 | |||
9773e62c46 |
16 changed files with 419 additions and 142 deletions
|
@ -1,6 +1,5 @@
|
|||
#lang racket/base
|
||||
(require (prefix-in easy: net/http-easy)
|
||||
"../src/data.rkt"
|
||||
(require "../src/data.rkt"
|
||||
"xexpr-utils.rkt")
|
||||
|
||||
(provide
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
(provide
|
||||
; headers to always send on all http responses
|
||||
always-headers
|
||||
; timeout durations for http-easy requests
|
||||
timeouts
|
||||
; generates a consistent footer
|
||||
application-footer
|
||||
; generates a consistent template for wiki page content to sit in
|
||||
|
@ -39,7 +37,6 @@
|
|||
(define always-headers
|
||||
(list (header #"Referrer-Policy" #"same-origin") ; header to not send referers to fandom
|
||||
(header #"Link" (string->bytes/latin-1 link-header))))
|
||||
(define timeouts (easy:make-timeout-config #:lease 5 #:connect 5))
|
||||
|
||||
(define-runtime-path path-static "../static")
|
||||
(define theme-icons
|
||||
|
@ -71,9 +68,6 @@
|
|||
`(p ,(format "This instance is run by the ~a developer, " (config-get 'application_name))
|
||||
(a (@ (href "https://cadence.moe/contact"))
|
||||
"Cadence")
|
||||
". Proudly hosted by "
|
||||
(a (@ (href "http://alphamethyl.barr0w.net"))
|
||||
"Barrow Network Solutions" (sup "XD"))
|
||||
".")
|
||||
`(p
|
||||
,(format "This unofficial instance is based off the ~a source code, but is not controlled by the code developer." (config-get 'application_name)))))
|
||||
|
@ -91,11 +85,13 @@
|
|||
|
||||
;; generate a notice with a link if a fandom wiki has a replacement as part of NIWA or similar
|
||||
;; if the wiki has no replacement, display nothing
|
||||
(define (extwiki-notice wikiname title)
|
||||
(define (extwiki-notice wikiname title req user-cookies)
|
||||
(define xt (findf (λ (item) (member wikiname (extwiki^-wikinames item))) extwikis))
|
||||
(cond/var
|
||||
[xt
|
||||
(let* ([group (hash-ref extwiki-groups (extwiki^-group xt))]
|
||||
(let* ([seen? (member wikiname (user-cookies^-notices user-cookies))]
|
||||
[aside-class (if seen? "niwa__notice niwa--seen" "niwa__notice")]
|
||||
[group (hash-ref extwiki-groups (extwiki^-group xt))]
|
||||
[search-page (format "/Special:Search?~a"
|
||||
(params->query `(("search" . ,title)
|
||||
("go" . "Go"))))]
|
||||
|
@ -106,21 +102,24 @@
|
|||
[props (extwiki-props^ go)])
|
||||
(cond
|
||||
[(eq? (extwiki^-banner xt) 'default)
|
||||
`(aside (@ (class "niwa__notice"))
|
||||
`(aside (@ (class ,aside-class))
|
||||
(h1 (@ (class "niwa__header")) ,(extwiki^-name xt) " has its own website separate from Fandom.")
|
||||
(a (@ (class "niwa__go") (href ,go)) "Read " ,title " on " ,(extwiki^-name xt) " →")
|
||||
(div (@ (class "niwa__cols"))
|
||||
(div (@ (class "niwa__left"))
|
||||
(p ,((extwiki-group^-description group) props))
|
||||
(p ,((extwiki^-description xt) props))
|
||||
(p "This wiki's core community has wholly migrated away from Fandom. You should "
|
||||
(p ,((extwiki-group^-description group) props))
|
||||
(p "This wiki's core community has largely migrated away from Fandom. You should "
|
||||
(a (@ (href ,go)) "go to " ,(extwiki^-name xt) " now!"))
|
||||
(p (@ (class "niwa__feedback"))
|
||||
,@(add-between
|
||||
`(,@(for/list ([link (extwiki-group^-links group)])
|
||||
`(a (@ (href ,(cdr link))) ,(car link)))
|
||||
"This notice is from BreezeWiki"
|
||||
(a (@ (href "https://docs.breezewiki.com/Reporting_Bugs.html")) "Feedback?"))
|
||||
(a (@ (rel "nofollow")
|
||||
(class "niwa__got-it")
|
||||
(href ,(user-cookies-setter-url/add-notice req user-cookies wikiname)))
|
||||
"OK, got it"))
|
||||
" / ")))
|
||||
(div (@ (class "niwa__right"))
|
||||
(img (@ (class "niwa__logo") (src ,(extwiki^-logo xt)))))))]
|
||||
|
@ -175,7 +174,7 @@
|
|||
(define styles
|
||||
(list
|
||||
(format "~a/wikia.php?controller=ThemeApi&method=themeVariables&variant=~a" origin (user-cookies^-theme user-cookies))
|
||||
(format "~a/load.php?lang=en&modules=site.styles%7Cskin.fandomdesktop.styles%7Cext.fandom.PortableInfoboxFandomDesktop.css%7Cext.fandom.GlobalComponents.CommunityHeaderBackground.css%7Cext.gadget.site-styles%2Csound-styles&only=styles&skin=fandomdesktop" origin)))
|
||||
(format "~a/load.php?lang=en&modules=site.styles%7Cskin.fandomdesktop.styles%7Cext.fandom.PortableInfoboxFandomDesktop.css%7Cext.fandom.GlobalComponents.CommunityHeaderBackground.css%7Cext.fandom.photoGallery.gallery.css%7Cext.gadget.site-styles%2Csound-styles&only=styles&skin=fandomdesktop" origin)))
|
||||
(if (config-true? 'strict_proxy)
|
||||
(map u-proxy-url styles)
|
||||
styles)]
|
||||
|
@ -203,10 +202,11 @@
|
|||
`(script (@ (type "module") (src ,(get-static-url "search-suggestions.js"))))
|
||||
"")
|
||||
(script (@ (type "module") (src ,(get-static-url "countdown.js"))))
|
||||
(script (@ (defer) (src ,(get-static-url "tabs.js"))))
|
||||
(link (@ (rel "icon") (href ,(u (λ (v) (config-true? 'strict_proxy))
|
||||
(λ (v) (u-proxy-url v))
|
||||
(head-data^-icon-url head-data))))))
|
||||
(body (@ (class ,(head-data^-body-class head-data)))
|
||||
(body (@ (class ,(head-data^-body-class head-data) " bw-tabs-nojs"))
|
||||
,(let ([extension-eligible?
|
||||
(cond/var
|
||||
[(not req) #f]
|
||||
|
@ -227,7 +227,7 @@
|
|||
(div (@ (class "fandom-community-header__background tileHorizontally header")))
|
||||
(div (@ (class "page"))
|
||||
(main (@ (class "page__main"))
|
||||
,(extwiki-notice wikiname title)
|
||||
,(extwiki-notice wikiname title req user-cookies)
|
||||
(div (@ (class "custom-top"))
|
||||
(h1 (@ (class "page-title")) ,title)
|
||||
(nav (@ (class "sitesearch"))
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
; all values here are optimised for maximum prettiness
|
||||
(parameterize ([pretty-print-columns 80])
|
||||
(display "config: ")
|
||||
(pretty-write ((inst sort (Pairof Symbol String))
|
||||
(pretty-write ((inst sort (Pairof Symbol String) Symbol)
|
||||
(hash->list (make-immutable-hasheq combined-alist))
|
||||
symbol<?
|
||||
#:key car))))
|
||||
|
|
41
src/data.rkt
41
src/data.rkt
|
@ -1,14 +1,15 @@
|
|||
#lang racket/base
|
||||
(require racket/list
|
||||
racket/match
|
||||
racket/string
|
||||
web-server/http/request-structs
|
||||
net/url-string
|
||||
(only-in net/cookies/server cookie-header->alist cookie->set-cookie-header make-cookie)
|
||||
(prefix-in easy: net/http-easy)
|
||||
db
|
||||
memo
|
||||
"fandom-request.rkt"
|
||||
"static-data.rkt"
|
||||
"whole-utils.rkt"
|
||||
"../lib/url-utils.rkt"
|
||||
"../lib/xexpr-utils.rkt"
|
||||
"../archiver/archiver-database.rkt"
|
||||
|
@ -27,7 +28,8 @@
|
|||
user-cookies-getter
|
||||
user-cookies-default
|
||||
user-cookies-setter
|
||||
user-cookies-setter-url)
|
||||
user-cookies-setter-url
|
||||
user-cookies-setter-url/add-notice)
|
||||
|
||||
(struct siteinfo^ (sitename basepage license) #:transparent)
|
||||
(struct license^ (text url) #:transparent)
|
||||
|
@ -54,21 +56,21 @@
|
|||
(vector-ref row 3)))
|
||||
siteinfo-default)]
|
||||
[else
|
||||
(define dest-url
|
||||
(format "https://~a.fandom.com/api.php?~a"
|
||||
(define res
|
||||
(fandom-get-api
|
||||
wikiname
|
||||
(params->query '(("action" . "query")
|
||||
'(("action" . "query")
|
||||
("meta" . "siteinfo")
|
||||
("siprop" . "general|rightsinfo")
|
||||
("format" . "json")
|
||||
("formatversion" . "2")))))
|
||||
(log-outgoing dest-url)
|
||||
(define res (easy:get dest-url))
|
||||
("formatversion" . "2"))))
|
||||
(cond [(= (easy:response-status-code res) 200)
|
||||
(define data (easy:response-json res))
|
||||
(siteinfo^ (jp "/query/general/sitename" data)
|
||||
(second (regexp-match #rx"/wiki/(.*)" (jp "/query/general/base" data)))
|
||||
(license^ (jp "/query/rightsinfo/text" data)
|
||||
(jp "/query/rightsinfo/url" data)))]))
|
||||
(jp "/query/rightsinfo/url" data)))]
|
||||
[else siteinfo-default])]))
|
||||
|
||||
(define/memoize (head-data-getter wikiname) #:hash hash
|
||||
;; data will be stored here, can be referenced by the memoized closure
|
||||
|
@ -90,8 +92,8 @@
|
|||
;; then no matter what, return the best information we have so far
|
||||
this-data))
|
||||
|
||||
(struct user-cookies^ (theme) #:prefab)
|
||||
(define user-cookies-default (user-cookies^ 'default))
|
||||
(struct user-cookies^ (theme notices) #:prefab)
|
||||
(define user-cookies-default (user-cookies^ 'default '()))
|
||||
(define (user-cookies-getter req)
|
||||
(define cookie-header (headers-assq* #"cookie" (request-headers/raw req)))
|
||||
(define cookies-alist (if cookie-header (cookie-header->alist (header-value cookie-header) bytes->string/utf-8) null))
|
||||
|
@ -100,16 +102,29 @@
|
|||
(match pair
|
||||
[(cons "theme" (and theme (or "light" "dark" "default")))
|
||||
(values 'theme (string->symbol theme))]
|
||||
[(cons "notices" notices)
|
||||
(values 'notices (string-split notices "|"))]
|
||||
[_ (values #f #f)])))
|
||||
(user-cookies^
|
||||
(hash-ref cookies-hash 'theme (user-cookies^-theme user-cookies-default))))
|
||||
(hash-ref cookies-hash 'theme (user-cookies^-theme user-cookies-default))
|
||||
(hash-ref cookies-hash 'notices (user-cookies^-notices user-cookies-default))))
|
||||
|
||||
(define (user-cookies-setter user-cookies)
|
||||
(map (λ (c) (header #"Set-Cookie" (cookie->set-cookie-header c)))
|
||||
(list (make-cookie "theme" (symbol->string (user-cookies^-theme user-cookies))
|
||||
#:path "/"
|
||||
#:max-age (* 60 60 24 365 10))
|
||||
(make-cookie "notices" (string-join (user-cookies^-notices user-cookies) "|")
|
||||
|
||||
#:path "/"
|
||||
#:max-age (* 60 60 24 365 10)))))
|
||||
|
||||
(define (user-cookies-setter-url req new-settings)
|
||||
(format "/set-user-settings?~a" (params->query `(("next_location" . ,(url->string (request-uri req)))
|
||||
("new_settings" . ,(format "~a" new-settings))))))
|
||||
("new_settings" . ,(format "~s" new-settings))))))
|
||||
|
||||
(define (user-cookies-setter-url/add-notice req user-cookies notice-name)
|
||||
(user-cookies-setter-url
|
||||
req
|
||||
(struct-copy user-cookies^ user-cookies
|
||||
[notices (cons notice-name (user-cookies^-notices user-cookies))])))
|
||||
|
|
|
@ -33,26 +33,43 @@
|
|||
; don't forget that I'm returning *code* - return a call to the function
|
||||
(datum->syntax stx `(make-dispatcher-tree ,ds)))
|
||||
|
||||
; guard that the page returned a response, otherwise print more detailed debugging information
|
||||
(define-syntax-rule (page ds name)
|
||||
(λ (req)
|
||||
(define dispatcher (hash-ref ds (quote name)))
|
||||
(define page-response (dispatcher req))
|
||||
(if (response? page-response)
|
||||
page-response
|
||||
(response/output
|
||||
#:code 500
|
||||
#:mime-type #"text/plain"
|
||||
(λ (out)
|
||||
(for ([port (list (current-error-port) out)])
|
||||
(parameterize ([current-output-port port])
|
||||
(printf "error in ~a:~n expected page to return a response~n actually returned: ~v~n"
|
||||
(quote name)
|
||||
page-response))))))))
|
||||
|
||||
(define (make-dispatcher-tree ds)
|
||||
(define subdomain-dispatcher (hash-ref ds 'subdomain-dispatcher))
|
||||
(define tree
|
||||
(sequencer:make
|
||||
subdomain-dispatcher
|
||||
(pathprocedure:make "/" (hash-ref ds 'page-home))
|
||||
(pathprocedure:make "/proxy" (hash-ref ds 'page-proxy))
|
||||
(pathprocedure:make "/search" (hash-ref ds 'page-global-search))
|
||||
(pathprocedure:make "/set-user-settings" (hash-ref ds 'page-set-user-settings))
|
||||
(pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works))
|
||||
(filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category)))
|
||||
(filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file)))
|
||||
(pathprocedure:make "/" (page ds page-home))
|
||||
(pathprocedure:make "/proxy" (page ds page-proxy))
|
||||
(pathprocedure:make "/search" (page ds page-global-search))
|
||||
(pathprocedure:make "/set-user-settings" (page ds page-set-user-settings))
|
||||
(pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (page ds page-it-works))
|
||||
(filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (page ds page-category)))
|
||||
(filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (page ds page-file)))
|
||||
(if (config-true? 'feature_offline::enabled)
|
||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki-offline)))
|
||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (page ds page-wiki-offline)))
|
||||
(λ (_conn _req) (next-dispatcher)))
|
||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki)))
|
||||
(filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (hash-ref ds 'page-search)))
|
||||
(filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (hash-ref ds 'redirect-wiki-home)))
|
||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (page ds page-wiki)))
|
||||
(filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (page ds page-search)))
|
||||
(filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (page ds redirect-wiki-home)))
|
||||
(if (config-true? 'feature_offline::enabled)
|
||||
(filter:make (pregexp (format "^/archive/~a/(styles|images)/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-static-archive)))
|
||||
(filter:make (pregexp (format "^/archive/~a/(styles|images)/.+$" px-wikiname)) (lift:make (page ds page-static-archive)))
|
||||
(λ (_conn _req) (next-dispatcher)))
|
||||
(hash-ref ds 'static-dispatcher)
|
||||
(lift:make (hash-ref ds 'page-not-found))))
|
||||
|
|
|
@ -27,6 +27,13 @@
|
|||
(λ (props)
|
||||
`(p "The Square Enix Indpendent Wiki Alliance, or SEIWA, is a network of independent wikis established in 2011 and focused on providing high-quality coverage of Square Enix and its content. We work together, along with our affiliates and others, to co-operate and support one another while providing the best-quality content on the various Square Enix video games and media.")))
|
||||
|
||||
'GWN
|
||||
(extwiki-group^
|
||||
"GWN"
|
||||
'(("Gaming Wiki Network" . "https://gamingwikinetwork.org/"))
|
||||
(λ (props)
|
||||
`(p "This wiki is part of the Gaming Wiki Network, a network of independently-hosted wikis about video game franchises. The GWN was founded on October 21, 2022. It aims to support all gaming communities in building independently-hosted wikis.")))
|
||||
|
||||
'Terraria
|
||||
(extwiki-group^
|
||||
"Terraria"
|
||||
|
@ -71,11 +78,25 @@
|
|||
(λ (props)
|
||||
'(p "The wiki was founded by Citricsquid on July 16th, 2009 as a way to document information from Minecraft. Since November 15th, 2010, it has been hosted by Curse Media. On December 12th, 2018, it moved to Fandom as it purchased Curse Media. Since September 24, 2023, it forked from Fandom and has been hosted by Weird Gloop.")))
|
||||
|
||||
'Tardis
|
||||
(extwiki-group^
|
||||
"Tardis"
|
||||
'(("Forking announcement" . "https://tardis.wiki/wiki/Tardis:Forking_announcement")
|
||||
("Discussion on Reddit" . "https://old.reddit.com/r/doctorwho/comments/1azxmrl/tardis_wiki_has_regenerated/"))
|
||||
(λ (props) '()))
|
||||
|
||||
'Rainverse
|
||||
(extwiki-group^
|
||||
"Rainverse"
|
||||
'(("Forking announcement" . "https://transfem.social/notes/9qsqdkmqi78e01bh"))
|
||||
(λ (props)
|
||||
'()))
|
||||
|
||||
'empty
|
||||
(extwiki-group^
|
||||
"Misc"
|
||||
'(("This wiki doesn't have a description yet. Add one?" . "https://docs.breezewiki.com/Reporting_Bugs.html"))
|
||||
#f)))
|
||||
(λ (props) '()))))
|
||||
|
||||
;; wikiname, niwa-name, url, logo-url
|
||||
(struct extwiki^ (wikinames banner group name home logo description) #:transparent)
|
||||
|
@ -309,11 +330,11 @@
|
|||
(extwiki^
|
||||
'("zelda" "zelda-archive") 'default
|
||||
'NIWA
|
||||
"Zeldapedia"
|
||||
"https://zeldapedia.wiki/wiki/Main_Page"
|
||||
"Zelda Wiki"
|
||||
"https://zeldawiki.wiki/wiki/Main_Page"
|
||||
"https://niwanetwork.org/images/logos/zeldapedia.png"
|
||||
(λ (props)
|
||||
`((p "Founded on April 23, 2005 as Zelda Wiki, today's Zeldapedia is your definitive source for encyclopedic information on The Legend of Zelda series, as well as all of the latest Zelda news. Zeldapedia went independent from Fandom in October 2022, citing Fandom's recent buyouts and staffing decisions among their reasons."))))
|
||||
`((p "Founded on April 23, 2005, Zelda Wiki is your definitive source for encyclopedic information on The Legend of Zelda series, as well as all of the latest Zelda news. Zelda Wiki went independent from Fandom in October 2022, citing Fandom's recent buyouts and staffing decisions among their reasons."))))
|
||||
|
||||
(extwiki^
|
||||
'("chrono") 'default
|
||||
|
@ -419,6 +440,99 @@
|
|||
(λ (props)
|
||||
`()))
|
||||
|
||||
(extwiki^
|
||||
'("tardis") 'default
|
||||
'Tardis
|
||||
"TARDIS Wiki"
|
||||
"https://tardis.wiki/wiki/Doctor_Who_Wiki"
|
||||
"https://tardis.wiki/w/images/Tardis_Images/e/e6/Site-logo.png"
|
||||
(λ (props)
|
||||
`()))
|
||||
|
||||
(extwiki^
|
||||
'("wizardry") 'default
|
||||
'GWN
|
||||
"Wizardry Wiki"
|
||||
"https://wizardry.wiki.gg/wiki/Wizardry_Wiki"
|
||||
"https://wizardry.wiki.gg/images/e/e6/Site-logo.png"
|
||||
(λ (props)
|
||||
`((p "On March 21, 2023, the wiki has decided to leave and abandoning from Fandom due to numerous of issues such as intrusive advertising, long-lasting bugs, restrictions on customization, etcetera. Wizardry Wiki was officially inducted into the wiki.gg wikifarm, with all contents forked over.")
|
||||
(p "The wiki has partnered with " (a (@ (href "https://fallout.wiki/")) "Independent Fallout Wiki") " as of June 14, 2024."))))
|
||||
|
||||
(extwiki^
|
||||
'("jackryan") 'default
|
||||
'GWN
|
||||
"Tom Clancy Wiki"
|
||||
"https://tomclancy.wiki.gg/wiki/Tom_Clancy_Wiki"
|
||||
"https://tomclancy.wiki.gg/images/thumb/c/c5/Jack_Ryan_Logo_Dark.png/600px-Jack_Ryan_Logo_Dark.png"
|
||||
(λ (props)
|
||||
`((p "The Tom Clancy Wiki is a collaborative encyclopedia dedicated to Tom Clancy’s franchises. The Tom Clancy franchise is a 40-year old expansive franchise founded by Tom Clancy, telling several unique sagas through books, video games, and films, as well as a TV show."))))
|
||||
|
||||
(extwiki^
|
||||
'("hollowknight") 'default
|
||||
'GWN
|
||||
"Hollow Knight Wiki"
|
||||
"https://hollowknight.wiki/wiki/Main_Page"
|
||||
"https://gamingwikinetwork.org/images/logos/hollowknight.png"
|
||||
(λ (props)
|
||||
`((p "We are an independently hosted wiki for the games Hollow Knight and Hollow Knight: Silksong, created by fans, for fans. The wiki is a fork of the FANDOM Hollow Knight Wiki and was officially unveiled on October 31, 2023."))))
|
||||
|
||||
(extwiki^
|
||||
'("hellokitty" "sanrio") 'default
|
||||
'GWN
|
||||
"Sanrio Wiki"
|
||||
"https://sanriowiki.com/wiki/Sanrio_Wiki"
|
||||
"https://cdn.sanriowiki.com/wiki.png"
|
||||
(λ (props)
|
||||
`((p "Sanrio Wiki is a project that was started on April 14, 2015 by EvieMelody. It was hosted on the wiki-farm ShoutWiki and has since become independent."))))
|
||||
|
||||
(extwiki^
|
||||
'("sto") 'default
|
||||
'GWN
|
||||
"Star Trek Online Wiki"
|
||||
"https://stowiki.net/wiki/Main_Page"
|
||||
"https://gamingwikinetwork.org/images/logos/stowiki.png"
|
||||
(λ (props)
|
||||
`()))
|
||||
|
||||
(extwiki^
|
||||
'("rayman-game" "ubisoftrayman") 'default
|
||||
'GWN
|
||||
"Rayman Wiki"
|
||||
"https://raymanpc.com/wiki/en/Main_Page"
|
||||
"https://raymanpc.com/wiki/script-en/resources/assets/logo-en.png?5c608"
|
||||
(λ (props)
|
||||
`()))
|
||||
|
||||
(extwiki^
|
||||
'("granblue") 'empty
|
||||
'empty
|
||||
"Granblue Fantasy Wiki"
|
||||
"https://gbf.wiki/"
|
||||
"https://gbf.wiki/images/1/18/Vyrnball.png?0704c"
|
||||
(λ (props)
|
||||
`()))
|
||||
|
||||
(extwiki^
|
||||
'("hellmet-roblox") 'empty
|
||||
'empty
|
||||
"HELLMET Wiki"
|
||||
"https://hellmet.miraheze.org/wiki/Main_Page"
|
||||
"https://static.miraheze.org/hellmetwiki/thumb/c/ce/Hellmet_Wiki_Logo.png/135px-Hellmet_Wiki_Logo.png"
|
||||
(λ (props)
|
||||
`()))
|
||||
|
||||
(extwiki^
|
||||
'("rain-web-comic") 'default
|
||||
'empty
|
||||
"Rainverse Wiki"
|
||||
"https://rainverse.wiki/wiki/Main_Page"
|
||||
"https://static.miraheze.org/rainversewiki/2/2c/Rain_comic_cover.png"
|
||||
(λ (props)
|
||||
`((p "We have a newly-migrated Rainverse Wiki which escaped from Fandom! Rain is the comic that helped me figure out my gender, so I am really glad to have a wiki on a non-evil host.")
|
||||
(p "Please stop using the abandoned copy of Rain Wiki on Fandom. Fandom is still \"training\" a generator which adds procedurally-generated bullshit to articles, with no way for users to remove or correct it, and they're demanding volunteer wiki admins waste time \"vetting\" the procedurally-generated BS for accuracy. As Jocelyn herself said, \"fuck Fandom forever.\"")
|
||||
(p "If you are interested, please add more articles related to other Rainverse stories."))))
|
||||
|
||||
;; fandom wikinames * empty * empty * Name * Home Page
|
||||
(extwiki^ '("aether") 'empty 'empty "Aether Wiki" "https://aether.wiki.gg/wiki/Aether_Wiki" #f #f)
|
||||
(extwiki^ '("before-darkness-falls") 'empty 'empty "Before Darkness Falls Wiki" "https://beforedarknessfalls.wiki.gg/wiki/Before_Darkness_Falls_Wiki" #f #f)
|
||||
|
@ -434,6 +548,7 @@
|
|||
(extwiki^ '("doom") 'empty 'empty "DoomWiki.org" "https://doomwiki.org/wiki/Entryway" #f #f)
|
||||
(extwiki^ '("dreamscaper") 'empty 'empty "Official Dreamscaper Wiki" "https://dreamscaper.wiki.gg/wiki/Dreamscaper_Wiki" #f #f)
|
||||
(extwiki^ '("elderscrolls") 'empty 'empty "UESP" "https://en.uesp.net/wiki/Main_Page" #f #f)
|
||||
(extwiki^ '("enterthegungeon" "exit-the-gungeon" "enter-the-gungeon-archive") 'empty 'empty "Official Enter The Gungeon Wiki" "https://enterthegungeon.wiki.gg/wiki/Enter_the_Gungeon_Wiki" "https://enterthegungeon.wiki.gg/images/e/e6/Site-logo.png" #f)
|
||||
(extwiki^ '("fiend-folio") 'empty 'empty "Official Fiend Folio Wiki" "https://fiendfolio.wiki.gg/wiki/Fiend_Folio_Wiki" #f #f)
|
||||
(extwiki^ '("foxhole") 'empty 'empty "Foxhole Wiki" "https://foxhole.wiki.gg/wiki/Foxhole_Wiki" #f #f)
|
||||
(extwiki^ '("have-a-nice-death") 'empty 'empty "Have a Nice Death Wiki" "https://haveanicedeath.wiki.gg/wiki/Have_a_Nice_Death_Wiki" #f #f)
|
||||
|
|
74
src/fandom-request.rkt
Normal file
74
src/fandom-request.rkt
Normal file
|
@ -0,0 +1,74 @@
|
|||
#lang typed/racket/base
|
||||
(require racket/format
|
||||
racket/string
|
||||
"config.rkt"
|
||||
"../lib/url-utils.rkt")
|
||||
(define-type Headers (HashTable Symbol (U Bytes String)))
|
||||
(require/typed net/http-easy
|
||||
[#:opaque Timeout-Config timeout-config?]
|
||||
[#:opaque Response response?]
|
||||
[#:opaque Session session?]
|
||||
[response-status-code (Response -> Natural)]
|
||||
[current-session (Parameter Session)]
|
||||
[current-user-agent (Parameter (U Bytes String))]
|
||||
[make-timeout-config ([#:lease Positive-Real] [#:connect Positive-Real] -> Timeout-Config)]
|
||||
[get ((U Bytes String)
|
||||
[#:close? Boolean]
|
||||
[#:headers Headers]
|
||||
[#:timeouts Timeout-Config]
|
||||
[#:max-attempts Exact-Positive-Integer]
|
||||
[#:max-redirects Exact-Nonnegative-Integer]
|
||||
[#:user-agent (U Bytes String)]
|
||||
-> Response)])
|
||||
|
||||
(provide
|
||||
fandom-get
|
||||
fandom-get-api
|
||||
timeouts)
|
||||
|
||||
(unless (string-contains? (~a (current-user-agent)) "BreezeWiki")
|
||||
(current-user-agent
|
||||
(format "BreezeWiki/1.0 (~a) ~a"
|
||||
(if (config-true? 'canonical_origin)
|
||||
(config-get 'canonical_origin)
|
||||
"local")
|
||||
(current-user-agent))))
|
||||
|
||||
(define timeouts (make-timeout-config #:lease 5 #:connect 5))
|
||||
|
||||
(: last-failure Flonum)
|
||||
(define last-failure 0.0)
|
||||
(: stored-failure (Option Response))
|
||||
(define stored-failure #f)
|
||||
(define failure-persist-time 30000)
|
||||
|
||||
(: no-headers Headers)
|
||||
(define no-headers '#hasheq())
|
||||
|
||||
(: fandom-get (String String [#:headers (Option Headers)] -> Response))
|
||||
(define (fandom-get wikiname path #:headers [headers #f])
|
||||
(or
|
||||
(and ((current-inexact-milliseconds) . < . (+ last-failure failure-persist-time)) stored-failure)
|
||||
(let ()
|
||||
(define dest-url (string-append "https://www.fandom.com" path))
|
||||
(define host (string-append wikiname ".fandom.com"))
|
||||
(log-outgoing wikiname path)
|
||||
(define res
|
||||
(get dest-url
|
||||
#:timeouts timeouts
|
||||
#:headers (hash-set (or headers no-headers) 'Host host)))
|
||||
(when (memq (response-status-code res) '(403 406))
|
||||
(set! last-failure (current-inexact-milliseconds))
|
||||
(set! stored-failure res))
|
||||
res)))
|
||||
|
||||
(: fandom-get-api (String (Listof (Pair String String)) [#:headers (Option Headers)] -> Response))
|
||||
(define (fandom-get-api wikiname params #:headers [headers #f])
|
||||
(fandom-get wikiname
|
||||
(string-append "/api.php?" (params->query params))
|
||||
#:headers headers))
|
||||
|
||||
(: log-outgoing (String String -> Void))
|
||||
(define (log-outgoing wikiname path)
|
||||
(when (config-true? 'log_outgoing)
|
||||
(printf "out: ~a ~a~n" wikiname path)))
|
|
@ -15,11 +15,11 @@
|
|||
"application-globals.rkt"
|
||||
"config.rkt"
|
||||
"data.rkt"
|
||||
"fandom-request.rkt"
|
||||
"page-wiki.rkt"
|
||||
"../lib/syntax.rkt"
|
||||
"../lib/thread-utils.rkt"
|
||||
"../lib/url-utils.rkt"
|
||||
"whole-utils.rkt"
|
||||
"../lib/xexpr-utils.rkt")
|
||||
|
||||
(provide
|
||||
|
@ -73,30 +73,24 @@
|
|||
(define-values (members-data page-data siteinfo)
|
||||
(thread-values
|
||||
(λ ()
|
||||
(define dest-url
|
||||
(format "~a/api.php?~a"
|
||||
origin
|
||||
(params->query `(("action" . "query")
|
||||
(easy:response-json
|
||||
(fandom-get-api
|
||||
wikiname
|
||||
`(("action" . "query")
|
||||
("list" . "categorymembers")
|
||||
("cmtitle" . ,prefixed-category)
|
||||
("cmlimit" . "max")
|
||||
("formatversion" . "2")
|
||||
("format" . "json")))))
|
||||
(log-outgoing dest-url)
|
||||
(define dest-res (easy:get dest-url #:timeouts timeouts))
|
||||
(easy:response-json dest-res))
|
||||
(λ ()
|
||||
(define dest-url
|
||||
(format "~a/api.php?~a"
|
||||
origin
|
||||
(params->query `(("action" . "parse")
|
||||
(easy:response-json
|
||||
(fandom-get-api
|
||||
wikiname
|
||||
`(("action" . "parse")
|
||||
("page" . ,prefixed-category)
|
||||
("prop" . "text|headhtml|langlinks")
|
||||
("formatversion" . "2")
|
||||
("format" . "json")))))
|
||||
(log-outgoing dest-url)
|
||||
(define dest-res (easy:get dest-url #:timeouts timeouts))
|
||||
(easy:response-json dest-res))
|
||||
(λ ()
|
||||
(siteinfo-fetch wikiname))))
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
"application-globals.rkt"
|
||||
"config.rkt"
|
||||
"data.rkt"
|
||||
"fandom-request.rkt"
|
||||
"page-wiki.rkt"
|
||||
"../lib/syntax.rkt"
|
||||
"../lib/thread-utils.rkt"
|
||||
"../lib/url-utils.rkt"
|
||||
"whole-utils.rkt"
|
||||
"../lib/xexpr-utils.rkt")
|
||||
|
||||
(provide page-file)
|
||||
|
@ -40,8 +40,7 @@
|
|||
(imageDescription . #f))))
|
||||
|
||||
(define (url-content-type url)
|
||||
(log-outgoing url)
|
||||
(define dest-res (easy:head url #:timeouts timeouts))
|
||||
(define dest-res (easy:head url))
|
||||
(easy:response-headers-ref dest-res 'content-type))
|
||||
|
||||
(define (get-media-html url content-type)
|
||||
|
@ -106,20 +105,18 @@
|
|||
(response-handler
|
||||
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
||||
(define prefixed-title (path/param-path (caddr (url-path (request-uri req)))))
|
||||
(define origin (format "https://~a.fandom.com" wikiname))
|
||||
(define source-url (format "~a/wiki/~a" origin prefixed-title))
|
||||
(define source-url (format "https://~a.fandom.com/wiki/~a" wikiname prefixed-title))
|
||||
|
||||
(define-values (media-detail siteinfo)
|
||||
(thread-values
|
||||
(λ ()
|
||||
(define dest-url
|
||||
(format "~a/wikia.php?~a"
|
||||
origin
|
||||
(define dest-res
|
||||
(fandom-get
|
||||
wikiname
|
||||
(format "/wikia.php?~a"
|
||||
(params->query `(("format" . "json") ("controller" . "Lightbox")
|
||||
("method" . "getMediaDetail")
|
||||
("fileTitle" . ,prefixed-title)))))
|
||||
(log-outgoing dest-url)
|
||||
(define dest-res (easy:get dest-url #:timeouts timeouts))
|
||||
("fileTitle" . ,prefixed-title))))))
|
||||
(easy:response-json dest-res))
|
||||
(λ ()
|
||||
(siteinfo-fetch wikiname))))
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
(require racket/dict
|
||||
racket/list
|
||||
racket/string
|
||||
(prefix-in easy: net/http-easy)
|
||||
; html libs
|
||||
html-writing
|
||||
; web server libs
|
||||
|
@ -18,7 +17,6 @@
|
|||
"../lib/syntax.rkt"
|
||||
"../lib/thread-utils.rkt"
|
||||
"../lib/url-utils.rkt"
|
||||
"whole-utils.rkt"
|
||||
"../lib/xexpr-utils.rkt")
|
||||
|
||||
(provide
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
"application-globals.rkt"
|
||||
"config.rkt"
|
||||
"data.rkt"
|
||||
"fandom-request.rkt"
|
||||
"../lib/archive-file-mappings.rkt"
|
||||
"../lib/pure-utils.rkt"
|
||||
"../lib/syntax.rkt"
|
||||
"../lib/thread-utils.rkt"
|
||||
"../lib/tree-updater.rkt"
|
||||
"../lib/url-utils.rkt"
|
||||
"whole-utils.rkt"
|
||||
"../lib/xexpr-utils.rkt")
|
||||
|
||||
(provide
|
||||
|
@ -37,25 +38,21 @@
|
|||
|
||||
(define (page-wiki req)
|
||||
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
||||
(define segments (map path/param-path (cdr (url-path (request-uri req)))))
|
||||
(define user-cookies (user-cookies-getter req))
|
||||
(define origin (format "https://~a.fandom.com" wikiname))
|
||||
(define path (string-join (map path/param-path (cddr (url-path (request-uri req)))) "/"))
|
||||
(define path (string-join (cdr segments) "/"))
|
||||
(define source-url (format "https://~a.fandom.com/wiki/~a" wikiname path))
|
||||
|
||||
(define-values (dest-res siteinfo)
|
||||
(thread-values
|
||||
(λ ()
|
||||
(define dest-url
|
||||
(format "~a/api.php?~a"
|
||||
origin
|
||||
(params->query `(("action" . "parse")
|
||||
(fandom-get-api
|
||||
wikiname
|
||||
`(("action" . "parse")
|
||||
("page" . ,path)
|
||||
("prop" . "text|headhtml|langlinks")
|
||||
("formatversion" . "2")
|
||||
("format" . "json")))))
|
||||
(log-outgoing dest-url)
|
||||
(easy:get dest-url
|
||||
#:timeouts timeouts
|
||||
("format" . "json"))
|
||||
#:headers `#hasheq((cookie . ,(format "theme=~a" (user-cookies^-theme user-cookies))))))
|
||||
(λ ()
|
||||
(siteinfo-fetch wikiname))))
|
||||
|
@ -103,4 +100,35 @@
|
|||
#:code 200
|
||||
#:headers headers
|
||||
(λ (out)
|
||||
(write-html body out))))))]))
|
||||
(write-html body out))))))]
|
||||
[(eq? 404 (easy:response-status-code dest-res))
|
||||
(next-dispatcher)]
|
||||
[(memq (easy:response-status-code dest-res) '(403 406))
|
||||
(response-handler
|
||||
(define body
|
||||
(generate-wiki-page
|
||||
`(div
|
||||
(p "Sorry! Fandom isn't allowing BreezeWiki to show pages right now.")
|
||||
(p "We'll automatically try again in 30 seconds, so please stay on this page and be patient.")
|
||||
(p (small "In a hurry? " (a (@ (href ,source-url)) "Click here to read the page on Fandom."))))
|
||||
#:req req
|
||||
#:source-url source-url
|
||||
#:wikiname wikiname
|
||||
#:title (url-segments->guess-title segments)
|
||||
#:siteinfo siteinfo))
|
||||
(response/output
|
||||
#:code 503
|
||||
#:headers (build-headers
|
||||
always-headers
|
||||
(header #"Retry-After" #"30")
|
||||
(header #"Cache-Control" #"max-age=30, public")
|
||||
(header #"Refresh" #"35"))
|
||||
(λ (out)
|
||||
(write-html body out))))]
|
||||
[else
|
||||
(response-handler
|
||||
(error 'page-wiki "Tried to load page ~a/~a~nSadly, the page didn't load because Fandom returned status code ~a with response:~n~a"
|
||||
wikiname
|
||||
path
|
||||
(easy:response-status-code dest-res)
|
||||
(easy:response-body dest-res)))]))
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
(prefix-in easy: net/http-easy)
|
||||
"application-globals.rkt"
|
||||
"config.rkt"
|
||||
"fandom-request.rkt"
|
||||
"../lib/url-utils.rkt"
|
||||
"whole-utils.rkt"
|
||||
"../lib/xexpr-utils.rkt")
|
||||
|
||||
(provide
|
||||
|
@ -17,20 +17,14 @@
|
|||
'(#hasheq((ns . 0) (pageid . 219) (size . 1482) (snippet . "") (timestamp . "2022-08-21T08:54:23Z") (title . "Gacha Capsule") (wordcount . 214)) #hasheq((ns . 0) (pageid . 201) (size . 1198) (snippet . "") (timestamp . "2022-07-11T17:52:47Z") (title . "Badges") (wordcount . 181)))))
|
||||
|
||||
(define (search-fandom wikiname query params)
|
||||
;; constructing the URL where I want to get fandom data from...
|
||||
(define origin (format "https://~a.fandom.com" wikiname))
|
||||
;; the dest-URL will look something like https://minecraft.fandom.com/api.php?action=query&list=search&srsearch=Spawner&formatversion=2&format=json
|
||||
(define dest-url
|
||||
(format "~a/api.php?~a"
|
||||
origin
|
||||
(params->query `(("action" . "query")
|
||||
(define res
|
||||
(fandom-get-api
|
||||
wikiname
|
||||
`(("action" . "query")
|
||||
("list" . "search")
|
||||
("srsearch" . ,query)
|
||||
("formatversion" . "2")
|
||||
("format" . "json")))))
|
||||
;; HTTP request to dest-url for search results
|
||||
(log-outgoing dest-url)
|
||||
(define res (easy:get dest-url #:timeouts timeouts))
|
||||
("format" . "json"))))
|
||||
(define json (easy:response-json res))
|
||||
(define search-results (jp "/query/search" json))
|
||||
(generate-results-content-fandom wikiname query search-results))
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
"application-globals.rkt"
|
||||
"../lib/html-parsing/main.rkt"
|
||||
"../lib/url-utils.rkt"
|
||||
"whole-utils.rkt"
|
||||
"../lib/xexpr-utils.rkt")
|
||||
|
||||
(provide
|
||||
|
@ -36,8 +35,7 @@
|
|||
("hl.tag.post" . "</mark>")
|
||||
("sort" . ,(cdr sort))))))
|
||||
;; HTTP request to dest-url for search results
|
||||
(log-outgoing dest-url)
|
||||
(define res (easy:get dest-url #:timeouts timeouts))
|
||||
(define res (easy:get dest-url #:timeouts (easy:make-timeout-config #:lease 5 #:connect 5)))
|
||||
(define json (easy:response-json res))
|
||||
|
||||
;; build result objects
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#lang typed/racket/base
|
||||
(require "config.rkt")
|
||||
|
||||
(provide
|
||||
; prints "out: <url>"
|
||||
log-outgoing)
|
||||
|
||||
(: log-outgoing (String -> Void))
|
||||
(define (log-outgoing url-string)
|
||||
(when (config-true? 'log_outgoing)
|
||||
(printf "out: ~a~n" url-string)))
|
|
@ -202,11 +202,11 @@ figcaption, .lightbox-caption, .thumbcaption {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
/* show tabs always */
|
||||
.wds-tabs__wrapper {
|
||||
/* show tabs if tabs.js isn't loaded */
|
||||
.bw-tabs-nojs .wds-tabs__wrapper {
|
||||
display: none;
|
||||
}
|
||||
.wds-tab__content {
|
||||
.bw-tabs-nojs .wds-tab__content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
@ -431,6 +431,25 @@ a.ext-audiobutton { /* see hearthstone/wiki/Diablo_(Duels_hero) */
|
|||
font-size: 14px;
|
||||
text-align: right;
|
||||
}
|
||||
/* more compact notice after it's been seen the first time */
|
||||
.niwa--seen {
|
||||
padding: 1.5vw 2vw 2vw;
|
||||
overflow-y: auto;
|
||||
max-height: min(280px, 33vh);
|
||||
font-size: 17px;
|
||||
margin-top: -2vw;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.niwa--seen .niwa__header {
|
||||
font-size: 26px;
|
||||
}
|
||||
.niwa--seen .niwa__go {
|
||||
padding: 10px 18px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.niwa--seen .niwa__got-it {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* media queries */
|
||||
|
||||
|
|
40
static/tabs.js
Normal file
40
static/tabs.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
"use strict";
|
||||
|
||||
const tabFromHash = location.hash.length > 1 ? location.hash.substring(1) : null
|
||||
|
||||
for (const tabber of document.body.querySelectorAll(".wds-tabber")) {
|
||||
for (const [tab, content] of getTabberTabs(tabber)) {
|
||||
// set up click listener on every tab
|
||||
tab.addEventListener("click", e => {
|
||||
setCurrentTab(tabber, tab, content)
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
// re-open a specific tab on page load based on the URL hash
|
||||
if (tab.dataset.hash === tabFromHash) {
|
||||
setCurrentTab(tabber, tab, content)
|
||||
tab.scrollIntoView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTabberTabs(tabber) {
|
||||
// need to scope the selector to handle nested tabs. see /unturned/wiki/Crate for an example
|
||||
const tabs = [...tabber.querySelectorAll(":scope > .wds-tabs__wrapper .wds-tabs__tab")]
|
||||
const contents = [...tabber.querySelectorAll(":scope > .wds-tab__content")]
|
||||
return tabs.map((_, index) => [tabs[index], contents[index]]) // transpose arrays into [[tab, content], ...]
|
||||
}
|
||||
|
||||
function setCurrentTab(tabber, tab, content) {
|
||||
// clear currently selected tab
|
||||
getTabberTabs(tabber).flat().forEach(e => e.classList.remove("wds-is-current"))
|
||||
|
||||
// select new tab
|
||||
tab.classList.add("wds-is-current")
|
||||
content.classList.add("wds-is-current")
|
||||
if (tab.dataset.hash) {
|
||||
history.replaceState(null, "", `#${tab.dataset.hash}`)
|
||||
}
|
||||
}
|
||||
|
||||
document.body.classList.remove("bw-tabs-nojs")
|
Loading…
Reference in a new issue