1
0

ddg.php 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752
  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. $proxy = null;
  523. if($get["npt"]){
  524. [$jsgrep, $proxy] = $this->backend->get($get["npt"], "web");
  525. $extendedsearch = false;
  526. $inithtml = "";
  527. }else{
  528. $search = $get["s"];
  529. if(strlen($search) === 0){
  530. throw new Exception("Search term is empty!");
  531. }
  532. $proxy = $this->backend->get_ip();
  533. $country = $get["country"];
  534. $nsfw = $get["nsfw"];
  535. $older = $get["older"];
  536. $newer = $get["newer"];
  537. $extendedsearch = $get["extendedsearch"] == "yes" ? true : false;
  538. // generate filters
  539. $get_filters = [
  540. "q" => $search,
  541. "kz" => "1" // force instant answers
  542. ];
  543. if($country == "any"){
  544. $get_filters["kl"] = "wt-wt";
  545. }else{
  546. $get_filters["kl"] = $country;
  547. }
  548. switch($nsfw){
  549. case "yes": $get_filters["kp"] = "-2"; break;
  550. case "maybe": $get_filters["kp"] = "-1"; break;
  551. case "no": $get_filters["kp"] = "1"; break;
  552. }
  553. $df = true;
  554. if($newer === false){
  555. if($older !== false){
  556. $start = 36000;
  557. $end = $older;
  558. }else{
  559. $df = false;
  560. }
  561. }else{
  562. $start = $newer;
  563. if($older !== false){
  564. $end = $older;
  565. }else{
  566. $end = time();
  567. }
  568. }
  569. if($df === true){
  570. $get_filters["df"] = date("Y-m-d", $start) . ".." . date("Y-m-d", $end);
  571. }
  572. /*
  573. Get html
  574. */
  575. try{
  576. $inithtml = $this->get(
  577. $proxy,
  578. "https://duckduckgo.com/",
  579. $get_filters
  580. );
  581. }catch(Exception $e){
  582. throw new Exception("Failed to get html");
  583. }
  584. preg_match(
  585. '/DDG\.deep\.initialize\(\'(.*)\',/U',
  586. $inithtml,
  587. $jsgrep
  588. );
  589. if(!isset($jsgrep[1])){
  590. throw new Exception("Failed to get d.js URL");
  591. }
  592. $jsgrep = $jsgrep[1];
  593. }
  594. // get javascript
  595. try{
  596. $js = $this->get(
  597. $proxy,
  598. "https://links.duckduckgo.com" . $jsgrep,
  599. [],
  600. ddg::req_xhr
  601. );
  602. }catch(Exception $e){
  603. throw new Exception("Failed to fetch d.js");
  604. }
  605. // initialize api response array
  606. $out = [
  607. "status" => "ok",
  608. "spelling" => [
  609. "type" => "no_correction",
  610. "using" => null,
  611. "correction" => null
  612. ],
  613. "npt" => null,
  614. "answer" => [],
  615. "web" => [],
  616. "image" => [],
  617. "video" => [],
  618. "news" => [],
  619. "related" => []
  620. ];
  621. /*
  622. Additional requests
  623. */
  624. if($extendedsearch){
  625. /*
  626. Check for worknik results
  627. */
  628. preg_match(
  629. '/nrj\(\'\/js\/spice\/dictionary\/definition\/([^\'\)]+)/',
  630. $js,
  631. $wordnik
  632. );
  633. if(isset($wordnik[1])){
  634. try{
  635. $wordnik = $wordnik[1];
  636. // get definition
  637. $wordnikjs = $this->get(
  638. $proxy,
  639. "https://duckduckgo.com/js/spice/dictionary/definition/" . $wordnik,
  640. [],
  641. ddg::req_xhr
  642. );
  643. preg_match(
  644. '/ddg_spice_dictionary_definition\(\n?(\[{[\S\s]*}])/',
  645. $wordnikjs,
  646. $wordnikjson
  647. );
  648. if(isset($wordnikjson[1])){
  649. $wordnikjson = json_decode($wordnikjson[1], true);
  650. $out["answer"][0] = [
  651. "title" => urldecode($wordnik),
  652. "description" => [],
  653. "url" => "https://www.wordnik.com/words/" . $wordnik,
  654. "thumb" => null,
  655. "table" => [],
  656. "sublink" => []
  657. ];
  658. $partofspeech = false;
  659. $wastext = false;
  660. $textindent = 1;
  661. // get audio
  662. $wordnikaudio_json =
  663. json_decode(
  664. $this->get(
  665. $proxy,
  666. "https://duckduckgo.com/js/spice/dictionary/audio/" . $wordnik,
  667. [],
  668. ddg::req_xhr
  669. ),
  670. true
  671. );
  672. if(isset($wordnikaudio_json[0]["id"])){
  673. usort($wordnikaudio_json, function($a, $b){
  674. return $a["id"] < $b["id"];
  675. });
  676. $out["answer"][0]["description"][] = [
  677. "type" => "audio",
  678. "url" => $wordnikaudio_json[0]["fileUrl"]
  679. ];
  680. }
  681. $collection = [];
  682. $e[] = [];
  683. foreach($wordnikjson as $data){
  684. if(!isset($data["partOfSpeech"])){
  685. continue;
  686. }
  687. if(isset($data["text"])){
  688. if(!isset($collection[$data["partOfSpeech"]])){
  689. $collection[$data["partOfSpeech"]] = [];
  690. $c = 0;
  691. }else{
  692. $c = count($collection[$data["partOfSpeech"]]);
  693. }
  694. if(!isset($e[$data["partOfSpeech"]])){
  695. $e[$data["partOfSpeech"]] = 0;
  696. }
  697. $e[$data["partOfSpeech"]]++;
  698. $text = $e[$data["partOfSpeech"]] . ". " . $this->unescapehtml(strip_tags($data["text"]));
  699. $syn = false;
  700. if(
  701. isset($data["relatedWords"]) &&
  702. count($data["relatedWords"]) !== 0
  703. ){
  704. $syn = " (";
  705. $u = 0;
  706. foreach($data["relatedWords"] as $related){
  707. $syn .= ucfirst($related["relationshipType"]) . ": ";
  708. $c = count($related["words"]);
  709. $b = 0;
  710. foreach($related["words"] as $word){
  711. $syn .= trim($this->unescapehtml(strip_tags($word)));
  712. $b++;
  713. if($b !== $c){
  714. $syn .= ", ";
  715. }
  716. }
  717. $u++;
  718. if($u !== count($data["relatedWords"])){
  719. $syn .= ". ";
  720. }
  721. }
  722. $syn .= ")";
  723. }
  724. if(
  725. $c !== 0 &&
  726. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text"
  727. ){
  728. $collection[$data["partOfSpeech"]][$c - 1]["value"] .=
  729. "\n" . $text;
  730. }else{
  731. if(
  732. $c !== 0 &&
  733. (
  734. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text" ||
  735. $collection[$data["partOfSpeech"]][$c - 1]["type"] == "italic"
  736. )
  737. ){
  738. $text = "\n" . $text;
  739. }
  740. $collection[$data["partOfSpeech"]][] =
  741. [
  742. "type" => "text",
  743. "value" => $text
  744. ];
  745. }
  746. if($syn){
  747. $collection[$data["partOfSpeech"]][] = [
  748. "type" => "italic",
  749. "value" => $syn
  750. ];
  751. }
  752. if(isset($data["exampleUses"])){
  753. foreach($data["exampleUses"] as $use){
  754. $collection[$data["partOfSpeech"]][] = [
  755. "type" => "quote",
  756. "value" => $this->unescapehtml(strip_tags($use["text"]))
  757. ];
  758. }
  759. }
  760. if(isset($data["citations"])){
  761. foreach($data["citations"] as $citation){
  762. if(!isset($citation["cite"])){
  763. continue;
  764. }
  765. $value = $this->unescapehtml(strip_tags($citation["cite"]));
  766. if(
  767. isset($citation["source"]) &&
  768. trim($citation["source"]) != ""
  769. ){
  770. $value .= " - " . $this->unescapehtml(strip_tags($citation["source"]));
  771. }
  772. $collection[$data["partOfSpeech"]][] = [
  773. "type" => "quote",
  774. "value" => $value
  775. ];
  776. }
  777. }
  778. }
  779. }
  780. foreach($collection as $key => $items){
  781. $out["answer"][0]["description"][] =
  782. [
  783. "type" => "title",
  784. "value" => $key
  785. ];
  786. $out["answer"][0]["description"] =
  787. array_merge($out["answer"][0]["description"], $items);
  788. }
  789. }
  790. }catch(Exception $e){
  791. // do nothing
  792. }
  793. }
  794. unset($wordnik);
  795. /*
  796. Check for stackoverflow answers
  797. */
  798. // /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
  799. // /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
  800. // /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
  801. preg_match(
  802. '/nrj\(\'(\/a\.js\?.*from=nlp_qa.*)\'\)/U',
  803. $js,
  804. $stack
  805. );
  806. if(isset($stack[1])){
  807. $stack = $stack[1];
  808. try{
  809. $stackjs = $this->get(
  810. $proxy,
  811. "https://duckduckgo.com" . $stack,
  812. [],
  813. ddg::req_xhr
  814. );
  815. if(
  816. !preg_match(
  817. '/^DDG\.duckbar\.failed/',
  818. $stackjs
  819. )
  820. ){
  821. preg_match(
  822. '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U',
  823. $stackjs,
  824. $stackjson
  825. );
  826. $stackjson = json_decode($stackjson[1], true)[0]["data"][0];
  827. $out["answer"][] = [
  828. "title" => $stackjson["Heading"],
  829. "description" => $this->stackoverflow_parse($stackjson["Abstract"]),
  830. "url" => str_replace(["http://", "ddg"], ["https://", ""], $stackjson["AbstractURL"]),
  831. "thumb" => null,
  832. "table" => [],
  833. "sublink" => []
  834. ];
  835. }
  836. }catch(Exception $e){
  837. // do nothing
  838. }
  839. }
  840. /*
  841. Check for musicmatch (lyrics)
  842. */
  843. preg_match(
  844. '/nrj\(\'(\/a\.js\?.*&s=lyrics.*)\'\)/U',
  845. $js,
  846. $lyrics
  847. );
  848. if(isset($lyrics[1])){
  849. $lyrics = $lyrics[1];
  850. try{
  851. $lyricsjs = $this->get(
  852. $proxy,
  853. "https://duckduckgo.com" . $lyrics,
  854. [],
  855. ddg::req_xhr
  856. );
  857. if(
  858. !preg_match(
  859. '/^DDG\.duckbar\.failed/',
  860. $lyricsjs
  861. )
  862. ){
  863. preg_match(
  864. '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U',
  865. $lyricsjs,
  866. $lyricsjson
  867. );
  868. $lyricsjson = json_decode($lyricsjson[1], true)[0]["data"][0];
  869. $title = null;
  870. if(isset($lyricsjson["Heading"])){
  871. $title = $lyricsjson["Heading"];
  872. }elseif(isset($lyricsjson["data"][1]["urlTitle"])){
  873. $title = $lyricsjson["data"][1]["urlTitle"];
  874. }else{
  875. $title = $lyricsjson["data"][0]["song_title"];
  876. }
  877. $description = [
  878. [
  879. "type" => "text",
  880. "value" => null
  881. ]
  882. ];
  883. $parts =
  884. explode(
  885. "<br>",
  886. str_ireplace(
  887. ["<br>", "</br>", "<br/>"],
  888. "<br>",
  889. $lyricsjson["Abstract"]
  890. ),
  891. );
  892. for($i=0; $i<count($parts); $i++){
  893. $description[0]["value"] .= trim($parts[$i]) . "\n";
  894. }
  895. $description[0]["value"] = trim($description[0]["value"]);
  896. $description[] =
  897. [
  898. "type" => "quote",
  899. "value" =>
  900. "Written by " . implode(", ", $lyricsjson["data"][0]["writers"]) .
  901. "\nFrom the album " . $lyricsjson["data"][0]["albums"][0]["title"] .
  902. "\nReleased on the " . date("jS \of F Y", strtotime($lyricsjson["data"][0]["albums"][0]["release_date"]))
  903. ];
  904. $out["answer"][] = [
  905. "title" => $title,
  906. "description" => $description,
  907. "url" => $lyricsjson["AbstractURL"],
  908. "thumb" => null,
  909. "table" => [],
  910. "sublink" => []
  911. ];
  912. }
  913. }catch(Exception $e){
  914. // do nothing
  915. }
  916. }
  917. }
  918. /*
  919. Get related searches
  920. */
  921. preg_match(
  922. '/DDG\.duckbar\.loadModule\(\'related_searches\', ?{[\s\S]*"results":(\[{[\s\S]*}]),"vqd"/U',
  923. $js,
  924. $related
  925. );
  926. if(isset($related[1])){
  927. try{
  928. $related = json_decode($related[1], true);
  929. for($i=0; $i<count($related); $i++){
  930. if(isset($related[$i]["text"])){
  931. array_push($out["related"], $related[$i]["text"]);
  932. }
  933. }
  934. }catch(Exception $e){
  935. // do nothing
  936. }
  937. }
  938. unset($related);
  939. /*
  940. Get answers
  941. */
  942. $answer_count = preg_match_all(
  943. '/DDG\.duckbar\.add\(({.*[\S\s]*})(?:\);|,null,"index"\))/U',
  944. $js . $inithtml,
  945. $answers
  946. );
  947. try{
  948. if(isset($answers[1])){
  949. $answers = $answers[1];
  950. for($i=0; $i<$answer_count; $i++){
  951. $answers[$i] = json_decode($answers[$i], true);
  952. // remove dupes
  953. for($k=0; $k<count($out["answer"]); $k++){
  954. if(
  955. !isset($answers[$i]["data"]["AbstractURL"]) ||
  956. str_replace("_", "%20", $out["answer"][$k]["url"]) == str_replace("_", "%20", $this->sanitizeurl($answers[$i]["data"]["AbstractURL"]))
  957. ){
  958. continue 2;
  959. }
  960. }
  961. // get more related queries
  962. if(
  963. isset($answers[$i]["data"]["RelatedTopics"]) &&
  964. $answers[$i]["data"]["RelatedTopics"] != 0
  965. ){
  966. for($k=0; $k<count($answers[$i]["data"]["RelatedTopics"]); $k++){
  967. if(isset($answers[$i]["data"]["RelatedTopics"][$k]["Result"])){
  968. preg_match(
  969. '/">(.*)<\//',
  970. $answers[$i]["data"]["RelatedTopics"][$k]["Result"],
  971. $label
  972. );
  973. array_push($out["related"], htmlspecialchars_decode(strip_tags($label[1])));
  974. }
  975. }
  976. }
  977. $image = null;
  978. // get image
  979. if(
  980. isset($answers[$i]["data"]["Image"]) &&
  981. !empty($answers[$i]["data"]["Image"]) &&
  982. $answers[$i]["data"]["Image"] != "https://duckduckgo.com/i/"
  983. ){
  984. if(strpos($answers[$i]["data"]["Image"], "https://duckduckgo.com/i/") === true){
  985. $image = $answers[$i]["data"]["Image"];
  986. }else{
  987. if(
  988. strlen($answers[$i]["data"]["Image"]) > 0 &&
  989. $answers[$i]["data"]["Image"][0] == "/"
  990. ){
  991. $answers[$i]["data"]["Image"] = substr($answers[$i]["data"]["Image"], 1);
  992. }
  993. $image = "https://duckduckgo.com/" . $answers[$i]["data"]["Image"];
  994. }
  995. }
  996. $count = count($out["answer"]);
  997. if(isset($answers[$i]["data"]["AbstractText"]) && !empty($answers[$i]["data"]["AbstractText"])){
  998. $description = $this->stackoverflow_parse($answers[$i]["data"]["AbstractText"]);
  999. }elseif(isset($answers[$i]["data"]["Abstract"]) && !empty($answers[$i]["data"]["Abstract"])){
  1000. $description = $this->stackoverflow_parse($answers[$i]["data"]["Abstract"]);
  1001. }elseif(isset($answers[$i]["data"]["Answer"]) && !empty($answers[$i]["data"]["Answer"])){
  1002. $description = $this->stackoverflow_parse($answers[$i]["data"]["Answer"]);
  1003. }else{
  1004. $description = [];
  1005. }
  1006. if(isset($answers[$i]["data"]["Heading"]) && !empty($answers[$i]["data"]["Heading"])){
  1007. $title = $this->unescapehtml($answers[$i]["data"]["Heading"]);
  1008. }else{
  1009. // no title, ignore bs
  1010. continue;
  1011. //$title = null;
  1012. }
  1013. if(isset($answers[$i]["data"]["AbstractURL"]) && !empty($answers[$i]["data"]["AbstractURL"])){
  1014. $url = $answers[$i]["data"]["AbstractURL"];
  1015. }else{
  1016. $url = null;
  1017. }
  1018. $out["answer"][$count] = [
  1019. "title" => $title,
  1020. "description" => $description,
  1021. "url" => $this->sanitizeurl($url),
  1022. "thumb" => $image,
  1023. "table" => [],
  1024. "sublink" => []
  1025. ];
  1026. if(isset($answers[$i]["data"]["Infobox"]["content"])){
  1027. for($k=0; $k<count($answers[$i]["data"]["Infobox"]["content"]); $k++){
  1028. // populate table
  1029. if($answers[$i]["data"]["Infobox"]["content"][$k]["data_type"] == "string"){
  1030. $out["answer"][$count]["table"][$answers[$i]["data"]["Infobox"]["content"][$k]["label"]] =
  1031. $answers[$i]["data"]["Infobox"]["content"][$k]["value"];
  1032. continue;
  1033. }
  1034. $url = "";
  1035. $type = "Website";
  1036. switch($answers[$i]["data"]["Infobox"]["content"][$k]["data_type"]){
  1037. case "official_site":
  1038. case "official_website":
  1039. $type = "Website";
  1040. break;
  1041. case "wikipedia": $type = "Wikipedia"; break;
  1042. case "itunes": $type = "iTunes"; break;
  1043. case "amazon": $type = "Amazon"; break;
  1044. case "imdb_title_id":
  1045. case "imdb_id":
  1046. case "imdb_name_id":
  1047. $type = "IMDb";
  1048. $delim = substr($answers[$i]["data"]["Infobox"]["content"][$k]["value"], 0, 2);
  1049. if($delim == "nm"){
  1050. $url = "https://www.imdb.com/name/";
  1051. }elseif($delim == "tt"){
  1052. $url = "https://www.imdb.com/title/";
  1053. }elseif($delim == "co"){
  1054. $url = "https://www.imdb.com/search/title/?companies=";
  1055. }else{
  1056. $url = "https://www.imdb.com/title/";
  1057. }
  1058. break;
  1059. case "imdb_name_id": $url = "https://www.imdb.com/name/"; $type = "IMDb"; break;
  1060. case "twitter_profile": $url = "https://twitter.com/"; $type = "Twitter"; break;
  1061. case "instagram_profile": $url = "https://instagram.com/"; $type = "Instagram"; break;
  1062. case "facebook_profile": $url = "https://facebook.com/"; $type = "Facebook"; break;
  1063. case "spotify_artist_id": $url = "https://open.spotify.com/artist/"; $type = "Spotify"; break;
  1064. case "rotten_tomatoes": $url = "https://rottentomatoes.com/"; $type = "Rotten Tomatoes"; break;
  1065. case "youtube_channel": $url = "https://youtube.com/channel/"; $type = "YouTube"; break;
  1066. case "soundcloud_id": $url = "https://soundcloud.com/"; $type = "SoundCloud"; break;
  1067. default:
  1068. continue 2;
  1069. }
  1070. // populate sublinks
  1071. $out["answer"][$count]["sublink"][$type] =
  1072. $url . $answers[$i]["data"]["Infobox"]["content"][$k]["value"];
  1073. }
  1074. }
  1075. }
  1076. }
  1077. }catch(Exception $e){
  1078. // do nothing
  1079. }
  1080. /*
  1081. Get shitcoin conversions
  1082. */
  1083. if($extendedsearch){
  1084. if(
  1085. preg_match(
  1086. '/"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]+))\/?"/',
  1087. $js,
  1088. $shitcoins
  1089. )
  1090. ){
  1091. $shitcoins = array_values(array_filter($shitcoins));
  1092. preg_match(
  1093. '/(?:[\s,.]*[0-9]+)+/',
  1094. $search,
  1095. $amount
  1096. );
  1097. if(count($amount) === 1){
  1098. $amount = (float)str_replace([" ", ","], ["", "."], $amount[0]);
  1099. }else{
  1100. $amount = 1;
  1101. }
  1102. try{
  1103. $description = [];
  1104. $shitcoinjs = $this->get(
  1105. $proxy,
  1106. "https://duckduckgo.com/js/spice/cryptocurrency/{$shitcoins[1]}/{$shitcoins[2]}/1",
  1107. [],
  1108. ddg::req_xhr
  1109. );
  1110. preg_match(
  1111. '/ddg_spice_cryptocurrency\(\s*({[\S\s]*})\s*\);/',
  1112. $shitcoinjs,
  1113. $shitcoinjson
  1114. );
  1115. $shitcoinjson = json_decode($shitcoinjson[1], true);
  1116. if(
  1117. !isset($shitcoinjson["error"]) &&
  1118. $shitcoinjson["status"]["error_code"] == 0
  1119. ){
  1120. $shitcoinjson = $shitcoinjson["data"];
  1121. $array_values = array_values($shitcoinjson["quote"])[0];
  1122. if($amount != 1){
  1123. // show conversion
  1124. $description[] = [
  1125. "type" => "title",
  1126. "value" => "Conversion"
  1127. ];
  1128. $description[] = [
  1129. "type" => "text",
  1130. "value" =>
  1131. "{$amount} {$shitcoinjson["name"]} ({$shitcoinjson["symbol"]}) = " . $this->number_format($array_values["price"] * $amount) . " " . strtoupper($shitcoins[2]) . "\n" .
  1132. "{$amount} " . strtoupper($shitcoins[2]) . " = " . $this->number_format((1 / $array_values["price"]) * $amount) . " {$shitcoinjson["symbol"]}"
  1133. ];
  1134. }
  1135. $description[] = [
  1136. "type" => "title",
  1137. "value" => "Current rates"
  1138. ];
  1139. // rates
  1140. $description[] = [
  1141. "type" => "text",
  1142. "value" =>
  1143. "1 {$shitcoinjson["name"]} ({$shitcoinjson["symbol"]}) = " . $this->number_format($array_values["price"]) . " " . strtoupper($shitcoins[2]) . "\n" .
  1144. "1 " . strtoupper($shitcoins[2]) . " = " . $this->number_format(1 / $array_values["price"]) . " {$shitcoinjson["symbol"]}"
  1145. ];
  1146. $description[] = [
  1147. "type" => "quote",
  1148. "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($shitcoinjson["last_updated"]))
  1149. ];
  1150. $out["answer"][] = [
  1151. "title" => $shitcoinjson["name"] . " (" . strtoupper($shitcoins[1]) . ") & " . strtoupper($shitcoins[2]) . " market",
  1152. "description" => $description,
  1153. "url" => "https://coinmarketcap.com/converter/" . strtoupper($shitcoins[1]) . "/" . strtoupper($shitcoins[2]) . "/?amt={$amount}",
  1154. "thumb" => null,
  1155. "table" => [],
  1156. "sublink" => []
  1157. ];
  1158. }
  1159. }catch(Exception $e){
  1160. // do nothing
  1161. }
  1162. }else{
  1163. /*
  1164. Get currency conversion
  1165. */
  1166. if(
  1167. preg_match(
  1168. '/"https:\/\/www\.xe\.com\/currencyconverter\/convert\/\?From=([A-Z0-9]+)&To=([A-Z0-9]+)"/',
  1169. $js,
  1170. $currencies
  1171. )
  1172. ){
  1173. preg_match(
  1174. '/(?:[\s,.]*[0-9]+)+/',
  1175. $search,
  1176. $amount
  1177. );
  1178. if(count($amount) === 1){
  1179. $amount = (float)str_replace([" ", ","], ["", "."], $amount[0]);
  1180. }else{
  1181. $amount = 1;
  1182. }
  1183. try{
  1184. $currencyjs = $this->get(
  1185. $proxy,
  1186. "https://duckduckgo.com/js/spice/currency/{$amount}/" . strtolower($currencies[1]) . "/" . strtolower($currencies[2]),
  1187. [],
  1188. ddg::req_xhr
  1189. );
  1190. preg_match(
  1191. '/ddg_spice_currency\(\s*({[\S\s]*})\s*\);/',
  1192. $currencyjs,
  1193. $currencyjson
  1194. );
  1195. $currencyjson = json_decode($currencyjson[1], true);
  1196. if(empty($currencyjson["headers"]["description"])){
  1197. $currencyjson = $currencyjson["conversion"];
  1198. $description = [];
  1199. if($amount != 1){
  1200. $description[] =
  1201. [
  1202. "type" => "title",
  1203. "value" => "Conversion"
  1204. ];
  1205. $description[] =
  1206. [
  1207. "type" => "text",
  1208. "value" =>
  1209. $this->number_format($currencyjson["from-amount"]) . " {$currencyjson["from-currency-symbol"]} = " .
  1210. $this->number_format($currencyjson["converted-amount"]) . " {$currencyjson["to-currency-symbol"]}"
  1211. ];
  1212. }
  1213. $description[] =
  1214. [
  1215. "type" => "title",
  1216. "value" => "Current rates"
  1217. ];
  1218. $description[] =
  1219. [
  1220. "type" => "text",
  1221. "value" =>
  1222. "{$currencyjson["conversion-rate"]}\n" .
  1223. "{$currencyjson["conversion-inverse"]}"
  1224. ];
  1225. $description[] =
  1226. [
  1227. "type" => "quote",
  1228. "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($currencyjson["rate-utc-timestamp"]))
  1229. ];
  1230. $out["answer"][] = [
  1231. "title" =>
  1232. "{$currencyjson["from-currency-name"]} ({$currencyjson["from-currency-symbol"]}) to " .
  1233. "{$currencyjson["to-currency-name"]} ({$currencyjson["to-currency-symbol"]})",
  1234. "description" => $description,
  1235. "url" => "https://www.xe.com/currencyconverter/convert/?Amount={$amount}&From={$currencies[1]}&To={$currencies[2]}",
  1236. "thumb" => null,
  1237. "table" => [],
  1238. "sublink" => []
  1239. ];
  1240. }
  1241. }catch(Exception $e){
  1242. // do nothing
  1243. }
  1244. }
  1245. }
  1246. }
  1247. /*
  1248. Get small answer
  1249. */
  1250. preg_match(
  1251. '/DDG\.ready\(function ?\(\) ?{DDH\.add\(({[\S\s]+}),"index"\)}\)/U',
  1252. $inithtml,
  1253. $smallanswer
  1254. );
  1255. if(isset($smallanswer[1])){
  1256. $smallanswer = json_decode($smallanswer[1], true);
  1257. if(
  1258. !isset($smallanswer["require"]) &&
  1259. isset($smallanswer["data"]["title"])
  1260. ){
  1261. if(isset($smallanswer["data"]["url"])){
  1262. $url = $this->unescapehtml($smallanswer["data"]["url"]);
  1263. }elseif(isset($smallanswer["meta"]["sourceUrl"])){
  1264. $url = $this->unescapehtml($smallanswer["meta"]["sourceUrl"]);
  1265. }else{
  1266. $url = null;
  1267. }
  1268. $out["answer"] = [
  1269. [
  1270. "title" => $this->unescapehtml($smallanswer["data"]["title"]),
  1271. "description" => [],
  1272. "url" => $this->sanitizeurl($url),
  1273. "thumb" => null,
  1274. "table" => [],
  1275. "sublink" => []
  1276. ],
  1277. ...$out["answer"]
  1278. ];
  1279. if(isset($smallanswer["data"]["subtitle"])){
  1280. $out["answer"][0]["description"][] =
  1281. [
  1282. "type" => "text",
  1283. "value" => isset($smallanswer["data"]["subtitle"]) ? $this->unescapehtml($smallanswer["data"]["subtitle"]) : null
  1284. ];
  1285. }
  1286. }
  1287. }
  1288. unset($inithtml);
  1289. unset($answers);
  1290. unset($answer_count);
  1291. /*
  1292. Get spelling autocorrect
  1293. */
  1294. preg_match(
  1295. '/DDG\.page\.showMessage\(\'spelling\',({[\S\s]+})\)/U',
  1296. $js,
  1297. $spelling
  1298. );
  1299. if(isset($spelling[1])){
  1300. $spelling = json_decode($spelling[1], true);
  1301. switch((int)$spelling["qc"]){
  1302. case 1:
  1303. case 3:
  1304. case 5:
  1305. $type = "including";
  1306. break;
  1307. default:
  1308. $type = "not_many";
  1309. break;
  1310. }
  1311. $out["spelling"] = [
  1312. "type" => $type,
  1313. "using" => $this->unescapehtml(strip_tags($spelling["suggestion"])),
  1314. "correction" => $this->unescapehtml(strip_tags($spelling["recourseText"]))
  1315. ];
  1316. }
  1317. unset($spelling);
  1318. /*
  1319. Get web results
  1320. */
  1321. preg_match(
  1322. '/DDG\.pageLayout\.load\(\'d\', ?(\[{"[\S\s]*"}])\)/U',
  1323. $js,
  1324. $web
  1325. );
  1326. if(isset($web[1])){
  1327. try{
  1328. $web = json_decode($web[1], true);
  1329. for($i=0; $i<count($web); $i++){
  1330. // ignore google placeholder + fake next page
  1331. if(
  1332. isset($web[$i]["t"]) &&
  1333. (
  1334. $web[$i]["t"] == "EOP" ||
  1335. $web[$i]["t"] == "EOF"
  1336. ) &&
  1337. strpos($web[$i]["c"], "://www.google.") !== false
  1338. ){
  1339. break;
  1340. }
  1341. // store next page token
  1342. if(isset($web[$i]["n"])){
  1343. $out["npt"] = $this->backend->store($web[$i]["n"] . "&biaexp=b&eslexp=a&litexp=c&msvrtexp=b&wrap=1", "web", $proxy);
  1344. continue;
  1345. }
  1346. // ignore malformed data
  1347. if(!isset($web[$i]["t"])){
  1348. continue;
  1349. }
  1350. $sublinks = [];
  1351. if(isset($web[$i]["l"])){
  1352. for($k=0; $k<count($web[$i]["l"]); $k++){
  1353. if(
  1354. !isset($web[$i]["l"][$k]["targetUrl"]) ||
  1355. !isset($web[$i]["l"][$k]["text"])
  1356. ){
  1357. continue;
  1358. }
  1359. array_push(
  1360. $sublinks,
  1361. [
  1362. "title" => $this->titledots($this->unescapehtml($web[$i]["l"][$k]["text"])),
  1363. "date" => null,
  1364. "description" => isset($web[$i]["l"][$k]["snippet"]) ? $this->titledots($this->unescapehtml($web[$i]["l"][$k]["snippet"])) : null,
  1365. "url" => $this->sanitizeurl($web[$i]["l"][$k]["targetUrl"])
  1366. ]
  1367. );
  1368. }
  1369. }
  1370. if(
  1371. preg_match(
  1372. '/^<span class="result__type">PDF<\/span>/',
  1373. $web[$i]["t"]
  1374. )
  1375. ){
  1376. $type = "pdf";
  1377. $web[$i]["t"] =
  1378. str_replace(
  1379. '<span class="result__type">PDF</span>',
  1380. "",
  1381. $web[$i]["t"]
  1382. );
  1383. }else{
  1384. $type = "web";
  1385. }
  1386. if(isset($web[$i]["e"])){
  1387. $date = strtotime($web[$i]["e"]);
  1388. }else{
  1389. $date = null;
  1390. }
  1391. array_push(
  1392. $out["web"],
  1393. [
  1394. "title" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["t"]))),
  1395. "description" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["a"]))),
  1396. "url" => isset($web[$i]["u"]) ? $this->sanitizeurl($web[$i]["u"]) : $this->sanitizeurl($web[$i]["c"]),
  1397. "date" => $date,
  1398. "type" => $type,
  1399. "thumb" =>
  1400. [
  1401. "url" => null,
  1402. "ratio" => null
  1403. ],
  1404. "sublink" => $sublinks,
  1405. "table" => []
  1406. ]
  1407. );
  1408. }
  1409. }catch(Exception $e){
  1410. // do nothing
  1411. }
  1412. }
  1413. unset($web);
  1414. /*
  1415. Get images
  1416. */
  1417. preg_match(
  1418. '/DDG\.duckbar\.load\(\'images\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1419. $js,
  1420. $images
  1421. );
  1422. if(isset($images[1])){
  1423. try{
  1424. $images = json_decode($images[1], true);
  1425. for($i=0; $i<count($images); $i++){
  1426. if(
  1427. !isset($images[$i]["title"]) ||
  1428. !isset($images[$i]["image"]) ||
  1429. !isset($images[$i]["thumbnail"]) ||
  1430. !isset($images[$i]["width"]) ||
  1431. !isset($images[$i]["height"])
  1432. ){
  1433. continue;
  1434. }
  1435. $ratio =
  1436. $this->bingratio(
  1437. (int)$images[$i]["width"],
  1438. (int)$images[$i]["height"]
  1439. );
  1440. array_push(
  1441. $out["image"],
  1442. [
  1443. "title" => $this->titledots($this->unescapehtml($images[$i]["title"])),
  1444. "source" => [
  1445. [
  1446. "url" => $images[$i]["image"],
  1447. "width" => (int)$images[$i]["width"],
  1448. "height" => (int)$images[$i]["height"]
  1449. ],
  1450. [
  1451. "url" => $this->bingimg($images[$i]["thumbnail"]),
  1452. "width" => $ratio[0],
  1453. "height" => $ratio[1]
  1454. ]
  1455. ],
  1456. "url" => $this->sanitizeurl($images[$i]["url"])
  1457. ]
  1458. );
  1459. }
  1460. }catch(Exception $e){
  1461. // do nothing
  1462. }
  1463. }
  1464. unset($images);
  1465. /*
  1466. Get videos
  1467. */
  1468. preg_match(
  1469. '/DDG\.duckbar\.load\(\'videos\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1470. $js,
  1471. $videos
  1472. );
  1473. if(isset($videos[1])){
  1474. try{
  1475. $videos = json_decode($videos[1], true);
  1476. for($i=0; $i<count($videos); $i++){
  1477. $cachekey = false;
  1478. foreach(["large", "medium", "small"] as &$key){
  1479. if(isset($videos[$i]["images"][$key])){
  1480. $cachekey = $key;
  1481. break;
  1482. }
  1483. }
  1484. if(
  1485. !isset($videos[$i]["title"]) ||
  1486. !isset($videos[$i]["description"]) ||
  1487. $cachekey === false ||
  1488. !isset($videos[$i]["content"])
  1489. ){
  1490. continue;
  1491. }
  1492. array_push(
  1493. $out["video"],
  1494. [
  1495. "title" => $this->titledots($this->unescapehtml($videos[$i]["title"])),
  1496. "description" => $videos[$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($videos[$i]["description"])),
  1497. "date" => $videos[$i]["published"] == "" ? null : strtotime($videos[$i]["published"]),
  1498. "duration" => $videos[$i]["duration"] == 0 ? null : $this->hmstoseconds($videos[$i]["duration"]),
  1499. "views" => $videos[$i]["statistics"]["viewCount"] == 0 ? null : $videos[$i]["statistics"]["viewCount"],
  1500. "thumb" =>
  1501. [
  1502. "url" => $this->bingimg($videos[$i]["images"][$cachekey]),
  1503. "ratio" => "16:9"
  1504. ],
  1505. "url" => $this->sanitizeurl($videos[$i]["content"])
  1506. ]
  1507. );
  1508. }
  1509. }catch(Exception $e){
  1510. // do nothing
  1511. }
  1512. }
  1513. unset($videos);
  1514. /*
  1515. Get news
  1516. */
  1517. preg_match(
  1518. '/DDG\.duckbar\.load\(\'news\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U',
  1519. $js,
  1520. $news
  1521. );
  1522. if(isset($news[1])){
  1523. try{
  1524. $news = json_decode($news[1], true);
  1525. for($i=0; $i<count($news); $i++){
  1526. if(
  1527. !isset($news[$i]["title"]) ||
  1528. !isset($news[$i]["excerpt"]) ||
  1529. !isset($news[$i]["url"])
  1530. ){
  1531. continue;
  1532. }
  1533. array_push(
  1534. $out["news"],
  1535. [
  1536. "title" => $this->titledots($this->unescapehtml($news[$i]["title"])),
  1537. "description" => $this->titledots($this->unescapehtml(strip_tags($news[$i]["excerpt"]))),
  1538. "date" => isset($news[$i]["date"]) ? (int)$news[$i]["date"] : null,
  1539. "thumb" =>
  1540. [
  1541. "url" => isset($news[$i]["image"]) ? $news[$i]["image"] : null,
  1542. "ratio" => "16:9"
  1543. ],
  1544. "url" => $this->sanitizeurl($news[$i]["url"])
  1545. ]
  1546. );
  1547. }
  1548. }catch(Exception $e){
  1549. // do nothing
  1550. }
  1551. }
  1552. return $out;
  1553. }
  1554. public function image($get){
  1555. if($get["npt"]){
  1556. [$npt, $proxy] = $this->backend->get($get["npt"], "images");
  1557. try{
  1558. $json = json_decode($this->get(
  1559. $proxy,
  1560. "https://duckduckgo.com/i.js?" . $npt,
  1561. [],
  1562. ddg::req_xhr
  1563. ), true);
  1564. }catch(Exception $err){
  1565. throw new Exception("Failed to get i.js");
  1566. }
  1567. }else{
  1568. $search = $get["s"];
  1569. if(strlen($search) === 0){
  1570. throw new Exception("Search term is empty!");
  1571. }
  1572. $proxy = $this->backend->get_ip();
  1573. $country = $get["country"];
  1574. $nsfw = $get["nsfw"];
  1575. $date = $get["date"];
  1576. $size = $get["size"];
  1577. $color = $get["color"];
  1578. $type = $get["type"];
  1579. $layout = $get["layout"];
  1580. $license = $get["license"];
  1581. $filter = [];
  1582. $get_filters = [
  1583. "q" => $search,
  1584. "iax" => "images",
  1585. "ia" => "images"
  1586. ];
  1587. if($date != "any"){ $filter[] = "time:$date"; }
  1588. if($size != "any"){ $filter[] = "size:$size"; }
  1589. if($color != "any"){ $filter[] = "color:$color"; }
  1590. if($type != "any"){ $filter[] = "type:$type"; }
  1591. if($layout != "any"){ $filter[] = "layout:$layout"; }
  1592. if($license != "any"){ $filter[] = "license:$license"; }
  1593. $filter = implode(",", $filter);
  1594. if($filter != ""){
  1595. $get_filters["iaf"] = $filter;
  1596. }
  1597. switch($nsfw){
  1598. case "yes": $get_filters["kp"] = "-2"; break;
  1599. case "no": $get_filters["kp"] = "-1"; break;
  1600. }
  1601. try{
  1602. $html = $this->get(
  1603. $proxy,
  1604. "https://duckduckgo.com",
  1605. $get_filters,
  1606. ddg::req_web
  1607. );
  1608. }catch(Exception $err){
  1609. throw new Exception("Failed to get html");
  1610. }
  1611. preg_match(
  1612. '/vqd=([0-9-]+)/',
  1613. $html,
  1614. $vqd
  1615. );
  1616. if(!isset($vqd[1])){
  1617. throw new Exception("Failed to get vqd token");
  1618. }
  1619. $vqd = $vqd[1];
  1620. // @TODO: s param = image offset
  1621. $js_params = [
  1622. "l" => $country,
  1623. "o" => "json",
  1624. "q" => $search,
  1625. "vqd" => $vqd
  1626. ];
  1627. switch($nsfw){
  1628. case "yes": $js_params["p"] = "-1"; break;
  1629. case "no": $js_params["p"] = "1"; break;
  1630. }
  1631. if(empty($filter)){
  1632. $js_params["f"] = "1";
  1633. }else{
  1634. $js_params["f"] = $filter;
  1635. }
  1636. try{
  1637. $json = json_decode($this->get(
  1638. $proxy,
  1639. "https://duckduckgo.com/i.js",
  1640. $js_params,
  1641. ddg::req_xhr
  1642. ), true);
  1643. }catch(Exception $err){
  1644. throw new Exception("Failed to get i.js");
  1645. }
  1646. }
  1647. $out = [
  1648. "status" => "ok",
  1649. "npt" => null,
  1650. "image" => []
  1651. ];
  1652. if(isset($json["next"])){
  1653. if(!isset($vqd)){
  1654. $vqd = array_values($json["vqd"])[0];
  1655. }
  1656. $out["npt"] =
  1657. $this->backend->store(
  1658. explode("?", $json["next"])[1] . "&vqd=" .
  1659. $vqd,
  1660. "images",
  1661. $proxy
  1662. );
  1663. }
  1664. for($i=0; $i<count($json["results"]); $i++){
  1665. $bingimg = $this->bingimg($json["results"][$i]["thumbnail"]);
  1666. $ratio =
  1667. $this->bingratio(
  1668. (int)$json["results"][$i]["width"],
  1669. (int)$json["results"][$i]["height"]
  1670. );
  1671. $out["image"][] = [
  1672. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1673. "source" => [
  1674. [
  1675. "url" => $json["results"][$i]["image"],
  1676. "width" => (int)$json["results"][$i]["width"],
  1677. "height" => (int)$json["results"][$i]["height"]
  1678. ],
  1679. [
  1680. "url" => $bingimg,
  1681. "width" => $ratio[0],
  1682. "height" => $ratio[1],
  1683. ]
  1684. ],
  1685. "url" => $this->sanitizeurl($json["results"][$i]["url"])
  1686. ];
  1687. }
  1688. return $out;
  1689. }
  1690. public function video($get){
  1691. if($get["npt"]){
  1692. [$npt, $proxy] = $this->backend->get($get["npt"], "videos");
  1693. try{
  1694. $json = json_decode($this->get(
  1695. $proxy,
  1696. "https://duckduckgo.com/v.js?" .
  1697. $npt,
  1698. [],
  1699. ddg::req_xhr
  1700. ), true);
  1701. }catch(Exception $err){
  1702. throw new Exception("Failed to get v.js");
  1703. }
  1704. }else{
  1705. $search = $get["s"];
  1706. if(strlen($search) === 0){
  1707. throw new Exception("Search term is empty!");
  1708. }
  1709. $proxy = $this->backend->get_ip();
  1710. $country = $get["country"];
  1711. $nsfw = $get["nsfw"];
  1712. $date = $get["date"];
  1713. $resolution = $get["resolution"];
  1714. $duration = $get["duration"];
  1715. $license = $get["license"];
  1716. $filter = [];
  1717. $get_filters = [
  1718. "q" => $search,
  1719. "iax" => "videos",
  1720. "ia" => "videos"
  1721. ];
  1722. switch($nsfw){
  1723. case "yes": $get_filters["kp"] = "-2"; break;
  1724. case "no": $get_filters["kp"] = "-1"; break;
  1725. }
  1726. if($date != "any"){ $filter[] = "publishedAfter:{$date}"; }
  1727. if($resolution != "any"){ $filter[] = "videoDefinition:{$resolution}"; }
  1728. if($duration != "any"){ $filter[] = "videoDuration:{$duration}"; }
  1729. if($license != "any"){ $filter[] = "videoLicense:{$license}"; }
  1730. $filter = implode(",", $filter);
  1731. try{
  1732. $html = $this->get(
  1733. $proxy,
  1734. "https://duckduckgo.com",
  1735. $get_filters,
  1736. ddg::req_web
  1737. );
  1738. }catch(Exception $err){
  1739. throw new Exception("Failed to get html");
  1740. }
  1741. preg_match(
  1742. '/vqd=([0-9-]+)/',
  1743. $html,
  1744. $vqd
  1745. );
  1746. if(!isset($vqd[1])){
  1747. throw new Exception("Failed to get vqd token");
  1748. }
  1749. $vqd = $vqd[1];
  1750. try{
  1751. $json = json_decode($this->get(
  1752. $proxy,
  1753. "https://duckduckgo.com/v.js",
  1754. [
  1755. "l" => "us-en",
  1756. "o" => "json",
  1757. "sr" => 1,
  1758. "q" => $search,
  1759. "vqd" => $vqd,
  1760. "f" => $filter,
  1761. "p" => $get_filters["kp"]
  1762. ],
  1763. ddg::req_xhr
  1764. ), true);
  1765. }catch(Exception $err){
  1766. throw new Exception("Failed to get v.js");
  1767. }
  1768. }
  1769. $out = [
  1770. "status" => "ok",
  1771. "npt" => null,
  1772. "video" => [],
  1773. "author" => [],
  1774. "livestream" => [],
  1775. "playlist" => [],
  1776. "reel" => []
  1777. ];
  1778. if(isset($json["next"])){
  1779. $out["npt"] =
  1780. $this->backend->store(
  1781. explode("?", $json["next"])[1],
  1782. "videos",
  1783. $proxy
  1784. );
  1785. }
  1786. for($i=0; $i<count($json["results"]); $i++){
  1787. $cachekey = false;
  1788. foreach(["large", "medium", "small"] as &$key){
  1789. if(isset($json["results"][$i]["images"][$key])){
  1790. $cachekey = $key;
  1791. break;
  1792. }
  1793. }
  1794. if(
  1795. !isset($json["results"][$i]["title"]) ||
  1796. !isset($json["results"][$i]["description"]) ||
  1797. $cachekey === false ||
  1798. !isset($json["results"][$i]["content"])
  1799. ){
  1800. continue;
  1801. }
  1802. array_push(
  1803. $out["video"],
  1804. [
  1805. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1806. "description" => $json["results"][$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($json["results"][$i]["description"])),
  1807. "author" => [
  1808. "name" => empty($json["results"][$i]["uploader"]) ? null : $this->unescapehtml($json["results"][$i]["uploader"]),
  1809. "url" => null,
  1810. "avatar" => null
  1811. ],
  1812. "date" => $json["results"][$i]["published"] == "" ? null : strtotime($json["results"][$i]["published"]),
  1813. "duration" => $json["results"][$i]["duration"] == 0 ? null : $this->hmstoseconds($json["results"][$i]["duration"]),
  1814. "views" => $json["results"][$i]["statistics"]["viewCount"] == 0 ? null : $json["results"][$i]["statistics"]["viewCount"],
  1815. "thumb" => [
  1816. "url" => $this->bingimg($json["results"][$i]["images"][$cachekey]),
  1817. "ratio" => "16:9"
  1818. ],
  1819. "url" => $this->sanitizeurl($json["results"][$i]["content"])
  1820. ]
  1821. );
  1822. }
  1823. return $out;
  1824. }
  1825. public function news($get){
  1826. if($get["npt"]){
  1827. [$req, $proxy] = $this->backend->get($get["npt"], "news");
  1828. try{
  1829. $json = json_decode($this->get(
  1830. $proxy,
  1831. "https://duckduckgo.com/news.js?" .
  1832. $req,
  1833. [],
  1834. ddg::req_xhr
  1835. ), true);
  1836. }catch(Exception $err){
  1837. throw new Exception("Failed to get news.js");
  1838. }
  1839. }else{
  1840. $search = $get["s"];
  1841. if(strlen($search) === 0){
  1842. throw new Exception("Search term is empty!");
  1843. }
  1844. $proxy = $this->backend->get_ip();
  1845. $country = $get["country"];
  1846. $nsfw = $get["nsfw"];
  1847. $date = $get["date"];
  1848. $get_params = [
  1849. "q" => $search,
  1850. "iar" => "news",
  1851. "ia" => "news"
  1852. ];
  1853. switch($nsfw){
  1854. case "yes": $get_filters["kp"] = "-2"; break;
  1855. case "maybe": $get_filters["kp"] = "-1"; break;
  1856. case "no": $get_filters["kp"] = "1"; break;
  1857. }
  1858. if($date != "any"){
  1859. $get_params["df"] = $date;
  1860. }
  1861. try{
  1862. $html = $this->get(
  1863. $proxy,
  1864. "https://duckduckgo.com",
  1865. $get_params,
  1866. ddg::req_web
  1867. );
  1868. }catch(Exception $err){
  1869. throw new Exception("Failed to get html");
  1870. }
  1871. preg_match(
  1872. '/vqd=([0-9-]+)/',
  1873. $html,
  1874. $vqd
  1875. );
  1876. if(!isset($vqd[1])){
  1877. throw new Exception("Failed to get vqd token");
  1878. }
  1879. $vqd = $vqd[1];
  1880. try{
  1881. $js_params = [
  1882. "l" => $country,
  1883. "o" => "json",
  1884. "noamp" => "1",
  1885. "q" => $search,
  1886. "vqd" => $vqd,
  1887. "p" => $get_filters["kp"]
  1888. ];
  1889. if($date != "any"){
  1890. $js_params["df"] = $date;
  1891. }else{
  1892. $js_params["df"] = "";
  1893. }
  1894. $json = json_decode($this->get(
  1895. $proxy,
  1896. "https://duckduckgo.com/news.js",
  1897. $js_params,
  1898. ddg::req_xhr
  1899. ), true);
  1900. }catch(Exception $err){
  1901. throw new Exception("Failed to get news.js");
  1902. }
  1903. }
  1904. $out = [
  1905. "status" => "ok",
  1906. "npt" => null,
  1907. "news" => []
  1908. ];
  1909. if(isset($json["next"])){
  1910. $out["npt"] =
  1911. $this->backend->store(
  1912. explode("?", $json["next"])[1],
  1913. "news",
  1914. $proxy
  1915. );
  1916. }
  1917. for($i=0; $i<count($json["results"]); $i++){
  1918. $out["news"][] = [
  1919. "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])),
  1920. "author" => $this->unescapehtml($json["results"][$i]["source"]),
  1921. "description" => $this->titledots($this->unescapehtml(strip_tags($json["results"][$i]["excerpt"]))),
  1922. "date" => $json["results"][$i]["date"],
  1923. "thumb" =>
  1924. [
  1925. "url" => isset($json["results"][$i]["image"]) ? $json["results"][$i]["image"] : null,
  1926. "ratio" => "16:9"
  1927. ],
  1928. "url" => $this->sanitizeurl($json["results"][$i]["url"])
  1929. ];
  1930. }
  1931. return $out;
  1932. }
  1933. private function hmstoseconds($time){
  1934. $parts = explode(":", $time, 3);
  1935. $time = 0;
  1936. if(count($parts) === 3){
  1937. // hours
  1938. $time = $time + ((int)$parts[0] * 3600);
  1939. array_shift($parts);
  1940. }
  1941. if(count($parts) === 2){
  1942. // minutes
  1943. $time = $time + ((int)$parts[0] * 60);
  1944. array_shift($parts);
  1945. }
  1946. // seconds
  1947. $time = $time + (int)$parts[0];
  1948. return $time;
  1949. }
  1950. private function titledots($title){
  1951. $substr = substr($title, -3);
  1952. if(
  1953. $substr == "..." ||
  1954. $substr == "…"
  1955. ){
  1956. return trim(substr($title, 0, -3));
  1957. }
  1958. return trim($title);
  1959. }
  1960. private function unescapehtml($str){
  1961. return html_entity_decode(
  1962. str_replace(
  1963. [
  1964. "<br>",
  1965. "<br/>",
  1966. "</br>",
  1967. "<BR>",
  1968. "<BR/>",
  1969. "</BR>",
  1970. ],
  1971. "\n",
  1972. $str
  1973. ),
  1974. ENT_QUOTES | ENT_XML1, 'UTF-8'
  1975. );
  1976. }
  1977. private function bingimg($url){
  1978. $parse = parse_url($url);
  1979. parse_str($parse["query"], $parts);
  1980. return "https://" . $parse["host"] . "/th?id=" . urlencode($parts["id"]);
  1981. }
  1982. private function appendtext($payload, &$text, &$index){
  1983. if(trim($payload) == ""){
  1984. return;
  1985. }
  1986. if(
  1987. $index !== 0 &&
  1988. $text[$index - 1]["type"] == "text"
  1989. ){
  1990. $text[$index - 1]["value"] .= preg_replace('/ $/', " ", $payload);
  1991. }else{
  1992. $text[] = [
  1993. "type" => "text",
  1994. "value" => preg_replace('/ $/', " ", $payload)
  1995. ];
  1996. $index++;
  1997. }
  1998. }
  1999. private function stackoverflow_parse($html){
  2000. $i = 0;
  2001. $answer = [];
  2002. $this->fuckhtml->load($html);
  2003. $tags = $this->fuckhtml->getElementsByTagName("*");
  2004. if(count($tags) === 0){
  2005. return [
  2006. [
  2007. "type" => "text",
  2008. "value" => htmlspecialchars_decode($html)
  2009. ]
  2010. ];
  2011. }
  2012. foreach($tags as $snippet){
  2013. switch($snippet["tagName"]){
  2014. case "p":
  2015. $this->fuckhtml->load($snippet["innerHTML"]);
  2016. $codetags =
  2017. $this->fuckhtml
  2018. ->getElementsByTagName("*");
  2019. $tmphtml = $snippet["innerHTML"];
  2020. foreach($codetags as $tag){
  2021. if(!isset($tag["outerHTML"])){
  2022. continue;
  2023. }
  2024. $tmphtml =
  2025. explode(
  2026. $tag["outerHTML"],
  2027. $tmphtml,
  2028. 2
  2029. );
  2030. $value = $this->fuckhtml->getTextContent($tmphtml[0], false, false);
  2031. $this->appendtext($value, $answer, $i);
  2032. $type = null;
  2033. switch($tag["tagName"]){
  2034. case "code": $type = "inline_code"; break;
  2035. case "em": $type = "italic"; break;
  2036. case "blockquote": $type = "quote"; break;
  2037. default: $type = "text";
  2038. }
  2039. if($type !== null){
  2040. $value = $this->fuckhtml->getTextContent($tag, false, false);
  2041. if(trim($value) != ""){
  2042. $answer[] = [
  2043. "type" => $type,
  2044. "value" => rtrim($value)
  2045. ];
  2046. $i++;
  2047. }
  2048. }
  2049. if(count($tmphtml) === 2){
  2050. $tmphtml = $tmphtml[1] . "\n";
  2051. }else{
  2052. break;
  2053. }
  2054. }
  2055. if(is_array($tmphtml)){
  2056. $tmphtml = $tmphtml[0];
  2057. }
  2058. if(strlen($tmphtml) !== 0){
  2059. $value = $this->fuckhtml->getTextContent($tmphtml, true, false);
  2060. $this->appendtext($value, $answer, $i);
  2061. }
  2062. break;
  2063. case "img":
  2064. $answer[] = [
  2065. "type" => "image",
  2066. "url" =>
  2067. $this->fuckhtml
  2068. ->getTextContent(
  2069. $tag["attributes"]["src"]
  2070. )
  2071. ];
  2072. $i++;
  2073. break;
  2074. case "pre":
  2075. switch($answer[$i - 1]["type"]){
  2076. case "text":
  2077. case "italic":
  2078. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  2079. break;
  2080. }
  2081. $answer[] =
  2082. [
  2083. "type" => "code",
  2084. "value" =>
  2085. rtrim(
  2086. $this->fuckhtml
  2087. ->getTextContent(
  2088. $snippet,
  2089. true,
  2090. false
  2091. )
  2092. )
  2093. ];
  2094. $i++;
  2095. break;
  2096. case "ol":
  2097. $o = 0;
  2098. $this->fuckhtml->load($snippet);
  2099. $li =
  2100. $this->fuckhtml
  2101. ->getElementsByTagName("li");
  2102. foreach($li as $elem){
  2103. $o++;
  2104. $this->appendtext(
  2105. $o . ". " .
  2106. $this->fuckhtml
  2107. ->getTextContent(
  2108. $elem
  2109. ),
  2110. $answer,
  2111. $i
  2112. );
  2113. }
  2114. break;
  2115. }
  2116. }
  2117. if(
  2118. $i !== 0 &&
  2119. $answer[$i - 1]["type"] == "text"
  2120. ){
  2121. $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]);
  2122. }
  2123. return $answer;
  2124. }
  2125. private function bstoutf8($bs){
  2126. return iconv("UTF-8", "ISO-8859-1//TRANSLIT", $bs);
  2127. }
  2128. private function limitnewlines($text){
  2129. preg_replace(
  2130. '/(?:[\n\r] *){2,}/m',
  2131. "\n\n",
  2132. $text
  2133. );
  2134. return $text;
  2135. }
  2136. private function sanitizeurl($url){
  2137. // check for domains w/out first short subdomain (ex: www.)
  2138. $domain = parse_url($url, PHP_URL_HOST);
  2139. $subdomain = preg_replace(
  2140. '/^[A-z0-9]{1,3}\./',
  2141. "",
  2142. $domain
  2143. );
  2144. switch($subdomain){
  2145. case "ebay.com.au":
  2146. case "ebay.at":
  2147. case "ebay.ca":
  2148. case "ebay.fr":
  2149. case "ebay.de":
  2150. case "ebay.com.hk":
  2151. case "ebay.ie":
  2152. case "ebay.it":
  2153. case "ebay.com.my":
  2154. case "ebay.nl":
  2155. case "ebay.ph":
  2156. case "ebay.pl":
  2157. case "ebay.com.sg":
  2158. case "ebay.es":
  2159. case "ebay.ch":
  2160. case "ebay.co.uk":
  2161. case "cafr.ebay.ca":
  2162. case "ebay.com":
  2163. case "community.ebay.com":
  2164. case "pages.ebay.com":
  2165. // remove ebay tracking elements
  2166. $old_params = parse_url($url, PHP_URL_QUERY);
  2167. parse_str($old_params, $params);
  2168. if(isset($params["mkevt"])){ unset($params["mkevt"]); }
  2169. if(isset($params["mkcid"])){ unset($params["mkcid"]); }
  2170. if(isset($params["mkrid"])){ unset($params["mkrid"]); }
  2171. if(isset($params["campid"])){ unset($params["campid"]); }
  2172. if(isset($params["customid"])){ unset($params["customid"]); }
  2173. if(isset($params["toolid"])){ unset($params["toolid"]); }
  2174. if(isset($params["_sop"])){ unset($params["_sop"]); }
  2175. if(isset($params["_dcat"])){ unset($params["_dcat"]); }
  2176. if(isset($params["epid"])){ unset($params["epid"]); }
  2177. if(isset($params["epid"])){ unset($params["oid"]); }
  2178. $params = http_build_query($params);
  2179. if(strlen($params) === 0){
  2180. $replace = "\?";
  2181. }else{
  2182. $replace = "";
  2183. }
  2184. $url = preg_replace(
  2185. "/" . $replace . preg_quote($old_params, "/") . "$/",
  2186. $params,
  2187. $url
  2188. );
  2189. break;
  2190. }
  2191. return $url;
  2192. }
  2193. private function number_format($number){
  2194. $number = explode(".", sprintf('%f', $number));
  2195. if(count($number) === 1){
  2196. return number_format((float)$number[0], 0, ",", ".");
  2197. }
  2198. return number_format((float)$number[0], 0, ",", "") . "." . (string)$number[1];
  2199. }
  2200. private function bingratio($width, $height){
  2201. $ratio = [
  2202. 474 / $width,
  2203. 474 / $height
  2204. ];
  2205. if($ratio[0] < $ratio[1]){
  2206. $ratio = $ratio[0];
  2207. }else{
  2208. $ratio = $ratio[1];
  2209. }
  2210. return [
  2211. floor($width * $ratio),
  2212. floor($height * $ratio)
  2213. ];
  2214. }
  2215. }