ddg.php 62 KB

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