ddg.php 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750
  1. <?php
  2. class ddg{
  3. public function __construct(){
  4. include "lib/backend.php";
  5. $this->backend = new backend("ddg");
  6. include "lib/fuckhtml.php";
  7. $this->fuckhtml = new fuckhtml();
  8. }
  9. /*
  10. curl functions
  11. */
  12. private const req_web = 0;
  13. private const req_xhr = 1;
  14. private function get($proxy, $url, $get = [], $reqtype = self::req_web){
  15. $curlproc = curl_init();
  16. if($get !== []){
  17. $get = http_build_query($get);
  18. $url .= "?" . $get;
  19. }
  20. curl_setopt($curlproc, CURLOPT_URL, $url);
  21. switch($reqtype){
  22. case self::req_web:
  23. $headers =
  24. ["User-Agent: " . config::USER_AGENT,
  25. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
  26. "Accept-Encoding: gzip",
  27. "Accept-Language: en-US,en;q=0.5",
  28. "DNT: 1",
  29. "Connection: keep-alive",
  30. "Upgrade-Insecure-Requests: 1",
  31. "Sec-Fetch-Dest: document",
  32. "Sec-Fetch-Mode: navigate",
  33. "Sec-Fetch-Site: cross-site",
  34. "Upgrade-Insecure-Requests: 1"];
  35. break;
  36. case self::req_xhr:
  37. $headers =
  38. ["User-Agent: " . config::USER_AGENT,
  39. "Accept: */*",
  40. "Accept-Encoding: gzip",
  41. "Accept-Language: en-US,en;q=0.5",
  42. "Connection: keep-alive",
  43. "Referer: https://duckduckgo.com/",
  44. "X-Requested-With: XMLHttpRequest",
  45. "DNT: 1",
  46. "Sec-Fetch-Dest: script",
  47. "Sec-Fetch-Mode: no-cors",
  48. "Sec-Fetch-Site: same-site"];
  49. break;
  50. }
  51. $this->backend->assign_proxy($curlproc, $proxy);
  52. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  53. curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers);
  54. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  55. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  56. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  57. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  58. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  59. $data = curl_exec($curlproc);
  60. if(curl_errno($curlproc)){
  61. throw new Exception(curl_error($curlproc));
  62. }
  63. curl_close($curlproc);
  64. return $data;
  65. }
  66. public function getfilters($pagetype){
  67. switch($pagetype){
  68. case "web":
  69. return
  70. [
  71. "country" => [
  72. "display" => "Country",
  73. "option" => [
  74. "any" => "All Regions",
  75. "ar-es" => "Argentina",
  76. "au-en" => "Australia",
  77. "at-de" => "Austria",
  78. "be-fr" => "Belgium (fr)",
  79. "be-nl" => "Belgium (nl)",
  80. "br-pt" => "Brazil",
  81. "bg-bg" => "Bulgaria",
  82. "ca-en" => "Canada (en)",
  83. "ca-fr" => "Canada (fr)",
  84. "ct-ca" => "Catalonia",
  85. "cl-es" => "Chile",
  86. "cn-zh" => "China",
  87. "co-es" => "Colombia",
  88. "hr-hr" => "Croatia",
  89. "cz-cs" => "Czech Republic",
  90. "dk-da" => "Denmark",
  91. "ee-et" => "Estonia",
  92. "fi-fi" => "Finland",
  93. "fr-fr" => "France",
  94. "de-de" => "Germany",
  95. "gr-el" => "Greece",
  96. "hk-tzh" => "Hong Kong",
  97. "hu-hu" => "Hungary",
  98. "in-en" => "India (en)",
  99. "id-en" => "Indonesia (en)",
  100. "ie-en" => "Ireland",
  101. "il-en" => "Israel (en)",
  102. "it-it" => "Italy",
  103. "jp-jp" => "Japan",
  104. "kr-kr" => "Korea",
  105. "lv-lv" => "Latvia",
  106. "lt-lt" => "Lithuania",
  107. "my-en" => "Malaysia (en)",
  108. "mx-es" => "Mexico",
  109. "nl-nl" => "Netherlands",
  110. "nz-en" => "New Zealand",
  111. "no-no" => "Norway",
  112. "pk-en" => "Pakistan (en)",
  113. "pe-es" => "Peru",
  114. "ph-en" => "Philippines (en)",
  115. "pl-pl" => "Poland",
  116. "pt-pt" => "Portugal",
  117. "ro-ro" => "Romania",
  118. "ru-ru" => "Russia",
  119. "xa-ar" => "Saudi Arabia",
  120. "sg-en" => "Singapore",
  121. "sk-sk" => "Slovakia",
  122. "sl-sl" => "Slovenia",
  123. "za-en" => "South Africa",
  124. "es-ca" => "Spain (ca)",
  125. "es-es" => "Spain (es)",
  126. "se-sv" => "Sweden",
  127. "ch-de" => "Switzerland (de)",
  128. "ch-fr" => "Switzerland (fr)",
  129. "tw-tzh" => "Taiwan",
  130. "th-en" => "Thailand (en)",
  131. "tr-tr" => "Turkey",
  132. "us-en" => "US (English)",
  133. "us-es" => "US (Spanish)",
  134. "ua-uk" => "Ukraine",
  135. "uk-en" => "United Kingdom",
  136. "vn-en" => "Vietnam (en)"
  137. ]
  138. ],
  139. "nsfw" => [
  140. "display" => "NSFW",
  141. "option" => [
  142. "yes" => "Yes",
  143. "maybe" => "Maybe",
  144. "no" => "No"
  145. ]
  146. ],
  147. "newer" => [
  148. "display" => "Newer than",
  149. "option" => "_DATE"
  150. ],
  151. "older" => [
  152. "display" => "Older than",
  153. "option" => "_DATE"
  154. ],
  155. "extendedsearch" => [
  156. // undefined display, so it wont show in frontend
  157. "option" => [
  158. "yes" => "Yes",
  159. "no" => "No"
  160. ]
  161. ]
  162. ];
  163. break;
  164. case "images":
  165. return
  166. [
  167. "country" => [
  168. "display" => "Country",
  169. "option" => [
  170. "us-en" => "US (English)",
  171. "ar-es" => "Argentina",
  172. "au-en" => "Australia",
  173. "at-de" => "Austria",
  174. "be-fr" => "Belgium (fr)",
  175. "be-nl" => "Belgium (nl)",
  176. "br-pt" => "Brazil",
  177. "bg-bg" => "Bulgaria",
  178. "ca-en" => "Canada (en)",
  179. "ca-fr" => "Canada (fr)",
  180. "ct-ca" => "Catalonia",
  181. "cl-es" => "Chile",
  182. "cn-zh" => "China",
  183. "co-es" => "Colombia",
  184. "hr-hr" => "Croatia",
  185. "cz-cs" => "Czech Republic",
  186. "dk-da" => "Denmark",
  187. "ee-et" => "Estonia",
  188. "fi-fi" => "Finland",
  189. "fr-fr" => "France",
  190. "de-de" => "Germany",
  191. "gr-el" => "Greece",
  192. "hk-tzh" => "Hong Kong",
  193. "hu-hu" => "Hungary",
  194. "in-en" => "India (en)",
  195. "id-en" => "Indonesia (en)",
  196. "ie-en" => "Ireland",
  197. "il-en" => "Israel (en)",
  198. "it-it" => "Italy",
  199. "jp-jp" => "Japan",
  200. "kr-kr" => "Korea",
  201. "lv-lv" => "Latvia",
  202. "lt-lt" => "Lithuania",
  203. "my-en" => "Malaysia (en)",
  204. "mx-es" => "Mexico",
  205. "nl-nl" => "Netherlands",
  206. "nz-en" => "New Zealand",
  207. "no-no" => "Norway",
  208. "pk-en" => "Pakistan (en)",
  209. "pe-es" => "Peru",
  210. "ph-en" => "Philippines (en)",
  211. "pl-pl" => "Poland",
  212. "pt-pt" => "Portugal",
  213. "ro-ro" => "Romania",
  214. "ru-ru" => "Russia",
  215. "xa-ar" => "Saudi Arabia",
  216. "sg-en" => "Singapore",
  217. "sk-sk" => "Slovakia",
  218. "sl-sl" => "Slovenia",
  219. "za-en" => "South Africa",
  220. "es-ca" => "Spain (ca)",
  221. "es-es" => "Spain (es)",
  222. "se-sv" => "Sweden",
  223. "ch-de" => "Switzerland (de)",
  224. "ch-fr" => "Switzerland (fr)",
  225. "tw-tzh" => "Taiwan",
  226. "th-en" => "Thailand (en)",
  227. "tr-tr" => "Turkey",
  228. "us-es" => "US (Spanish)",
  229. "ua-uk" => "Ukraine",
  230. "uk-en" => "United Kingdom",
  231. "vn-en" => "Vietnam (en)"
  232. ]
  233. ],
  234. "nsfw" => [
  235. "display" => "NSFW",
  236. "option" => [
  237. "yes" => "Yes",
  238. "no" => "No"
  239. ]
  240. ],
  241. "date" => [
  242. "display" => "Time posted",
  243. "option" => [
  244. "any" => "Any time",
  245. "Day" => "Past day",
  246. "Week" => "Past week",
  247. "Month" => "Past month"
  248. ]
  249. ],
  250. "size" => [
  251. "display" => "Size",
  252. "option" => [
  253. "any" => "Any size",
  254. "Small" => "Small",
  255. "Medium" => "Medium",
  256. "Large" => "Large",
  257. "Wallpaper" => "Wallpaper"
  258. ]
  259. ],
  260. "color" => [
  261. "display" => "Colors",
  262. "option" => [
  263. "any" => "All colors",
  264. "Monochrome" => "Black and white",
  265. "Red" => "Red",
  266. "Orange" => "Orange",
  267. "Yellow" => "Yellow",
  268. "Green" => "Green",
  269. "Blue" => "Blue",
  270. "Purple" => "Purple",
  271. "Pink" => "Pink",
  272. "Brown" => "Brown",
  273. "Black" => "Black",
  274. "Gray" => "Gray",
  275. "Teal" => "Teal",
  276. "White" => "White"
  277. ]
  278. ],
  279. "type" => [
  280. "display" => "Type",
  281. "option" => [
  282. "any" => "All types",
  283. "photo" => "Photograph",
  284. "clipart" => "Clipart",
  285. "gif" => "Animated GIF",
  286. "transparent" => "Transparent"
  287. ]
  288. ],
  289. "layout" => [
  290. "display" => "Layout",
  291. "option" => [
  292. "any" => "All layouts",
  293. "Square" => "Square",
  294. "Tall" => "Tall",
  295. "Wide" => "Wide"
  296. ]
  297. ],
  298. "license" => [
  299. "display" => "License",
  300. "option" => [
  301. "any" => "All licenses", // blame ddg for this
  302. "Any" => "All Creative Commons",
  303. "Public" => "Public domain",
  304. "Share" => "Free to Share and Use",
  305. "ShareCommercially" => "Free to Share and Use Commercially",
  306. "Modify" => "Free to Modify, Share, and Use",
  307. "ModifyCommercially" => "Free to Modify, Share, and Use Commercially"
  308. ]
  309. ]
  310. ];
  311. break;
  312. case "videos":
  313. return
  314. [
  315. "country" => [
  316. "display" => "Country",
  317. "option" => [
  318. "us-en" => "US (English)",
  319. "ar-es" => "Argentina",
  320. "au-en" => "Australia",
  321. "at-de" => "Austria",
  322. "be-fr" => "Belgium (fr)",
  323. "be-nl" => "Belgium (nl)",
  324. "br-pt" => "Brazil",
  325. "bg-bg" => "Bulgaria",
  326. "ca-en" => "Canada (en)",
  327. "ca-fr" => "Canada (fr)",
  328. "ct-ca" => "Catalonia",
  329. "cl-es" => "Chile",
  330. "cn-zh" => "China",
  331. "co-es" => "Colombia",
  332. "hr-hr" => "Croatia",
  333. "cz-cs" => "Czech Republic",
  334. "dk-da" => "Denmark",
  335. "ee-et" => "Estonia",
  336. "fi-fi" => "Finland",
  337. "fr-fr" => "France",
  338. "de-de" => "Germany",
  339. "gr-el" => "Greece",
  340. "hk-tzh" => "Hong Kong",
  341. "hu-hu" => "Hungary",
  342. "in-en" => "India (en)",
  343. "id-en" => "Indonesia (en)",
  344. "ie-en" => "Ireland",
  345. "il-en" => "Israel (en)",
  346. "it-it" => "Italy",
  347. "jp-jp" => "Japan",
  348. "kr-kr" => "Korea",
  349. "lv-lv" => "Latvia",
  350. "lt-lt" => "Lithuania",
  351. "my-en" => "Malaysia (en)",
  352. "mx-es" => "Mexico",
  353. "nl-nl" => "Netherlands",
  354. "nz-en" => "New Zealand",
  355. "no-no" => "Norway",
  356. "pk-en" => "Pakistan (en)",
  357. "pe-es" => "Peru",
  358. "ph-en" => "Philippines (en)",
  359. "pl-pl" => "Poland",
  360. "pt-pt" => "Portugal",
  361. "ro-ro" => "Romania",
  362. "ru-ru" => "Russia",
  363. "xa-ar" => "Saudi Arabia",
  364. "sg-en" => "Singapore",
  365. "sk-sk" => "Slovakia",
  366. "sl-sl" => "Slovenia",
  367. "za-en" => "South Africa",
  368. "es-ca" => "Spain (ca)",
  369. "es-es" => "Spain (es)",
  370. "se-sv" => "Sweden",
  371. "ch-de" => "Switzerland (de)",
  372. "ch-fr" => "Switzerland (fr)",
  373. "tw-tzh" => "Taiwan",
  374. "th-en" => "Thailand (en)",
  375. "tr-tr" => "Turkey",
  376. "us-en" => "US (English)",
  377. "us-es" => "US (Spanish)",
  378. "ua-uk" => "Ukraine",
  379. "uk-en" => "United Kingdom",
  380. "vn-en" => "Vietnam (en)"
  381. ]
  382. ],
  383. "nsfw" => [
  384. "display" => "NSFW",
  385. "option" => [
  386. "yes" => "Yes",
  387. "no" => "No"
  388. ]
  389. ],
  390. "date" => [
  391. "display" => "Time fetched",
  392. "option" => [
  393. "any" => "Any time",
  394. "d" => "Past day",
  395. "w" => "Past week",
  396. "m" => "Past month"
  397. ]
  398. ],
  399. "resolution" => [ //videoDefinition
  400. "display" => "Resolution",
  401. "option" => [
  402. "any" => "Any resolution",
  403. "high" => "High definition",
  404. "standard" => "Standard definition"
  405. ]
  406. ],
  407. "duration" => [ // videoDuration
  408. "display" => "Duration",
  409. "option" => [
  410. "any" => "Any duration",
  411. "short" => "Short (>5min)",
  412. "medium" => "Medium (5-20min)",
  413. "long" => "Long (<20min)"
  414. ]
  415. ],
  416. "license" => [
  417. "display" => "License",
  418. "option" => [
  419. "any" => "Any license",
  420. "creativeCommon" => "Creative Commons",
  421. "youtube" => "YouTube Standard"
  422. ]
  423. ]
  424. ];
  425. break;
  426. case "news":
  427. return
  428. [
  429. "country" => [
  430. "display" => "Country",
  431. "option" => [
  432. "us-en" => "US (English)",
  433. "ar-es" => "Argentina",
  434. "au-en" => "Australia",
  435. "at-de" => "Austria",
  436. "be-fr" => "Belgium (fr)",
  437. "be-nl" => "Belgium (nl)",
  438. "br-pt" => "Brazil",
  439. "bg-bg" => "Bulgaria",
  440. "ca-en" => "Canada (en)",
  441. "ca-fr" => "Canada (fr)",
  442. "ct-ca" => "Catalonia",
  443. "cl-es" => "Chile",
  444. "cn-zh" => "China",
  445. "co-es" => "Colombia",
  446. "hr-hr" => "Croatia",
  447. "cz-cs" => "Czech Republic",
  448. "dk-da" => "Denmark",
  449. "ee-et" => "Estonia",
  450. "fi-fi" => "Finland",
  451. "fr-fr" => "France",
  452. "de-de" => "Germany",
  453. "gr-el" => "Greece",
  454. "hk-tzh" => "Hong Kong",
  455. "hu-hu" => "Hungary",
  456. "in-en" => "India (en)",
  457. "id-en" => "Indonesia (en)",
  458. "ie-en" => "Ireland",
  459. "il-en" => "Israel (en)",
  460. "it-it" => "Italy",
  461. "jp-jp" => "Japan",
  462. "kr-kr" => "Korea",
  463. "lv-lv" => "Latvia",
  464. "lt-lt" => "Lithuania",
  465. "my-en" => "Malaysia (en)",
  466. "mx-es" => "Mexico",
  467. "nl-nl" => "Netherlands",
  468. "nz-en" => "New Zealand",
  469. "no-no" => "Norway",
  470. "pk-en" => "Pakistan (en)",
  471. "pe-es" => "Peru",
  472. "ph-en" => "Philippines (en)",
  473. "pl-pl" => "Poland",
  474. "pt-pt" => "Portugal",
  475. "ro-ro" => "Romania",
  476. "ru-ru" => "Russia",
  477. "xa-ar" => "Saudi Arabia",
  478. "sg-en" => "Singapore",
  479. "sk-sk" => "Slovakia",
  480. "sl-sl" => "Slovenia",
  481. "za-en" => "South Africa",
  482. "es-ca" => "Spain (ca)",
  483. "es-es" => "Spain (es)",
  484. "se-sv" => "Sweden",
  485. "ch-de" => "Switzerland (de)",
  486. "ch-fr" => "Switzerland (fr)",
  487. "tw-tzh" => "Taiwan",
  488. "th-en" => "Thailand (en)",
  489. "tr-tr" => "Turkey",
  490. "us-en" => "US (English)",
  491. "us-es" => "US (Spanish)",
  492. "ua-uk" => "Ukraine",
  493. "uk-en" => "United Kingdom",
  494. "vn-en" => "Vietnam (en)"
  495. ]
  496. ],
  497. "nsfw" => [
  498. "display" => "NSFW",
  499. "option" => [
  500. "yes" => "Yes",
  501. "maybe" => "Maybe",
  502. "no" => "No"
  503. ]
  504. ],
  505. "date" => [
  506. "display" => "Time posted",
  507. "option" => [
  508. "any" => "Any time",
  509. "d" => "Past day",
  510. "w" => "Past week",
  511. "m" => "Past month"
  512. ]
  513. ]
  514. ];
  515. break;
  516. default:
  517. return [];
  518. break;
  519. }
  520. }
  521. public function web($get){
  522. if($get["npt"]){
  523. [$jsgrep, $proxy] = $this->backend->get($get["npt"], "web");
  524. $extendedsearch = false;
  525. $inithtml = "";
  526. }else{
  527. $search = $get["s"];
  528. if(strlen($search) === 0){
  529. throw new Exception("Search term is empty!");
  530. }
  531. $proxy = $this->backend->get_ip();
  532. $country = $get["country"];
  533. $nsfw = $get["nsfw"];
  534. $older = $get["older"];
  535. $newer = $get["newer"];
  536. $extendedsearch = $get["extendedsearch"] == "yes" ? true : false;
  537. // generate filters
  538. $get_filters = [
  539. "q" => $search,
  540. "kz" => "1" // force instant answers
  541. ];
  542. if($country == "any"){
  543. $get_filters["kl"] = "wt-wt";
  544. }else{
  545. $get_filters["kl"] = $country;
  546. }
  547. switch($nsfw){
  548. case "yes": $get_filters["kp"] = "-2"; break;
  549. case "maybe": $get_filters["kp"] = "-1"; break;
  550. case "no": $get_filters["kp"] = "1"; break;
  551. }
  552. $df = true;
  553. if($newer === false){
  554. if($older !== false){
  555. $start = 36000;
  556. $end = $older;
  557. }else{
  558. $df = false;
  559. }
  560. }else{
  561. $start = $newer;
  562. if($older !== false){
  563. $end = $older;
  564. }else{
  565. $end = time();
  566. }
  567. }
  568. if($df === true){
  569. $get_filters["df"] = date("Y-m-d", $start) . ".." . date("Y-m-d", $end);
  570. }
  571. /*
  572. Get html
  573. */
  574. try{
  575. $inithtml = $this->get(
  576. $proxy,
  577. "https://duckduckgo.com/",
  578. $get_filters
  579. );
  580. }catch(Exception $e){
  581. throw new Exception("Failed to get html");
  582. }
  583. preg_match(
  584. '/DDG\.deep\.initialize\(\'(.*)\',/U',
  585. $inithtml,
  586. $jsgrep
  587. );
  588. if(!isset($jsgrep[1])){
  589. throw new Exception("Failed to get d.js URL");
  590. }
  591. $jsgrep = $jsgrep[1];
  592. }
  593. // get javascript
  594. try{
  595. $js = $this->get(
  596. $proxy,
  597. "https://links.duckduckgo.com" . $jsgrep,
  598. [],
  599. ddg::req_xhr
  600. );
  601. }catch(Exception $e){
  602. throw new Exception("Failed to fetch d.js");
  603. }
  604. // initialize api response array
  605. $out = [
  606. "status" => "ok",
  607. "spelling" => [
  608. "type" => "no_correction",
  609. "using" => null,
  610. "correction" => null
  611. ],
  612. "npt" => null,
  613. "answer" => [],
  614. "web" => [],
  615. "image" => [],
  616. "video" => [],
  617. "news" => [],
  618. "related" => []
  619. ];
  620. /*
  621. Additional requests
  622. */
  623. if($extendedsearch){
  624. /*
  625. Check for worknik results
  626. */
  627. preg_match(
  628. '/nrj\(\'\/js\/spice\/dictionary\/definition\/([^\'\)]+)/',
  629. $js,
  630. $wordnik
  631. );
  632. if(isset($wordnik[1])){
  633. try{
  634. $wordnik = $wordnik[1];
  635. // get definition
  636. $wordnikjs = $this->get(
  637. $proxy,
  638. "https://duckduckgo.com/js/spice/dictionary/definition/" . $wordnik,
  639. [],
  640. ddg::req_xhr
  641. );
  642. preg_match(
  643. '/ddg_spice_dictionary_definition\(\n?(\[{[\S\s]*}])/',
  644. $wordnikjs,
  645. $wordnikjson
  646. );
  647. if(isset($wordnikjson[1])){
  648. $wordnikjson = json_decode($wordnikjson[1], true);
  649. $out["answer"][0] = [
  650. "title" => urldecode($wordnik),
  651. "description" => [],
  652. "url" => "https://www.wordnik.com/words/" . $wordnik,
  653. "thumb" => null,
  654. "table" => [],
  655. "sublink" => []
  656. ];
  657. $partofspeech = false;
  658. $wastext = false;
  659. $textindent = 1;
  660. // get audio
  661. $wordnikaudio_json =
  662. json_decode(
  663. $this->get(
  664. $proxy,
  665. "https://duckduckgo.com/js/spice/dictionary/audio/" . $wordnik,
  666. [],
  667. ddg::req_xhr
  668. ),
  669. true
  670. );
  671. if(isset($wordnikaudio_json[0]["id"])){
  672. usort($wordnikaudio_json, function($a, $b){
  673. return $a["id"] < $b["id"];
  674. });
  675. $out["answer"][0]["description"][] = [
  676. "type" => "audio",
  677. "url" => $wordnikaudio_json[0]["fileUrl"]
  678. ];
  679. }
  680. $collection = [];
  681. $e[] = [];
  682. foreach($wordnikjson as $data){
  683. if(!isset($data["partOfSpeech"])){
  684. continue;
  685. }
  686. if(isset($data["text"])){
  687. if(!isset($collection[$data["partOfSpeech"]])){
  688. $collection[$data["partOfSpeech"]] = [];
  689. $c = 0;
  690. }else{
  691. $c = count($collection[$data["partOfSpeech"]]);
  692. }
  693. if(!isset($e[$data["partOfSpeech"]])){
  694. $e[$data["partOfSpeech"]] = 0;
  695. }
  696. $e[$data["partOfSpeech"]]++;
  697. $text = $e[$data["partOfSpeech"]] . ". " . $this->unescapehtml(strip_tags($data["text"]));
  698. $syn = false;
  699. if(
  700. isset($data["relatedWords"]) &&
  701. count($data["relatedWords"]) !== 0
  702. ){
  703. $syn = " (";
  704. $u = 0;
  705. foreach($data["relatedWords"] as $related){
  706. $syn .= ucfirst($related["relationshipType"]) . ": ";
  707. $c = count($related["words"]);
  708. $b = 0;
  709. foreach($related["words"] as $word){
  710. $syn .= trim($this->unescapehtml(strip_tags($word)));
  711. $b++;
  712. if($b !== $c){
  713. $syn .= ", ";
  714. }
  715. }
  716. $u++;
  717. if($u !== count($data["relatedWords"])){
  718. $syn .= ". ";
  719. }
  720. }
  721. $syn .= ")";
  722. }
  723. if(
  724. $c !== 0 &&
  725. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text"
  726. ){
  727. $collection[$data["partOfSpeech"]][$c - 1]["value"] .=
  728. "\n" . $text;
  729. }else{
  730. if(
  731. $c !== 0 &&
  732. (
  733. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text" ||
  734. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "italic"
  735. )
  736. ){
  737. $text = "\n" . $text;
  738. }
  739. $collection[$data["partOfSpeech"]][] =
  740. [
  741. "type" => "text",
  742. "value" => $text
  743. ];
  744. }
  745. if($syn){
  746. $collection[$data["partOfSpeech"]][] = [
  747. "type" => "italic",
  748. "value" => $syn
  749. ];
  750. }
  751. if(isset($data["exampleUses"])){
  752. foreach($data["exampleUses"] as $use){
  753. $collection[$data["partOfSpeech"]][] = [
  754. "type" => "quote",
  755. "value" => $this->unescapehtml(strip_tags($use["text"]))
  756. ];
  757. }
  758. }
  759. if(isset($data["citations"])){
  760. foreach($data["citations"] as $citation){
  761. if(!isset($citation["cite"])){
  762. continue;
  763. }
  764. $value = $this->unescapehtml(strip_tags($citation["cite"]));
  765. if(
  766. isset($citation["source"]) &&
  767. trim($citation["source"]) != ""
  768. ){
  769. $value .= " - " . $this->unescapehtml(strip_tags($citation["source"]));
  770. }
  771. $collection[$data["partOfSpeech"]][] = [
  772. "type" => "quote",
  773. "value" => $value
  774. ];
  775. }
  776. }
  777. }
  778. }
  779. foreach($collection as $key => $items){
  780. $out["answer"][0]["description"][] =
  781. [
  782. "type" => "title",
  783. "value" => $key
  784. ];
  785. $out["answer"][0]["description"] =
  786. array_merge($out["answer"][0]["description"], $items);
  787. }
  788. }
  789. }catch(Exception $e){
  790. // do nothing
  791. }
  792. }
  793. unset($wordnik);
  794. /*
  795. Check for stackoverflow answers
  796. */
  797. // /a.js?p=1&src_id=stack_overflow&from=nlp_qa&id=3390396,2559318&q=how%20can%20i%20check%20for%20undefined%20in%20javascript&s=stackoverflow.com&tl=How%20can%20I%20check%20for%20%22undefined%22%20in%20JavaScript%3F%20%2D%20Stack%20Overflow
  798. // /a.js?p=1&src_id=arqade&from=nlp_qa&id=370293,375682&q=what%20is%20the%20difference%20between%20at%20and%20positioned%20in%20execute&s=gaming.stackexchange.com&tl=minecraft%20java%20edition%20minecraft%20commands%20%2D%20What%20is%20the%20difference
  799. // /a.js?p=1&src_id=unix&from=nlp_qa&id=312754&q=how%20to%20strip%20metadata%20from%20image%20files&s=unix.stackexchange.com&tl=How%20to%20strip%20metadata%20from%20image%20files%20%2D%20Unix%20%26%20Linux%20Stack%20Exchange
  800. preg_match(
  801. '/nrj\(\'(\/a\.js\?.*from=nlp_qa.*)\'\)/U',
  802. $js,
  803. $stack
  804. );
  805. if(isset($stack[1])){
  806. $stack = $stack[1];
  807. try{
  808. $stackjs = $this->get(
  809. $proxy,
  810. "https://duckduckgo.com" . $stack,
  811. [],
  812. ddg::req_xhr
  813. );
  814. if(
  815. !preg_match(
  816. '/^DDG\.duckbar\.failed/',
  817. $stackjs
  818. )
  819. ){
  820. preg_match(
  821. '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U',
  822. $stackjs,
  823. $stackjson
  824. );
  825. $stackjson = json_decode($stackjson[1], true)[0]["data"][0];
  826. $out["answer"][] = [
  827. "title" => $stackjson["Heading"],
  828. "description" => $this->stackoverflow_parse($stackjson["Abstract"]),
  829. "url" => str_replace(["http://", "ddg"], ["https://", ""], $stackjson["AbstractURL"]),
  830. "thumb" => null,
  831. "table" => [],
  832. "sublink" => []
  833. ];
  834. }
  835. }catch(Exception $e){
  836. // do nothing
  837. }
  838. }
  839. /*
  840. Check for musicmatch (lyrics)
  841. */
  842. preg_match(
  843. '/nrj\(\'(\/a\.js\?.*&s=lyrics.*)\'\)/U',
  844. $js,
  845. $lyrics
  846. );
  847. if(isset($lyrics[1])){
  848. $lyrics = $lyrics[1];
  849. try{
  850. $lyricsjs = $this->get(
  851. $proxy,
  852. "https://duckduckgo.com" . $lyrics,
  853. [],
  854. ddg::req_xhr
  855. );
  856. if(
  857. !preg_match(
  858. '/^DDG\.duckbar\.failed/',
  859. $lyricsjs
  860. )
  861. ){
  862. preg_match(
  863. '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U',
  864. $lyricsjs,
  865. $lyricsjson
  866. );
  867. $lyricsjson = json_decode($lyricsjson[1], true)[0]["data"][0];
  868. $title = null;
  869. if(isset($lyricsjson["Heading"])){
  870. $title = $lyricsjson["Heading"];
  871. }elseif(isset($lyricsjson["data"][1]["urlTitle"])){
  872. $title = $lyricsjson["data"][1]["urlTitle"];
  873. }else{
  874. $title = $lyricsjson["data"][0]["song_title"];
  875. }
  876. $description = [
  877. [
  878. "type" => "text",
  879. "value" => null
  880. ]
  881. ];
  882. $parts =
  883. explode(
  884. "<br>",
  885. str_ireplace(
  886. ["<br>", "</br>", "<br/>"],
  887. "<br>",
  888. $lyricsjson["Abstract"]
  889. ),
  890. );
  891. for($i=0; $i<count($parts); $i++){
  892. $description[0]["value"] .= trim($parts[$i]) . "\n";
  893. }
  894. $description[0]["value"] = trim($description[0]["value"]);
  895. $description[] =
  896. [
  897. "type" => "quote",
  898. "value" =>
  899. "Written by " . implode(", ", $lyricsjson["data"][0]["writers"]) .
  900. "\nFrom the album " . $lyricsjson["data"][0]["albums"][0]["title"] .
  901. "\nReleased on the " . date("jS \of F Y", strtotime($lyricsjson["data"][0]["albums"][0]["release_date"]))
  902. ];
  903. $out["answer"][] = [
  904. "title" => $title,
  905. "description" => $description,
  906. "url" => $lyricsjson["AbstractURL"],
  907. "thumb" => null,
  908. "table" => [],
  909. "sublink" => []
  910. ];
  911. }
  912. }catch(Exception $e){
  913. // do nothing
  914. }
  915. }
  916. }
  917. /*
  918. Get related searches
  919. */
  920. preg_match(
  921. '/DDG\.duckbar\.loadModule\(\'related_searches\', ?{[\s\S]*"results":(\[{[\s\S]*}]),"vqd"/U',
  922. $js,
  923. $related
  924. );
  925. if(isset($related[1])){
  926. try{
  927. $related = json_decode($related[1], true);
  928. for($i=0; $i<count($related); $i++){
  929. if(isset($related[$i]["text"])){
  930. array_push($out["related"], $related[$i]["text"]);
  931. }
  932. }
  933. }catch(Exception $e){
  934. // do nothing
  935. }
  936. }
  937. unset($related);
  938. /*
  939. Get answers
  940. */
  941. $answer_count = preg_match_all(
  942. '/DDG\.duckbar\.add\(({.*[\S\s]*})(?:\);|,null,"index"\))/U',
  943. $js . $inithtml,
  944. $answers
  945. );
  946. try{
  947. if(isset($answers[1])){
  948. $answers = $answers[1];
  949. for($i=0; $i<$answer_count; $i++){
  950. $answers[$i] = json_decode($answers[$i], true);
  951. // remove dupes
  952. for($k=0; $k<count($out["answer"]); $k++){
  953. if(
  954. !isset($answers[$i]["data"]["AbstractURL"]) ||
  955. str_replace("_", "%20", $out["answer"][$k]["url"]) == str_replace("_", "%20", $this->sanitizeurl($answers[$i]["data"]["AbstractURL"]))
  956. ){
  957. continue 2;
  958. }
  959. }
  960. // get more related queries
  961. if(
  962. isset($answers[$i]["data"]["RelatedTopics"]) &&
  963. $answers[$i]["data"]["RelatedTopics"] != 0
  964. ){
  965. for($k=0; $k<count($answers[$i]["data"]["RelatedTopics"]); $k++){
  966. if(isset($answers[$i]["data"]["RelatedTopics"][$k]["Result"])){
  967. preg_match(
  968. '/">(.*)<\//',
  969. $answers[$i]["data"]["RelatedTopics"][$k]["Result"],
  970. $label
  971. );
  972. array_push($out["related"], htmlspecialchars_decode(strip_tags($label[1])));
  973. }
  974. }
  975. }
  976. $image = null;
  977. // get image
  978. if(
  979. isset($answers[$i]["data"]["Image"]) &&
  980. !empty($answers[$i]["data"]["Image"]) &&
  981. $answers[$i]["data"]["Image"] != "https://duckduckgo.com/i/"
  982. ){
  983. if(strpos($answers[$i]["data"]["Image"], "https://duckduckgo.com/i/") === true){
  984. $image = $answers[$i]["data"]["Image"];
  985. }else{
  986. if(
  987. strlen($answers[$i]["data"]["Image"]) > 0 &&
  988. $answers[$i]["data"]["Image"][0] == "/"
  989. ){
  990. $answers[$i]["data"]["Image"] = substr($answers[$i]["data"]["Image"], 1);
  991. }
  992. $image = "https://duckduckgo.com/" . $answers[$i]["data"]["Image"];
  993. }
  994. }
  995. $count = count($out["answer"]);
  996. if(isset($answers[$i]["data"]["AbstractText"]) && !empty($answers[$i]["data"]["AbstractText"])){
  997. $description = $this->stackoverflow_parse($answers[$i]["data"]["AbstractText"]);
  998. }elseif(isset($answers[$i]["data"]["Abstract"]) && !empty($answers[$i]["data"]["Abstract"])){
  999. $description = $this->stackoverflow_parse($answers[$i]["data"]["Abstract"]);
  1000. }elseif(isset($answers[$i]["data"]["Answer"]) && !empty($answers[$i]["data"]["Answer"])){
  1001. $description = $this->stackoverflow_parse($answers[$i]["data"]["Answer"]);
  1002. }else{
  1003. $description = [];
  1004. }
  1005. if(isset($answers[$i]["data"]["Heading"]) && !empty($answers[$i]["data"]["Heading"])){
  1006. $title = $this->unescapehtml($answers[$i]["data"]["Heading"]);
  1007. }else{
  1008. // no title, ignore bs
  1009. continue;
  1010. //$title = null;
  1011. }
  1012. if(isset($answers[$i]["data"]["AbstractURL"]) && !empty($answers[$i]["data"]["AbstractURL"])){
  1013. $url = $answers[$i]["data"]["AbstractURL"];
  1014. }else{
  1015. $url = null;
  1016. }
  1017. $out["answer"][$count] = [
  1018. "title" => $title,
  1019. "description" => $description,
  1020. "url" => $this->sanitizeurl($url),
  1021. "thumb" => $image,
  1022. "table" => [],
  1023. "sublink" => []
  1024. ];
  1025. if(isset($answers[$i]["data"]["Infobox"]["content"])){
  1026. for($k=0; $k<count($answers[$i]["data"]["Infobox"]["content"]); $k++){
  1027. // populate table
  1028. if($answers[$i]["data"]["Infobox"]["content"][$k]["data_type"] == "string"){
  1029. $out["answer"][$count]["table"][$answers[$i]["data"]["Infobox"]["content"][$k]["label"]] =
  1030. $answers[$i]["data"]["Infobox"]["content"][$k]["value"];
  1031. continue;
  1032. }
  1033. $url = "";
  1034. $type = "Website";
  1035. switch($answers[$i]["data"]["Infobox"]["content"][$k]["data_type"]){
  1036. case "official_site":
  1037. case "official_website":
  1038. $type = "Website";
  1039. break;
  1040. case "wikipedia": $type = "Wikipedia"; break;
  1041. case "itunes": $type = "iTunes"; break;
  1042. case "amazon": $type = "Amazon"; break;
  1043. case "imdb_title_id":
  1044. case "imdb_id":
  1045. case "imdb_name_id":
  1046. $type = "IMDb";
  1047. $delim = substr($answers[$i]["data"]["Infobox"]["content"][$k]["value"], 0, 2);
  1048. if($delim == "nm"){
  1049. $url = "https://www.imdb.com/name/";
  1050. }elseif($delim == "tt"){
  1051. $url = "https://www.imdb.com/title/";
  1052. }elseif($delim == "co"){
  1053. $url = "https://www.imdb.com/search/title/?companies=";
  1054. }else{
  1055. $url = "https://www.imdb.com/title/";
  1056. }
  1057. break;
  1058. case "imdb_name_id": $url = "https://www.imdb.com/name/"; $type = "IMDb"; break;
  1059. case "twitter_profile": $url = "https://twitter.com/"; $type = "Twitter"; break;
  1060. case "instagram_profile": $url = "https://instagram.com/"; $type = "Instagram"; break;
  1061. case "facebook_profile": $url = "https://facebook.com/"; $type = "Facebook"; break;
  1062. case "spotify_artist_id": $url = "https://open.spotify.com/artist/"; $type = "Spotify"; break;
  1063. case "rotten_tomatoes": $url = "https://rottentomatoes.com/"; $type = "Rotten Tomatoes"; break;
  1064. case "youtube_channel": $url = "https://youtube.com/channel/"; $type = "YouTube"; break;
  1065. case "soundcloud_id": $url = "https://soundcloud.com/"; $type = "SoundCloud"; break;
  1066. default:
  1067. continue 2;
  1068. }
  1069. // populate sublinks
  1070. $out["answer"][$count]["sublink"][$type] =
  1071. $url . $answers[$i]["data"]["Infobox"]["content"][$k]["value"];
  1072. }
  1073. }
  1074. }
  1075. }
  1076. }catch(Exception $e){
  1077. // do nothing
  1078. }
  1079. /*
  1080. Get shitcoin conversions
  1081. */
  1082. if($extendedsearch){
  1083. if(
  1084. preg_match(
  1085. '/"https?:\/\/(?:www\.coinbase\.com\/converter\/([a-z0-9]+)\/([a-z0-9]+)|changelly\.com\/exchange\/([a-z0-9]+)\/([a-z0-9]+)|coinmarketcap\.com\/currencies\/[a-z0-9]+\/([a-z0-9]+)\/([a-z0-9]+))\/?"/',
  1086. $js,
  1087. $shitcoins
  1088. )
  1089. ){
  1090. $shitcoins = array_values(array_filter($shitcoins));
  1091. preg_match(
  1092. '/(?:[\s,.]*[0-9]+)+/',
  1093. $search,
  1094. $amount
  1095. );
  1096. if(count($amount) === 1){
  1097. $amount = (float)str_replace([" ", ","], ["", "."], $amount[0]);
  1098. }else{
  1099. $amount = 1;
  1100. }
  1101. try{
  1102. $description = [];
  1103. $shitcoinjs = $this->get(
  1104. $proxy,
  1105. "https://duckduckgo.com/js/spice/cryptocurrency/{$shitcoins[1]}/{$shitcoins[2]}/1",
  1106. [],
  1107. ddg::req_xhr
  1108. );
  1109. preg_match(
  1110. '/ddg_spice_cryptocurrency\(\s*({[\S\s]*})\s*\);/',
  1111. $shitcoinjs,
  1112. $shitcoinjson
  1113. );
  1114. $shitcoinjson = json_decode($shitcoinjson[1], true);
  1115. if(
  1116. !isset($shitcoinjson["error"]) &&
  1117. $shitcoinjson["status"]["error_code"] == 0
  1118. ){
  1119. $shitcoinjson = $shitcoinjson["data"];
  1120. $array_values = array_values($shitcoinjson["quote"])[0];
  1121. if($amount != 1){
  1122. // show conversion
  1123. $description[] = [
  1124. "type" => "title",
  1125. "value" => "Conversion"
  1126. ];
  1127. $description[] = [
  1128. "type" => "text",
  1129. "value" =>
  1130. "{$amount} {$shitcoinjson["name"]} ({$shitcoinjson["symbol"]}) = " . $this->number_format($array_values["price"] * $amount) . " " . strtoupper($shitcoins[2]) . "\n" .
  1131. "{$amount} " . strtoupper($shitcoins[2]) . " = " . $this->number_format((1 / $array_values["price"]) * $amount) . " {$shitcoinjson["symbol"]}"
  1132. ];
  1133. }
  1134. $description[] = [
  1135. "type" => "title",
  1136. "value" => "Current rates"
  1137. ];
  1138. // rates
  1139. $description[] = [
  1140. "type" => "text",
  1141. "value" =>
  1142. "1 {$shitcoinjson["name"]} ({$shitcoinjson["symbol"]}) = " . $this->number_format($array_values["price"]) . " " . strtoupper($shitcoins[2]) . "\n" .
  1143. "1 " . strtoupper($shitcoins[2]) . " = " . $this->number_format(1 / $array_values["price"]) . " {$shitcoinjson["symbol"]}"
  1144. ];
  1145. $description[] = [
  1146. "type" => "quote",
  1147. "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($shitcoinjson["last_updated"]))
  1148. ];
  1149. $out["answer"][] = [
  1150. "title" => $shitcoinjson["name"] . " (" . strtoupper($shitcoins[1]) . ") & " . strtoupper($shitcoins[2]) . " market",
  1151. "description" => $description,
  1152. "url" => "https://coinmarketcap.com/converter/" . strtoupper($shitcoins[1]) . "/" . strtoupper($shitcoins[2]) . "/?amt={$amount}",
  1153. "thumb" => null,
  1154. "table" => [],
  1155. "sublink" => []
  1156. ];
  1157. }
  1158. }catch(Exception $e){
  1159. // do nothing
  1160. }
  1161. }else{
  1162. /*
  1163. Get currency conversion
  1164. */
  1165. if(
  1166. preg_match(
  1167. '/"https:\/\/www\.xe\.com\/currencyconverter\/convert\/\?From=([A-Z0-9]+)&To=([A-Z0-9]+)"/',
  1168. $js,
  1169. $currencies
  1170. )
  1171. ){
  1172. preg_match(
  1173. '/(?:[\s,.]*[0-9]+)+/',
  1174. $search,
  1175. $amount
  1176. );
  1177. if(count($amount) === 1){
  1178. $amount = (float)str_replace([" ", ","], ["", "."], $amount[0]);
  1179. }else{
  1180. $amount = 1;
  1181. }
  1182. try{
  1183. $currencyjs = $this->get(
  1184. $proxy,
  1185. "https://duckduckgo.com/js/spice/currency/{$amount}/" . strtolower($currencies[1]) . "/" . strtolower($currencies[2]),
  1186. [],
  1187. ddg::req_xhr
  1188. );
  1189. preg_match(
  1190. '/ddg_spice_currency\(\s*({[\S\s]*})\s*\);/',
  1191. $currencyjs,
  1192. $currencyjson
  1193. );
  1194. $currencyjson = json_decode($currencyjson[1], true);
  1195. if(empty($currencyjson["headers"]["description"])){
  1196. $currencyjson = $currencyjson["conversion"];
  1197. $description = [];
  1198. if($amount != 1){
  1199. $description[] =
  1200. [
  1201. "type" => "title",
  1202. "value" => "Conversion"
  1203. ];
  1204. $description[] =
  1205. [
  1206. "type" => "text",
  1207. "value" =>
  1208. $this->number_format($currencyjson["from-amount"]) . " {$currencyjson["from-currency-symbol"]} = " .
  1209. $this->number_format($currencyjson["converted-amount"]) . " {$currencyjson["to-currency-symbol"]}"
  1210. ];
  1211. }
  1212. $description[] =
  1213. [
  1214. "type" => "title",
  1215. "value" => "Current rates"
  1216. ];
  1217. $description[] =
  1218. [
  1219. "type" => "text",
  1220. "value" =>
  1221. "{$currencyjson["conversion-rate"]}\n" .
  1222. "{$currencyjson["conversion-inverse"]}"
  1223. ];
  1224. $description[] =
  1225. [
  1226. "type" => "quote",
  1227. "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($currencyjson["rate-utc-timestamp"]))
  1228. ];
  1229. $out["answer"][] = [
  1230. "title" =>
  1231. "{$currencyjson["from-currency-name"]} ({$currencyjson["from-currency-symbol"]}) to " .
  1232. "{$currencyjson["to-currency-name"]} ({$currencyjson["to-currency-symbol"]})",
  1233. "description" => $description,
  1234. "url" => "https://www.xe.com/currencyconverter/convert/?Amount={$amount}&From={$currencies[1]}&To={$currencies[2]}",
  1235. "thumb" => null,
  1236. "table" => [],
  1237. "sublink" => []
  1238. ];
  1239. }
  1240. }catch(Exception $e){
  1241. // do nothing
  1242. }
  1243. }
  1244. }
  1245. }
  1246. /*
  1247. Get small answer
  1248. */
  1249. preg_match(
  1250. '/DDG\.ready\(function ?\(\) ?{DDH\.add\(({[\S\s]+}),"index"\)}\)/U',
  1251. $inithtml,
  1252. $smallanswer
  1253. );
  1254. if(isset($smallanswer[1])){
  1255. $smallanswer = json_decode($smallanswer[1], true);
  1256. if(
  1257. !isset($smallanswer["require"]) &&
  1258. isset($smallanswer["data"]["title"])
  1259. ){
  1260. if(isset($smallanswer["data"]["url"])){
  1261. $url = $this->unescapehtml($smallanswer["data"]["url"]);
  1262. }elseif(isset($smallanswer["meta"]["sourceUrl"])){
  1263. $url = $this->unescapehtml($smallanswer["meta"]["sourceUrl"]);
  1264. }else{
  1265. $url = null;
  1266. }
  1267. $out["answer"] = [
  1268. [
  1269. "title" => $this->unescapehtml($smallanswer["data"]["title"]),
  1270. "description" => [],
  1271. "url" => $this->sanitizeurl($url),
  1272. "thumb" => null,
  1273. "table" => [],
  1274. "sublink" => []
  1275. ],
  1276. ...$out["answer"]
  1277. ];
  1278. if(isset($smallanswer["data"]["subtitle"])){
  1279. $out["answer"][0]["description"][] =
  1280. [
  1281. "type" => "text",
  1282. "value" => isset($smallanswer["data"]["subtitle"]) ? $this->unescapehtml($smallanswer["data"]["subtitle"]) : null
  1283. ];
  1284. }
  1285. }
  1286. }
  1287. unset($inithtml);
  1288. unset($answers);
  1289. unset($answer_count);
  1290. /*
  1291. Get spelling autocorrect
  1292. */
  1293. preg_match(
  1294. '/DDG\.page\.showMessage\(\'spelling\',({[\S\s]+})\)/U',
  1295. $js,
  1296. $spelling
  1297. );
  1298. if(isset($spelling[1])){
  1299. $spelling = json_decode($spelling[1], true);
  1300. switch((int)$spelling["qc"]){
  1301. case 1:
  1302. case 3:
  1303. case 5:
  1304. $type = "including";
  1305. break;
  1306. default:
  1307. $type = "not_many";
  1308. break;
  1309. }
  1310. $out["spelling"] = [
  1311. "type" => $type,
  1312. "using" => $this->unescapehtml(strip_tags($spelling["suggestion"])),
  1313. "correction" => $this->unescapehtml(strip_tags($spelling["recourseText"]))
  1314. ];
  1315. }
  1316. unset($spelling);
  1317. /*
  1318. Get web results
  1319. */
  1320. preg_match(
  1321. '/DDG\.pageLayout\.load\(\'d\', ?(\[{"[\S\s]*"}])\)/U',
  1322. $js,
  1323. $web
  1324. );
  1325. if(isset($web[1])){
  1326. try{
  1327. $web = json_decode($web[1], true);
  1328. for($i=0; $i<count($web); $i++){
  1329. // ignore google placeholder + fake next page
  1330. if(
  1331. isset($web[$i]["t"]) &&
  1332. (
  1333. $web[$i]["t"] == "EOP" ||
  1334. $web[$i]["t"] == "EOF"
  1335. ) &&
  1336. strpos($web[$i]["c"], "://www.google.") !== false
  1337. ){
  1338. break;
  1339. }
  1340. // store next page token
  1341. if(isset($web[$i]["n"])){
  1342. $out["npt"] = $this->backend->store($web[$i]["n"] . "&biaexp=b&eslexp=a&litexp=c&msvrtexp=b&wrap=1", "web", $proxy);
  1343. continue;
  1344. }
  1345. // ignore malformed data
  1346. if(!isset($web[$i]["t"])){
  1347. continue;
  1348. }
  1349. $sublinks = [];
  1350. if(isset($web[$i]["l"])){
  1351. for($k=0; $k<count($web[$i]["l"]); $k++){
  1352. if(
  1353. !isset($web[$i]["l"][$k]["targetUrl"]) ||
  1354. !isset($web[$i]["l"][$k]["text"])
  1355. ){
  1356. continue;
  1357. }
  1358. array_push(
  1359. $sublinks,
  1360. [
  1361. "title" => $this->titledots($this->unescapehtml($web[$i]["l"][$k]["text"])),
  1362. "date" => null,
  1363. "description" => isset($web[$i]["l"][$k]["snippet"]) ? $this->titledots($this->unescapehtml($web[$i]["l"][$k]["snippet"])) : null,
  1364. "url" => $this->sanitizeurl($web[$i]["l"][$k]["targetUrl"])
  1365. ]
  1366. );
  1367. }
  1368. }
  1369. if(
  1370. preg_match(
  1371. '/^<span class="result__type">PDF<\/span>/',
  1372. $web[$i]["t"]
  1373. )
  1374. ){
  1375. $type = "pdf";
  1376. $web[$i]["t"] =
  1377. str_replace(
  1378. '<span class="result__type">PDF</span>',
  1379. "",
  1380. $web[$i]["t"]
  1381. );
  1382. }else{
  1383. $type = "web";
  1384. }
  1385. if(isset($web[$i]["e"])){
  1386. $date = strtotime($web[$i]["e"]);
  1387. }else{
  1388. $date = null;
  1389. }
  1390. array_push(
  1391. $out["web"],
  1392. [
  1393. "title" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["t"]))),
  1394. "description" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["a"]))),
  1395. "url" => isset($web[$i]["u"]) ? $this->sanitizeurl($web[$i]["u"]) : $this->sanitizeurl($web[$i]["c"]),
  1396. "date" => $date,
  1397. "type" => $type,
  1398. "thumb" =>
  1399. [
  1400. "url" => null,
  1401. "ratio" => null
  1402. ],
  1403. "sublink" => $sublinks,
  1404. "table" => []
  1405. ]
  1406. );
  1407. }
  1408. }catch(Exception $e){
  1409. // do nothing
  1410. }
  1411. }
  1412. unset($web);
  1413. /*
  1414. Get images
  1415. */
  1416. preg_match(
  1417. '/DDG\.duckbar\.load\(\'images\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1418. $js,
  1419. $images
  1420. );
  1421. if(isset($images[1])){
  1422. try{
  1423. $images = json_decode($images[1], true);
  1424. for($i=0; $i<count($images); $i++){
  1425. if(
  1426. !isset($images[$i]["title"]) ||
  1427. !isset($images[$i]["image"]) ||
  1428. !isset($images[$i]["thumbnail"]) ||
  1429. !isset($images[$i]["width"]) ||
  1430. !isset($images[$i]["height"])
  1431. ){
  1432. continue;
  1433. }
  1434. $ratio =
  1435. $this->bingratio(
  1436. (int)$images[$i]["width"],
  1437. (int)$images[$i]["height"]
  1438. );
  1439. array_push(
  1440. $out["image"],
  1441. [
  1442. "title" => $this->titledots($this->unescapehtml($images[$i]["title"])),
  1443. "source" => [
  1444. [
  1445. "url" => $images[$i]["image"],
  1446. "width" => (int)$images[$i]["width"],
  1447. "height" => (int)$images[$i]["height"]
  1448. ],
  1449. [
  1450. "url" => $this->bingimg($images[$i]["thumbnail"]),
  1451. "width" => $ratio[0],
  1452. "height" => $ratio[1]
  1453. ]
  1454. ],
  1455. "url" => $this->sanitizeurl($images[$i]["url"])
  1456. ]
  1457. );
  1458. }
  1459. }catch(Exception $e){
  1460. // do nothing
  1461. }
  1462. }
  1463. unset($images);
  1464. /*
  1465. Get videos
  1466. */
  1467. preg_match(
  1468. '/DDG\.duckbar\.load\(\'videos\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1469. $js,
  1470. $videos
  1471. );
  1472. if(isset($videos[1])){
  1473. try{
  1474. $videos = json_decode($videos[1], true);
  1475. for($i=0; $i<count($videos); $i++){
  1476. $cachekey = false;
  1477. foreach(["large", "medium", "small"] as &$key){
  1478. if(isset($videos[$i]["images"][$key])){
  1479. $cachekey = $key;
  1480. break;
  1481. }
  1482. }
  1483. if(
  1484. !isset($videos[$i]["title"]) ||
  1485. !isset($videos[$i]["description"]) ||
  1486. $cachekey === false ||
  1487. !isset($videos[$i]["content"])
  1488. ){
  1489. continue;
  1490. }
  1491. array_push(
  1492. $out["video"],
  1493. [
  1494. "title" => $this->titledots($this->unescapehtml($videos[$i]["title"])),
  1495. "description" => $videos[$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($videos[$i]["description"])),
  1496. "date" => $videos[$i]["published"] == "" ? null : strtotime($videos[$i]["published"]),
  1497. "duration" => $videos[$i]["duration"] == 0 ? null : $this->hmstoseconds($videos[$i]["duration"]),
  1498. "views" => $videos[$i]["statistics"]["viewCount"] == 0 ? null : $videos[$i]["statistics"]["viewCount"],
  1499. "thumb" =>
  1500. [
  1501. "url" => $this->bingimg($videos[$i]["images"][$cachekey]),
  1502. "ratio" => "16:9"
  1503. ],
  1504. "url" => $this->sanitizeurl($videos[$i]["content"])
  1505. ]
  1506. );
  1507. }
  1508. }catch(Exception $e){
  1509. // do nothing
  1510. }
  1511. }
  1512. unset($videos);
  1513. /*
  1514. Get news
  1515. */
  1516. preg_match(
  1517. '/DDG\.duckbar\.load\(\'news\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1518. $js,
  1519. $news
  1520. );
  1521. if(isset($news[1])){
  1522. try{
  1523. $news = json_decode($news[1], true);
  1524. for($i=0; $i<count($news); $i++){
  1525. if(
  1526. !isset($news[$i]["title"]) ||
  1527. !isset($news[$i]["excerpt"]) ||
  1528. !isset($news[$i]["url"])
  1529. ){
  1530. continue;
  1531. }
  1532. array_push(
  1533. $out["news"],
  1534. [
  1535. "title" => $this->titledots($this->unescapehtml($news[$i]["title"])),
  1536. "description" => $this->titledots($this->unescapehtml(strip_tags($news[$i]["excerpt"]))),
  1537. "date" => isset($news[$i]["date"]) ? (int)$news[$i]["date"] : null,
  1538. "thumb" =>
  1539. [
  1540. "url" => isset($news[$i]["image"]) ? $news[$i]["image"] : null,
  1541. "ratio" => "16:9"
  1542. ],
  1543. "url" => $this->sanitizeurl($news[$i]["url"])
  1544. ]
  1545. );
  1546. }
  1547. }catch(Exception $e){
  1548. // do nothing
  1549. }
  1550. }
  1551. return $out;
  1552. }
  1553. public function image($get){
  1554. if($get["npt"]){
  1555. [$npt, $proxy] = $this->backend->get($get["npt"], "images");
  1556. try{
  1557. $json = json_decode($this->get(
  1558. $proxy,
  1559. "https://duckduckgo.com/i.js?" . $npt,
  1560. [],
  1561. ddg::req_xhr
  1562. ), true);
  1563. }catch(Exception $err){
  1564. throw new Exception("Failed to get i.js");
  1565. }
  1566. }else{
  1567. $search = $get["s"];
  1568. if(strlen($search) === 0){
  1569. throw new Exception("Search term is empty!");
  1570. }
  1571. $proxy = $this->backend->get_ip();
  1572. $country = $get["country"];
  1573. $nsfw = $get["nsfw"];
  1574. $date = $get["date"];
  1575. $size = $get["size"];
  1576. $color = $get["color"];
  1577. $type = $get["type"];
  1578. $layout = $get["layout"];
  1579. $license = $get["license"];
  1580. $filter = [];
  1581. $get_filters = [
  1582. "q" => $search,
  1583. "iax" => "images",
  1584. "ia" => "images"
  1585. ];
  1586. if($date != "any"){ $filter[] = "time:$date"; }
  1587. if($size != "any"){ $filter[] = "size:$size"; }
  1588. if($color != "any"){ $filter[] = "color:$color"; }
  1589. if($type != "any"){ $filter[] = "type:$type"; }
  1590. if($layout != "any"){ $filter[] = "layout:$layout"; }
  1591. if($license != "any"){ $filter[] = "license:$license"; }
  1592. $filter = implode(",", $filter);
  1593. if($filter != ""){
  1594. $get_filters["iaf"] = $filter;
  1595. }
  1596. switch($nsfw){
  1597. case "yes": $get_filters["kp"] = "-2"; break;
  1598. case "no": $get_filters["kp"] = "-1"; break;
  1599. }
  1600. try{
  1601. $html = $this->get(
  1602. $proxy,
  1603. "https://duckduckgo.com",
  1604. $get_filters,
  1605. ddg::req_web
  1606. );
  1607. }catch(Exception $err){
  1608. throw new Exception("Failed to get html");
  1609. }
  1610. preg_match(
  1611. '/vqd=([0-9-]+)/',
  1612. $html,
  1613. $vqd
  1614. );
  1615. if(!isset($vqd[1])){
  1616. throw new Exception("Failed to get vqd token");
  1617. }
  1618. $vqd = $vqd[1];
  1619. // @TODO: s param = image offset
  1620. $js_params = [
  1621. "l" => $country,
  1622. "o" => "json",
  1623. "q" => $search,
  1624. "vqd" => $vqd
  1625. ];
  1626. switch($nsfw){
  1627. case "yes": $js_params["p"] = "-1"; break;
  1628. case "no": $js_params["p"] = "1"; break;
  1629. }
  1630. if(empty($filter)){
  1631. $js_params["f"] = "1";
  1632. }else{
  1633. $js_params["f"] = $filter;
  1634. }
  1635. try{
  1636. $json = json_decode($this->get(
  1637. $proxy,
  1638. "https://duckduckgo.com/i.js",
  1639. $js_params,
  1640. ddg::req_xhr
  1641. ), true);
  1642. }catch(Exception $err){
  1643. throw new Exception("Failed to get i.js");
  1644. }
  1645. }
  1646. $out = [
  1647. "status" => "ok",
  1648. "npt" => null,
  1649. "image" => []
  1650. ];
  1651. if(isset($json["next"])){
  1652. if(!isset($vqd)){
  1653. $vqd = array_values($json["vqd"])[0];
  1654. }
  1655. $out["npt"] =
  1656. $this->backend->store(
  1657. explode("?", $json["next"])[1] . "&vqd=" .
  1658. $vqd,
  1659. "images",
  1660. $proxy
  1661. );
  1662. }
  1663. for($i=0; $i<count($json["results"]); $i++){
  1664. $bingimg = $this->bingimg($json["results"][$i]["thumbnail"]);
  1665. $ratio =
  1666. $this->bingratio(
  1667. (int)$json["results"][$i]["width"],
  1668. (int)$json["results"][$i]["height"]
  1669. );
  1670. $out["image"][] = [
  1671. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1672. "source" => [
  1673. [
  1674. "url" => $json["results"][$i]["image"],
  1675. "width" => (int)$json["results"][$i]["width"],
  1676. "height" => (int)$json["results"][$i]["height"]
  1677. ],
  1678. [
  1679. "url" => $bingimg,
  1680. "width" => $ratio[0],
  1681. "height" => $ratio[1],
  1682. ]
  1683. ],
  1684. "url" => $this->sanitizeurl($json["results"][$i]["url"])
  1685. ];
  1686. }
  1687. return $out;
  1688. }
  1689. public function video($get){
  1690. if($get["npt"]){
  1691. [$npt, $proxy] = $this->backend->get($get["npt"], "videos");
  1692. try{
  1693. $json = json_decode($this->get(
  1694. $proxy,
  1695. "https://duckduckgo.com/v.js?" .
  1696. $npt,
  1697. [],
  1698. ddg::req_xhr
  1699. ), true);
  1700. }catch(Exception $err){
  1701. throw new Exception("Failed to get v.js");
  1702. }
  1703. }else{
  1704. $search = $get["s"];
  1705. if(strlen($search) === 0){
  1706. throw new Exception("Search term is empty!");
  1707. }
  1708. $proxy = $this->backend->get_ip();
  1709. $country = $get["country"];
  1710. $nsfw = $get["nsfw"];
  1711. $date = $get["date"];
  1712. $resolution = $get["resolution"];
  1713. $duration = $get["duration"];
  1714. $license = $get["license"];
  1715. $filter = [];
  1716. $get_filters = [
  1717. "q" => $search,
  1718. "iax" => "videos",
  1719. "ia" => "videos"
  1720. ];
  1721. switch($nsfw){
  1722. case "yes": $get_filters["kp"] = "-2"; break;
  1723. case "no": $get_filters["kp"] = "-1"; break;
  1724. }
  1725. if($date != "any"){ $filter[] = "publishedAfter:{$date}"; }
  1726. if($resolution != "any"){ $filter[] = "videoDefinition:{$resolution}"; }
  1727. if($duration != "any"){ $filter[] = "videoDuration:{$duration}"; }
  1728. if($license != "any"){ $filter[] = "videoLicense:{$license}"; }
  1729. $filter = implode(",", $filter);
  1730. try{
  1731. $html = $this->get(
  1732. $proxy,
  1733. "https://duckduckgo.com",
  1734. $get_filters,
  1735. ddg::req_web
  1736. );
  1737. }catch(Exception $err){
  1738. throw new Exception("Failed to get html");
  1739. }
  1740. preg_match(
  1741. '/vqd=([0-9-]+)/',
  1742. $html,
  1743. $vqd
  1744. );
  1745. if(!isset($vqd[1])){
  1746. throw new Exception("Failed to get vqd token");
  1747. }
  1748. $vqd = $vqd[1];
  1749. try{
  1750. $json = json_decode($this->get(
  1751. $proxy,
  1752. "https://duckduckgo.com/v.js",
  1753. [
  1754. "l" => "us-en",
  1755. "o" => "json",
  1756. "sr" => 1,
  1757. "q" => $search,
  1758. "vqd" => $vqd,
  1759. "f" => $filter,
  1760. "p" => $get_filters["kp"]
  1761. ],
  1762. ddg::req_xhr
  1763. ), true);
  1764. }catch(Exception $err){
  1765. throw new Exception("Failed to get v.js");
  1766. }
  1767. }
  1768. $out = [
  1769. "status" => "ok",
  1770. "npt" => null,
  1771. "video" => [],
  1772. "author" => [],
  1773. "livestream" => [],
  1774. "playlist" => [],
  1775. "reel" => []
  1776. ];
  1777. if(isset($json["next"])){
  1778. $out["npt"] =
  1779. $this->backend->store(
  1780. explode("?", $json["next"])[1],
  1781. "videos",
  1782. $proxy
  1783. );
  1784. }
  1785. for($i=0; $i<count($json["results"]); $i++){
  1786. $cachekey = false;
  1787. foreach(["large", "medium", "small"] as &$key){
  1788. if(isset($json["results"][$i]["images"][$key])){
  1789. $cachekey = $key;
  1790. break;
  1791. }
  1792. }
  1793. if(
  1794. !isset($json["results"][$i]["title"]) ||
  1795. !isset($json["results"][$i]["description"]) ||
  1796. $cachekey === false ||
  1797. !isset($json["results"][$i]["content"])
  1798. ){
  1799. continue;
  1800. }
  1801. array_push(
  1802. $out["video"],
  1803. [
  1804. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1805. "description" => $json["results"][$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($json["results"][$i]["description"])),
  1806. "author" => [
  1807. "name" => empty($json["results"][$i]["uploader"]) ? null : $this->unescapehtml($json["results"][$i]["uploader"]),
  1808. "url" => null,
  1809. "avatar" => null
  1810. ],
  1811. "date" => $json["results"][$i]["published"] == "" ? null : strtotime($json["results"][$i]["published"]),
  1812. "duration" => $json["results"][$i]["duration"] == 0 ? null : $this->hmstoseconds($json["results"][$i]["duration"]),
  1813. "views" => $json["results"][$i]["statistics"]["viewCount"] == 0 ? null : $json["results"][$i]["statistics"]["viewCount"],
  1814. "thumb" => [
  1815. "url" => $this->bingimg($json["results"][$i]["images"][$cachekey]),
  1816. "ratio" => "16:9"
  1817. ],
  1818. "url" => $this->sanitizeurl($json["results"][$i]["content"])
  1819. ]
  1820. );
  1821. }
  1822. return $out;
  1823. }
  1824. public function news($get){
  1825. if($get["npt"]){
  1826. [$req, $proxy] = $this->backend->get($get["npt"], "news");
  1827. try{
  1828. $json = json_decode($this->get(
  1829. $proxy,
  1830. "https://duckduckgo.com/news.js?" .
  1831. $req,
  1832. [],
  1833. ddg::req_xhr
  1834. ), true);
  1835. }catch(Exception $err){
  1836. throw new Exception("Failed to get news.js");
  1837. }
  1838. }else{
  1839. $search = $get["s"];
  1840. if(strlen($search) === 0){
  1841. throw new Exception("Search term is empty!");
  1842. }
  1843. $proxy = $this->backend->get_ip();
  1844. $country = $get["country"];
  1845. $nsfw = $get["nsfw"];
  1846. $date = $get["date"];
  1847. $get_params = [
  1848. "q" => $search,
  1849. "iar" => "news",
  1850. "ia" => "news"
  1851. ];
  1852. switch($nsfw){
  1853. case "yes": $get_filters["kp"] = "-2"; break;
  1854. case "maybe": $get_filters["kp"] = "-1"; break;
  1855. case "no": $get_filters["kp"] = "1"; break;
  1856. }
  1857. if($date != "any"){
  1858. $get_params["df"] = $date;
  1859. }
  1860. try{
  1861. $html = $this->get(
  1862. $proxy,
  1863. "https://duckduckgo.com",
  1864. $get_params,
  1865. ddg::req_web
  1866. );
  1867. }catch(Exception $err){
  1868. throw new Exception("Failed to get html");
  1869. }
  1870. preg_match(
  1871. '/vqd=([0-9-]+)/',
  1872. $html,
  1873. $vqd
  1874. );
  1875. if(!isset($vqd[1])){
  1876. throw new Exception("Failed to get vqd token");
  1877. }
  1878. $vqd = $vqd[1];
  1879. try{
  1880. $js_params = [
  1881. "l" => $country,
  1882. "o" => "json",
  1883. "noamp" => "1",
  1884. "q" => $search,
  1885. "vqd" => $vqd,
  1886. "p" => $get_filters["kp"]
  1887. ];
  1888. if($date != "any"){
  1889. $js_params["df"] = $date;
  1890. }else{
  1891. $js_params["df"] = "";
  1892. }
  1893. $json = json_decode($this->get(
  1894. $proxy,
  1895. "https://duckduckgo.com/news.js",
  1896. $js_params,
  1897. ddg::req_xhr
  1898. ), true);
  1899. }catch(Exception $err){
  1900. throw new Exception("Failed to get news.js");
  1901. }
  1902. }
  1903. $out = [
  1904. "status" => "ok",
  1905. "npt" => null,
  1906. "news" => []
  1907. ];
  1908. if(isset($json["next"])){
  1909. $out["npt"] =
  1910. $this->backend->store(
  1911. explode("?", $json["next"])[1],
  1912. "news",
  1913. $proxy
  1914. );
  1915. }
  1916. for($i=0; $i<count($json["results"]); $i++){
  1917. $out["news"][] = [
  1918. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1919. "author" => $this->unescapehtml($json["results"][$i]["source"]),
  1920. "description" => $this->titledots($this->unescapehtml(strip_tags($json["results"][$i]["excerpt"]))),
  1921. "date" => $json["results"][$i]["date"],
  1922. "thumb" =>
  1923. [
  1924. "url" => isset($json["results"][$i]["image"]) ? $json["results"][$i]["image"] : null,
  1925. "ratio" => "16:9"
  1926. ],
  1927. "url" => $this->sanitizeurl($json["results"][$i]["url"])
  1928. ];
  1929. }
  1930. return $out;
  1931. }
  1932. private function hmstoseconds($time){
  1933. $parts = explode(":", $time, 3);
  1934. $time = 0;
  1935. if(count($parts) === 3){
  1936. // hours
  1937. $time = $time + ((int)$parts[0] * 3600);
  1938. array_shift($parts);
  1939. }
  1940. if(count($parts) === 2){
  1941. // minutes
  1942. $time = $time + ((int)$parts[0] * 60);
  1943. array_shift($parts);
  1944. }
  1945. // seconds
  1946. $time = $time + (int)$parts[0];
  1947. return $time;
  1948. }
  1949. private function titledots($title){
  1950. $substr = substr($title, -3);
  1951. if(
  1952. $substr == "..." ||
  1953. $substr == "…"
  1954. ){
  1955. return trim(substr($title, 0, -3));
  1956. }
  1957. return trim($title);
  1958. }
  1959. private function unescapehtml($str){
  1960. return html_entity_decode(
  1961. str_replace(
  1962. [
  1963. "<br>",
  1964. "<br/>",
  1965. "</br>",
  1966. "<BR>",
  1967. "<BR/>",
  1968. "</BR>",
  1969. ],
  1970. "\n",
  1971. $str
  1972. ),
  1973. ENT_QUOTES | ENT_XML1, 'UTF-8'
  1974. );
  1975. }
  1976. private function bingimg($url){
  1977. $parse = parse_url($url);
  1978. parse_str($parse["query"], $parts);
  1979. return "https://" . $parse["host"] . "/th?id=" . urlencode($parts["id"]);
  1980. }
  1981. private function appendtext($payload, &$text, &$index){
  1982. if(trim($payload) == ""){
  1983. return;
  1984. }
  1985. if(
  1986. $index !== 0 &&
  1987. $text[$index - 1]["type"] == "text"
  1988. ){
  1989. $text[$index - 1]["value"] .= preg_replace('/ $/', " ", $payload);
  1990. }else{
  1991. $text[] = [
  1992. "type" => "text",
  1993. "value" => preg_replace('/ $/', " ", $payload)
  1994. ];
  1995. $index++;
  1996. }
  1997. }
  1998. private function stackoverflow_parse($html){
  1999. $i = 0;
  2000. $answer = [];
  2001. $this->fuckhtml->load($html);
  2002. $tags = $this->fuckhtml->getElementsByTagName("*");
  2003. if(count($tags) === 0){
  2004. return [
  2005. [
  2006. "type" => "text",
  2007. "value" => htmlspecialchars_decode($html)
  2008. ]
  2009. ];
  2010. }
  2011. foreach($tags as $snippet){
  2012. switch($snippet["tagName"]){
  2013. case "p":
  2014. $this->fuckhtml->load($snippet["innerHTML"]);
  2015. $codetags =
  2016. $this->fuckhtml
  2017. ->getElementsByTagName("*");
  2018. $tmphtml = $snippet["innerHTML"];
  2019. foreach($codetags as $tag){
  2020. if(!isset($tag["outerHTML"])){
  2021. continue;
  2022. }
  2023. $tmphtml =
  2024. explode(
  2025. $tag["outerHTML"],
  2026. $tmphtml,
  2027. 2
  2028. );
  2029. $value = $this->fuckhtml->getTextContent($tmphtml[0], false, false);
  2030. $this->appendtext($value, $answer, $i);
  2031. $type = null;
  2032. switch($tag["tagName"]){
  2033. case "code": $type = "inline_code"; break;
  2034. case "em": $type = "italic"; break;
  2035. case "blockquote": $type = "quote"; break;
  2036. default: $type = "text";
  2037. }
  2038. if($type !== null){
  2039. $value = $this->fuckhtml->getTextContent($tag, false, false);
  2040. if(trim($value) != ""){
  2041. $answer[] = [
  2042. "type" => $type,
  2043. "value" => rtrim($value)
  2044. ];
  2045. $i++;
  2046. }
  2047. }
  2048. if(count($tmphtml) === 2){
  2049. $tmphtml = $tmphtml[1] . "\n";
  2050. }else{
  2051. break;
  2052. }
  2053. }
  2054. if(is_array($tmphtml)){
  2055. $tmphtml = $tmphtml[0];
  2056. }
  2057. if(strlen($tmphtml) !== 0){
  2058. $value = $this->fuckhtml->getTextContent($tmphtml, true, false);
  2059. $this->appendtext($value, $answer, $i);
  2060. }
  2061. break;
  2062. case "img":
  2063. $answer[] = [
  2064. "type" => "image",
  2065. "url" =>
  2066. $this->fuckhtml
  2067. ->getTextContent(
  2068. $tag["attributes"]["src"]
  2069. )
  2070. ];
  2071. $i++;
  2072. break;
  2073. case "pre":
  2074. switch($answer[$i - 1]["type"]){
  2075. case "text":
  2076. case "italic":
  2077. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  2078. break;
  2079. }
  2080. $answer[] =
  2081. [
  2082. "type" => "code",
  2083. "value" =>
  2084. rtrim(
  2085. $this->fuckhtml
  2086. ->getTextContent(
  2087. $snippet,
  2088. true,
  2089. false
  2090. )
  2091. )
  2092. ];
  2093. $i++;
  2094. break;
  2095. case "ol":
  2096. $o = 0;
  2097. $this->fuckhtml->load($snippet);
  2098. $li =
  2099. $this->fuckhtml
  2100. ->getElementsByTagName("li");
  2101. foreach($li as $elem){
  2102. $o++;
  2103. $this->appendtext(
  2104. $o . ". " .
  2105. $this->fuckhtml
  2106. ->getTextContent(
  2107. $elem
  2108. ),
  2109. $answer,
  2110. $i
  2111. );
  2112. }
  2113. break;
  2114. }
  2115. }
  2116. if(
  2117. $i !== 0 &&
  2118. $answer[$i - 1]["type"] == "text"
  2119. ){
  2120. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  2121. }
  2122. return $answer;
  2123. }
  2124. private function bstoutf8($bs){
  2125. return iconv("UTF-8", "ISO-8859-1//TRANSLIT", $bs);
  2126. }
  2127. private function limitnewlines($text){
  2128. preg_replace(
  2129. '/(?:[\n\r] *){2,}/m',
  2130. "\n\n",
  2131. $text
  2132. );
  2133. return $text;
  2134. }
  2135. private function sanitizeurl($url){
  2136. // check for domains w/out first short subdomain (ex: www.)
  2137. $domain = parse_url($url, PHP_URL_HOST);
  2138. $subdomain = preg_replace(
  2139. '/^[A-z0-9]{1,3}\./',
  2140. "",
  2141. $domain
  2142. );
  2143. switch($subdomain){
  2144. case "ebay.com.au":
  2145. case "ebay.at":
  2146. case "ebay.ca":
  2147. case "ebay.fr":
  2148. case "ebay.de":
  2149. case "ebay.com.hk":
  2150. case "ebay.ie":
  2151. case "ebay.it":
  2152. case "ebay.com.my":
  2153. case "ebay.nl":
  2154. case "ebay.ph":
  2155. case "ebay.pl":
  2156. case "ebay.com.sg":
  2157. case "ebay.es":
  2158. case "ebay.ch":
  2159. case "ebay.co.uk":
  2160. case "cafr.ebay.ca":
  2161. case "ebay.com":
  2162. case "community.ebay.com":
  2163. case "pages.ebay.com":
  2164. // remove ebay tracking elements
  2165. $old_params = parse_url($url, PHP_URL_QUERY);
  2166. parse_str($old_params, $params);
  2167. if(isset($params["mkevt"])){ unset($params["mkevt"]); }
  2168. if(isset($params["mkcid"])){ unset($params["mkcid"]); }
  2169. if(isset($params["mkrid"])){ unset($params["mkrid"]); }
  2170. if(isset($params["campid"])){ unset($params["campid"]); }
  2171. if(isset($params["customid"])){ unset($params["customid"]); }
  2172. if(isset($params["toolid"])){ unset($params["toolid"]); }
  2173. if(isset($params["_sop"])){ unset($params["_sop"]); }
  2174. if(isset($params["_dcat"])){ unset($params["_dcat"]); }
  2175. if(isset($params["epid"])){ unset($params["epid"]); }
  2176. if(isset($params["epid"])){ unset($params["oid"]); }
  2177. $params = http_build_query($params);
  2178. if(strlen($params) === 0){
  2179. $replace = "\?";
  2180. }else{
  2181. $replace = "";
  2182. }
  2183. $url = preg_replace(
  2184. "/" . $replace . preg_quote($old_params, "/") . "$/",
  2185. $params,
  2186. $url
  2187. );
  2188. break;
  2189. }
  2190. return $url;
  2191. }
  2192. private function number_format($number){
  2193. $number = explode(".", sprintf('%f', $number));
  2194. if(count($number) === 1){
  2195. return number_format((float)$number[0], 0, ",", ".");
  2196. }
  2197. return number_format((float)$number[0], 0, ",", "") . "." . (string)$number[1];
  2198. }
  2199. private function bingratio($width, $height){
  2200. $ratio = [
  2201. 474 / $width,
  2202. 474 / $height
  2203. ];
  2204. if($ratio[0] < $ratio[1]){
  2205. $ratio = $ratio[0];
  2206. }else{
  2207. $ratio = $ratio[1];
  2208. }
  2209. return [
  2210. floor($width * $ratio),
  2211. floor($height * $ratio)
  2212. ];
  2213. }
  2214. }