backend = new backend("ddg"); include "lib/heckhtml.php"; $this->heckhtml = new heckhtml(); } /* curl functions */ private const req_web = 0; private const req_xhr = 1; private function get($proxy, $url, $get = [], $reqtype = self::req_web){ $curlproc = curl_init(); if($get !== []){ $get = http_build_query($get); $url .= "?" . $get; } curl_setopt($curlproc, CURLOPT_URL, $url); switch($reqtype){ case self::req_web: $headers = ["User-Agent: " . config::USER_AGENT, "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Encoding: gzip", "Accept-Language: en-US,en;q=0.5", "DNT: 1", "Connection: keep-alive", "Upgrade-Insecure-Requests: 1", "Sec-Fetch-Dest: document", "Sec-Fetch-Mode: navigate", "Sec-Fetch-Site: cross-site", "Upgrade-Insecure-Requests: 1"]; break; case self::req_xhr: $headers = ["User-Agent: " . config::USER_AGENT, "Accept: */*", "Accept-Encoding: gzip", "Accept-Language: en-US,en;q=0.5", "Connection: keep-alive", "Referer: https://duckduckgo.com/", "X-Requested-With: XMLHttpRequest", "DNT: 1", "Sec-Fetch-Dest: script", "Sec-Fetch-Mode: no-cors", "Sec-Fetch-Site: same-site"]; break; } $this->backend->assign_proxy($curlproc, $proxy); curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding curl_setopt($curlproc, CURLOPT_HTTPHEADER, $headers); curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true); curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($curlproc, CURLOPT_TIMEOUT, 30); $data = curl_exec($curlproc); if(curl_errno($curlproc)){ throw new Exception(curl_error($curlproc)); } curl_close($curlproc); return $data; } public function getfilters($pagetype){ switch($pagetype){ case "web": return [ "country" => [ "display" => "Country", "option" => [ "any" => "All Regions", "ar-es" => "Argentina", "au-en" => "Australia", "at-de" => "Austria", "be-fr" => "Belgium (fr)", "be-nl" => "Belgium (nl)", "br-pt" => "Brazil", "bg-bg" => "Bulgaria", "ca-en" => "Canada (en)", "ca-fr" => "Canada (fr)", "ct-ca" => "Catalonia", "cl-es" => "Chile", "cn-zh" => "China", "co-es" => "Colombia", "hr-hr" => "Croatia", "cz-cs" => "Czech Republic", "dk-da" => "Denmark", "ee-et" => "Estonia", "fi-fi" => "Finland", "fr-fr" => "France", "de-de" => "Germany", "gr-el" => "Greece", "hk-tzh" => "Hong Kong", "hu-hu" => "Hungary", "in-en" => "India (en)", "id-en" => "Indonesia (en)", "ie-en" => "Ireland", "il-en" => "Israel (en)", "it-it" => "Italy", "jp-jp" => "Japan", "kr-kr" => "Korea", "lv-lv" => "Latvia", "lt-lt" => "Lithuania", "my-en" => "Malaysia (en)", "mx-es" => "Mexico", "nl-nl" => "Netherlands", "nz-en" => "New Zealand", "no-no" => "Norway", "pk-en" => "Pakistan (en)", "pe-es" => "Peru", "ph-en" => "Philippines (en)", "pl-pl" => "Poland", "pt-pt" => "Portugal", "ro-ro" => "Romania", "ru-ru" => "Russia", "xa-ar" => "Saudi Arabia", "sg-en" => "Singapore", "sk-sk" => "Slovakia", "sl-sl" => "Slovenia", "za-en" => "South Africa", "es-ca" => "Spain (ca)", "es-es" => "Spain (es)", "se-sv" => "Sweden", "ch-de" => "Switzerland (de)", "ch-fr" => "Switzerland (fr)", "tw-tzh" => "Taiwan", "th-en" => "Thailand (en)", "tr-tr" => "Turkey", "us-en" => "US (English)", "us-es" => "US (Spanish)", "ua-uk" => "Ukraine", "uk-en" => "United Kingdom", "vn-en" => "Vietnam (en)" ] ], "nsfw" => [ "display" => "NSFW", "option" => [ "yes" => "Yes", "maybe" => "Maybe", "no" => "No" ] ], "newer" => [ "display" => "Newer than", "option" => "_DATE" ], "older" => [ "display" => "Older than", "option" => "_DATE" ], "extendedsearch" => [ // undefined display, so it wont show in frontend "option" => [ "yes" => "Yes", "no" => "No" ] ] ]; break; case "images": return [ "country" => [ "display" => "Country", "option" => [ "us-en" => "US (English)", "ar-es" => "Argentina", "au-en" => "Australia", "at-de" => "Austria", "be-fr" => "Belgium (fr)", "be-nl" => "Belgium (nl)", "br-pt" => "Brazil", "bg-bg" => "Bulgaria", "ca-en" => "Canada (en)", "ca-fr" => "Canada (fr)", "ct-ca" => "Catalonia", "cl-es" => "Chile", "cn-zh" => "China", "co-es" => "Colombia", "hr-hr" => "Croatia", "cz-cs" => "Czech Republic", "dk-da" => "Denmark", "ee-et" => "Estonia", "fi-fi" => "Finland", "fr-fr" => "France", "de-de" => "Germany", "gr-el" => "Greece", "hk-tzh" => "Hong Kong", "hu-hu" => "Hungary", "in-en" => "India (en)", "id-en" => "Indonesia (en)", "ie-en" => "Ireland", "il-en" => "Israel (en)", "it-it" => "Italy", "jp-jp" => "Japan", "kr-kr" => "Korea", "lv-lv" => "Latvia", "lt-lt" => "Lithuania", "my-en" => "Malaysia (en)", "mx-es" => "Mexico", "nl-nl" => "Netherlands", "nz-en" => "New Zealand", "no-no" => "Norway", "pk-en" => "Pakistan (en)", "pe-es" => "Peru", "ph-en" => "Philippines (en)", "pl-pl" => "Poland", "pt-pt" => "Portugal", "ro-ro" => "Romania", "ru-ru" => "Russia", "xa-ar" => "Saudi Arabia", "sg-en" => "Singapore", "sk-sk" => "Slovakia", "sl-sl" => "Slovenia", "za-en" => "South Africa", "es-ca" => "Spain (ca)", "es-es" => "Spain (es)", "se-sv" => "Sweden", "ch-de" => "Switzerland (de)", "ch-fr" => "Switzerland (fr)", "tw-tzh" => "Taiwan", "th-en" => "Thailand (en)", "tr-tr" => "Turkey", "us-es" => "US (Spanish)", "ua-uk" => "Ukraine", "uk-en" => "United Kingdom", "vn-en" => "Vietnam (en)" ] ], "nsfw" => [ "display" => "NSFW", "option" => [ "yes" => "Yes", "no" => "No" ] ], "date" => [ "display" => "Time posted", "option" => [ "any" => "Any time", "Day" => "Past day", "Week" => "Past week", "Month" => "Past month" ] ], "size" => [ "display" => "Size", "option" => [ "any" => "Any size", "Small" => "Small", "Medium" => "Medium", "Large" => "Large", "Wallpaper" => "Wallpaper" ] ], "color" => [ "display" => "Colors", "option" => [ "any" => "All colors", "Monochrome" => "Black and white", "Red" => "Red", "Orange" => "Orange", "Yellow" => "Yellow", "Green" => "Green", "Blue" => "Blue", "Purple" => "Purple", "Pink" => "Pink", "Brown" => "Brown", "Black" => "Black", "Gray" => "Gray", "Teal" => "Teal", "White" => "White" ] ], "type" => [ "display" => "Type", "option" => [ "any" => "All types", "photo" => "Photograph", "clipart" => "Clipart", "gif" => "Animated GIF", "transparent" => "Transparent" ] ], "layout" => [ "display" => "Layout", "option" => [ "any" => "All layouts", "Square" => "Square", "Tall" => "Tall", "Wide" => "Wide" ] ], "license" => [ "display" => "License", "option" => [ "any" => "All licenses", // blame ddg for this "Any" => "All Creative Commons", "Public" => "Public domain", "Share" => "Free to Share and Use", "ShareCommercially" => "Free to Share and Use Commercially", "Modify" => "Free to Modify, Share, and Use", "ModifyCommercially" => "Free to Modify, Share, and Use Commercially" ] ] ]; break; case "videos": return [ "country" => [ "display" => "Country", "option" => [ "us-en" => "US (English)", "ar-es" => "Argentina", "au-en" => "Australia", "at-de" => "Austria", "be-fr" => "Belgium (fr)", "be-nl" => "Belgium (nl)", "br-pt" => "Brazil", "bg-bg" => "Bulgaria", "ca-en" => "Canada (en)", "ca-fr" => "Canada (fr)", "ct-ca" => "Catalonia", "cl-es" => "Chile", "cn-zh" => "China", "co-es" => "Colombia", "hr-hr" => "Croatia", "cz-cs" => "Czech Republic", "dk-da" => "Denmark", "ee-et" => "Estonia", "fi-fi" => "Finland", "fr-fr" => "France", "de-de" => "Germany", "gr-el" => "Greece", "hk-tzh" => "Hong Kong", "hu-hu" => "Hungary", "in-en" => "India (en)", "id-en" => "Indonesia (en)", "ie-en" => "Ireland", "il-en" => "Israel (en)", "it-it" => "Italy", "jp-jp" => "Japan", "kr-kr" => "Korea", "lv-lv" => "Latvia", "lt-lt" => "Lithuania", "my-en" => "Malaysia (en)", "mx-es" => "Mexico", "nl-nl" => "Netherlands", "nz-en" => "New Zealand", "no-no" => "Norway", "pk-en" => "Pakistan (en)", "pe-es" => "Peru", "ph-en" => "Philippines (en)", "pl-pl" => "Poland", "pt-pt" => "Portugal", "ro-ro" => "Romania", "ru-ru" => "Russia", "xa-ar" => "Saudi Arabia", "sg-en" => "Singapore", "sk-sk" => "Slovakia", "sl-sl" => "Slovenia", "za-en" => "South Africa", "es-ca" => "Spain (ca)", "es-es" => "Spain (es)", "se-sv" => "Sweden", "ch-de" => "Switzerland (de)", "ch-fr" => "Switzerland (fr)", "tw-tzh" => "Taiwan", "th-en" => "Thailand (en)", "tr-tr" => "Turkey", "us-en" => "US (English)", "us-es" => "US (Spanish)", "ua-uk" => "Ukraine", "uk-en" => "United Kingdom", "vn-en" => "Vietnam (en)" ] ], "nsfw" => [ "display" => "NSFW", "option" => [ "yes" => "Yes", "no" => "No" ] ], "date" => [ "display" => "Time fetched", "option" => [ "any" => "Any time", "d" => "Past day", "w" => "Past week", "m" => "Past month" ] ], "resolution" => [ //videoDefinition "display" => "Resolution", "option" => [ "any" => "Any resolution", "high" => "High definition", "standard" => "Standard definition" ] ], "duration" => [ // videoDuration "display" => "Duration", "option" => [ "any" => "Any duration", "short" => "Short (>5min)", "medium" => "Medium (5-20min)", "long" => "Long (<20min)" ] ], "license" => [ "display" => "License", "option" => [ "any" => "Any license", "creativeCommon" => "Creative Commons", "youtube" => "YouTube Standard" ] ] ]; break; case "news": return [ "country" => [ "display" => "Country", "option" => [ "us-en" => "US (English)", "ar-es" => "Argentina", "au-en" => "Australia", "at-de" => "Austria", "be-fr" => "Belgium (fr)", "be-nl" => "Belgium (nl)", "br-pt" => "Brazil", "bg-bg" => "Bulgaria", "ca-en" => "Canada (en)", "ca-fr" => "Canada (fr)", "ct-ca" => "Catalonia", "cl-es" => "Chile", "cn-zh" => "China", "co-es" => "Colombia", "hr-hr" => "Croatia", "cz-cs" => "Czech Republic", "dk-da" => "Denmark", "ee-et" => "Estonia", "fi-fi" => "Finland", "fr-fr" => "France", "de-de" => "Germany", "gr-el" => "Greece", "hk-tzh" => "Hong Kong", "hu-hu" => "Hungary", "in-en" => "India (en)", "id-en" => "Indonesia (en)", "ie-en" => "Ireland", "il-en" => "Israel (en)", "it-it" => "Italy", "jp-jp" => "Japan", "kr-kr" => "Korea", "lv-lv" => "Latvia", "lt-lt" => "Lithuania", "my-en" => "Malaysia (en)", "mx-es" => "Mexico", "nl-nl" => "Netherlands", "nz-en" => "New Zealand", "no-no" => "Norway", "pk-en" => "Pakistan (en)", "pe-es" => "Peru", "ph-en" => "Philippines (en)", "pl-pl" => "Poland", "pt-pt" => "Portugal", "ro-ro" => "Romania", "ru-ru" => "Russia", "xa-ar" => "Saudi Arabia", "sg-en" => "Singapore", "sk-sk" => "Slovakia", "sl-sl" => "Slovenia", "za-en" => "South Africa", "es-ca" => "Spain (ca)", "es-es" => "Spain (es)", "se-sv" => "Sweden", "ch-de" => "Switzerland (de)", "ch-fr" => "Switzerland (fr)", "tw-tzh" => "Taiwan", "th-en" => "Thailand (en)", "tr-tr" => "Turkey", "us-en" => "US (English)", "us-es" => "US (Spanish)", "ua-uk" => "Ukraine", "uk-en" => "United Kingdom", "vn-en" => "Vietnam (en)" ] ], "nsfw" => [ "display" => "NSFW", "option" => [ "yes" => "Yes", "maybe" => "Maybe", "no" => "No" ] ], "date" => [ "display" => "Time posted", "option" => [ "any" => "Any time", "d" => "Past day", "w" => "Past week", "m" => "Past month" ] ] ]; break; default: return []; break; } } public function web($get){ if($get["npt"]){ [$jsgrep, $proxy] = $this->backend->get($get["npt"], "web"); $extendedsearch = false; $inithtml = ""; }else{ $search = $get["s"]; if(strlen($search) === 0){ throw new Exception("Search term is empty!"); } $proxy = $this->backend->get_ip(); $country = $get["country"]; $nsfw = $get["nsfw"]; $older = $get["older"]; $newer = $get["newer"]; $extendedsearch = $get["extendedsearch"] == "yes" ? true : false; // generate filters $get_filters = [ "q" => $search, "kz" => "1" // force instant answers ]; if($country == "any"){ $get_filters["kl"] = "wt-wt"; }else{ $get_filters["kl"] = $country; } switch($nsfw){ case "yes": $get_filters["kp"] = "-2"; break; case "maybe": $get_filters["kp"] = "-1"; break; case "no": $get_filters["kp"] = "1"; break; } $df = true; if($newer === false){ if($older !== false){ $start = 36000; $end = $older; }else{ $df = false; } }else{ $start = $newer; if($older !== false){ $end = $older; }else{ $end = time(); } } if($df === true){ $get_filters["df"] = date("Y-m-d", $start) . ".." . date("Y-m-d", $end); } /* Get html */ try{ $inithtml = $this->get( $proxy, "https://duckduckgo.com/", $get_filters ); }catch(Exception $e){ throw new Exception("Failed to get html"); } preg_match( '/DDG\.deep\.initialize\(\'(.*)\',/U', $inithtml, $jsgrep ); if(!isset($jsgrep[1])){ throw new Exception("Failed to get d.js URL"); } $jsgrep = $jsgrep[1]; } // get javascript try{ $js = $this->get( $proxy, "https://links.duckduckgo.com" . $jsgrep, [], ddg::req_xhr ); }catch(Exception $e){ throw new Exception("Failed to fetch d.js"); } // initialize api response array $out = [ "status" => "ok", "spelling" => [ "type" => "no_correction", "using" => null, "correction" => null ], "npt" => null, "answer" => [], "web" => [], "image" => [], "video" => [], "news" => [], "related" => [] ]; /* Additional requests */ if($extendedsearch){ /* Check for worknik results */ preg_match( '/nrj\(\'\/js\/spice\/dictionary\/definition\/([^\'\)]+)/', $js, $wordnik ); if(isset($wordnik[1])){ try{ $wordnik = $wordnik[1]; // get definition $wordnikjs = $this->get( $proxy, "https://duckduckgo.com/js/spice/dictionary/definition/" . $wordnik, [], ddg::req_xhr ); preg_match( '/ddg_spice_dictionary_definition\(\n?(\[{[\S\s]*}])/', $wordnikjs, $wordnikjson ); if(isset($wordnikjson[1])){ $wordnikjson = json_decode($wordnikjson[1], true); $out["answer"][0] = [ "title" => urldecode($wordnik), "description" => [], "url" => "https://www.wordnik.com/words/" . $wordnik, "thumb" => null, "table" => [], "sublink" => [] ]; $partofspeech = false; $wastext = false; $textindent = 1; // get audio $wordnikaudio_json = json_decode( $this->get( $proxy, "https://duckduckgo.com/js/spice/dictionary/audio/" . $wordnik, [], ddg::req_xhr ), true ); if(isset($wordnikaudio_json[0]["id"])){ usort($wordnikaudio_json, function($a, $b){ return $a["id"] < $b["id"]; }); $out["answer"][0]["description"][] = [ "type" => "audio", "url" => $wordnikaudio_json[0]["fileUrl"] ]; } $collection = []; $e[] = []; foreach($wordnikjson as $data){ if(!isset($data["partOfSpeech"])){ continue; } if(isset($data["text"])){ if(!isset($collection[$data["partOfSpeech"]])){ $collection[$data["partOfSpeech"]] = []; $c = 0; }else{ $c = count($collection[$data["partOfSpeech"]]); } if(!isset($e[$data["partOfSpeech"]])){ $e[$data["partOfSpeech"]] = 0; } $e[$data["partOfSpeech"]]++; $text = $e[$data["partOfSpeech"]] . ". " . $this->unescapehtml(strip_tags($data["text"])); $syn = false; if( isset($data["relatedWords"]) && count($data["relatedWords"]) !== 0 ){ $syn = " ("; $u = 0; foreach($data["relatedWords"] as $related){ $syn .= ucfirst($related["relationshipType"]) . ": "; $c = count($related["words"]); $b = 0; foreach($related["words"] as $word){ $syn .= trim($this->unescapehtml(strip_tags($word))); $b++; if($b !== $c){ $syn .= ", "; } } $u++; if($u !== count($data["relatedWords"])){ $syn .= ". "; } } $syn .= ")"; } if( $c !== 0 && $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text" ){ $collection[$data["partOfSpeech"]][$c - 1]["value"] .= "\n" . $text; }else{ if( $c !== 0 && ( $collection[$data["partOfSpeech"]][$c - 1]["type"] == "text" || $collection[$data["partOfSpeech"]][$c - 1]["type"] == "italic" ) ){ $text = "\n" . $text; } $collection[$data["partOfSpeech"]][] = [ "type" => "text", "value" => $text ]; } if($syn){ $collection[$data["partOfSpeech"]][] = [ "type" => "italic", "value" => $syn ]; } if(isset($data["exampleUses"])){ foreach($data["exampleUses"] as $use){ $collection[$data["partOfSpeech"]][] = [ "type" => "quote", "value" => $this->unescapehtml(strip_tags($use["text"])) ]; } } if(isset($data["citations"])){ foreach($data["citations"] as $citation){ if(!isset($citation["cite"])){ continue; } $value = $this->unescapehtml(strip_tags($citation["cite"])); if( isset($citation["source"]) && trim($citation["source"]) != "" ){ $value .= " - " . $this->unescapehtml(strip_tags($citation["source"])); } $collection[$data["partOfSpeech"]][] = [ "type" => "quote", "value" => $value ]; } } } } foreach($collection as $key => $items){ $out["answer"][0]["description"][] = [ "type" => "title", "value" => $key ]; $out["answer"][0]["description"] = array_merge($out["answer"][0]["description"], $items); } } }catch(Exception $e){ // do nothing } } unset($wordnik); /* Check for stackoverflow answers */ // /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 // /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 // /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 preg_match( '/nrj\(\'(\/a\.js\?.*from=nlp_qa.*)\'\)/U', $js, $stack ); if(isset($stack[1])){ $stack = $stack[1]; try{ $stackjs = $this->get( $proxy, "https://duckduckgo.com" . $stack, [], ddg::req_xhr ); if( !preg_match( '/^DDG\.duckbar\.failed/', $stackjs ) ){ preg_match( '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U', $stackjs, $stackjson ); $stackjson = json_decode($stackjson[1], true)[0]["data"][0]; $out["answer"][] = [ "title" => $stackjson["Heading"], "description" => $this->stackoverflow_parse($stackjson["Abstract"]), "url" => str_replace(["http://", "ddg"], ["https://", ""], $stackjson["AbstractURL"]), "thumb" => null, "table" => [], "sublink" => [] ]; } }catch(Exception $e){ // do nothing } } /* Check for musicmatch (lyrics) */ preg_match( '/nrj\(\'(\/a\.js\?.*&s=lyrics.*)\'\)/U', $js, $lyrics ); if(isset($lyrics[1])){ $lyrics = $lyrics[1]; try{ $lyricsjs = $this->get( $proxy, "https://duckduckgo.com" . $lyrics, [], ddg::req_xhr ); if( !preg_match( '/^DDG\.duckbar\.failed/', $lyricsjs ) ){ preg_match( '/DDG\.duckbar\.add_array\((\[\{[\S\s]*}])\)/U', $lyricsjs, $lyricsjson ); $lyricsjson = json_decode($lyricsjson[1], true)[0]["data"][0]; $title = null; if(isset($lyricsjson["Heading"])){ $title = $lyricsjson["Heading"]; }elseif(isset($lyricsjson["data"][1]["urlTitle"])){ $title = $lyricsjson["data"][1]["urlTitle"]; }else{ $title = $lyricsjson["data"][0]["song_title"]; } $description = [ [ "type" => "text", "value" => null ] ]; $parts = explode( "
", str_ireplace( ["
", "
", "
"], "
", $lyricsjson["Abstract"] ), ); for($i=0; $i "quote", "value" => "Written by " . implode(", ", $lyricsjson["data"][0]["writers"]) . "\nFrom the album " . $lyricsjson["data"][0]["albums"][0]["title"] . "\nReleased on the " . date("jS \of F Y", strtotime($lyricsjson["data"][0]["albums"][0]["release_date"])) ]; $out["answer"][] = [ "title" => $title, "description" => $description, "url" => $lyricsjson["AbstractURL"], "thumb" => null, "table" => [], "sublink" => [] ]; } }catch(Exception $e){ // do nothing } } } /* Get related searches */ preg_match( '/DDG\.duckbar\.loadModule\(\'related_searches\', ?{[\s\S]*"results":(\[{[\s\S]*}]),"vqd"/U', $js, $related ); if(isset($related[1])){ try{ $related = json_decode($related[1], true); for($i=0; $isanitizeurl($answers[$i]["data"]["AbstractURL"])) ){ continue 2; } } // get more related queries if( isset($answers[$i]["data"]["RelatedTopics"]) && $answers[$i]["data"]["RelatedTopics"] != 0 ){ for($k=0; $k(.*)<\//', $answers[$i]["data"]["RelatedTopics"][$k]["Result"], $label ); array_push($out["related"], htmlspecialchars_decode(strip_tags($label[1]))); } } } $image = null; // get image if( isset($answers[$i]["data"]["Image"]) && !empty($answers[$i]["data"]["Image"]) && $answers[$i]["data"]["Image"] != "https://duckduckgo.com/i/" ){ if(strpos($answers[$i]["data"]["Image"], "https://duckduckgo.com/i/") === true){ $image = $answers[$i]["data"]["Image"]; }else{ if( strlen($answers[$i]["data"]["Image"]) > 0 && $answers[$i]["data"]["Image"][0] == "/" ){ $answers[$i]["data"]["Image"] = substr($answers[$i]["data"]["Image"], 1); } $image = "https://duckduckgo.com/" . $answers[$i]["data"]["Image"]; } } $count = count($out["answer"]); if(isset($answers[$i]["data"]["AbstractText"]) && !empty($answers[$i]["data"]["AbstractText"])){ $description = $this->stackoverflow_parse($answers[$i]["data"]["AbstractText"]); }elseif(isset($answers[$i]["data"]["Abstract"]) && !empty($answers[$i]["data"]["Abstract"])){ $description = $this->stackoverflow_parse($answers[$i]["data"]["Abstract"]); }elseif(isset($answers[$i]["data"]["Answer"]) && !empty($answers[$i]["data"]["Answer"])){ $description = $this->stackoverflow_parse($answers[$i]["data"]["Answer"]); }else{ $description = []; } if(isset($answers[$i]["data"]["Heading"]) && !empty($answers[$i]["data"]["Heading"])){ $title = $this->unescapehtml($answers[$i]["data"]["Heading"]); }else{ // no title, ignore bs continue; //$title = null; } if(isset($answers[$i]["data"]["AbstractURL"]) && !empty($answers[$i]["data"]["AbstractURL"])){ $url = $answers[$i]["data"]["AbstractURL"]; }else{ $url = null; } $out["answer"][$count] = [ "title" => $title, "description" => $description, "url" => $this->sanitizeurl($url), "thumb" => $image, "table" => [], "sublink" => [] ]; if(isset($answers[$i]["data"]["Infobox"]["content"])){ for($k=0; $kget( $proxy, "https://duckduckgo.com/js/spice/cryptocurrency/{$coins[1]}/{$coins[2]}/1", [], ddg::req_xhr ); preg_match( '/ddg_spice_cryptocurrency\(\s*({[\S\s]*})\s*\);/', $coinjs, $coinjson ); $coinjson = json_decode($coinjson[1], true); if( !isset($coinjson["error"]) && $coinjson["status"]["error_code"] == 0 ){ $coinjson = $coinjson["data"]; $array_values = array_values($coinjson["quote"])[0]; if($amount != 1){ // show conversion $description[] = [ "type" => "title", "value" => "Conversion" ]; $description[] = [ "type" => "text", "value" => "{$amount} {$coinjson["name"]} ({$coinjson["symbol"]}) = " . $this->number_format($array_values["price"] * $amount) . " " . strtoupper($coins[2]) . "\n" . "{$amount} " . strtoupper($coins[2]) . " = " . $this->number_format((1 / $array_values["price"]) * $amount) . " {$coinjson["symbol"]}" ]; } $description[] = [ "type" => "title", "value" => "Current rates" ]; // rates $description[] = [ "type" => "text", "value" => "1 {$coinjson["name"]} ({$coinjson["symbol"]}) = " . $this->number_format($array_values["price"]) . " " . strtoupper($coins[2]) . "\n" . "1 " . strtoupper($coins[2]) . " = " . $this->number_format(1 / $array_values["price"]) . " {$coinjson["symbol"]}" ]; $description[] = [ "type" => "quote", "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($coinjson["last_updated"])) ]; $out["answer"][] = [ "title" => $coinjson["name"] . " (" . strtoupper($coins[1]) . ") & " . strtoupper($coins[2]) . " market", "description" => $description, "url" => "https://coinmarketcap.com/converter/" . strtoupper($coins[1]) . "/" . strtoupper($coins[2]) . "/?amt={$amount}", "thumb" => null, "table" => [], "sublink" => [] ]; } }catch(Exception $e){ // do nothing } }else{ /* Get currency conversion */ if( preg_match( '/"https:\/\/www\.xe\.com\/currencyconverter\/convert\/\?From=([A-Z0-9]+)&To=([A-Z0-9]+)"/', $js, $currencies ) ){ preg_match( '/(?:[\s,.]*[0-9]+)+/', $search, $amount ); if(count($amount) === 1){ $amount = (float)str_replace([" ", ","], ["", "."], $amount[0]); }else{ $amount = 1; } try{ $currencyjs = $this->get( $proxy, "https://duckduckgo.com/js/spice/currency/{$amount}/" . strtolower($currencies[1]) . "/" . strtolower($currencies[2]), [], ddg::req_xhr ); preg_match( '/ddg_spice_currency\(\s*({[\S\s]*})\s*\);/', $currencyjs, $currencyjson ); $currencyjson = json_decode($currencyjson[1], true); if(empty($currencyjson["headers"]["description"])){ $currencyjson = $currencyjson["conversion"]; $description = []; if($amount != 1){ $description[] = [ "type" => "title", "value" => "Conversion" ]; $description[] = [ "type" => "text", "value" => $this->number_format($currencyjson["from-amount"]) . " {$currencyjson["from-currency-symbol"]} = " . $this->number_format($currencyjson["converted-amount"]) . " {$currencyjson["to-currency-symbol"]}" ]; } $description[] = [ "type" => "title", "value" => "Current rates" ]; $description[] = [ "type" => "text", "value" => "{$currencyjson["conversion-rate"]}\n" . "{$currencyjson["conversion-inverse"]}" ]; $description[] = [ "type" => "quote", "value" => "Last fetched: " . date("jS \of F Y @ g:ia", strtotime($currencyjson["rate-utc-timestamp"])) ]; $out["answer"][] = [ "title" => "{$currencyjson["from-currency-name"]} ({$currencyjson["from-currency-symbol"]}) to " . "{$currencyjson["to-currency-name"]} ({$currencyjson["to-currency-symbol"]})", "description" => $description, "url" => "https://www.xe.com/currencyconverter/convert/?Amount={$amount}&From={$currencies[1]}&To={$currencies[2]}", "thumb" => null, "table" => [], "sublink" => [] ]; } }catch(Exception $e){ // do nothing } } } } /* Get small answer */ preg_match( '/DDG\.ready\(function ?\(\) ?{DDH\.add\(({[\S\s]+}),"index"\)}\)/U', $inithtml, $smallanswer ); if(isset($smallanswer[1])){ $smallanswer = json_decode($smallanswer[1], true); if( !isset($smallanswer["require"]) && isset($smallanswer["data"]["title"]) ){ if(isset($smallanswer["data"]["url"])){ $url = $this->unescapehtml($smallanswer["data"]["url"]); }elseif(isset($smallanswer["meta"]["sourceUrl"])){ $url = $this->unescapehtml($smallanswer["meta"]["sourceUrl"]); }else{ $url = null; } $out["answer"] = [ [ "title" => $this->unescapehtml($smallanswer["data"]["title"]), "description" => [], "url" => $this->sanitizeurl($url), "thumb" => null, "table" => [], "sublink" => [] ], ...$out["answer"] ]; if(isset($smallanswer["data"]["subtitle"])){ $out["answer"][0]["description"][] = [ "type" => "text", "value" => isset($smallanswer["data"]["subtitle"]) ? $this->unescapehtml($smallanswer["data"]["subtitle"]) : null ]; } } } unset($inithtml); unset($answers); unset($answer_count); /* Get spelling autocorrect */ preg_match( '/DDG\.page\.showMessage\(\'spelling\',({[\S\s]+})\)/U', $js, $spelling ); if(isset($spelling[1])){ $spelling = json_decode($spelling[1], true); switch((int)$spelling["qc"]){ case 1: case 3: case 5: $type = "including"; break; default: $type = "not_many"; break; } $out["spelling"] = [ "type" => $type, "using" => $this->unescapehtml(strip_tags($spelling["suggestion"])), "correction" => $this->unescapehtml(strip_tags($spelling["recourseText"])) ]; } unset($spelling); /* Get web results */ preg_match( '/DDG\.pageLayout\.load\(\'d\', ?(\[{"[\S\s]*"}])\)/U', $js, $web ); if(isset($web[1])){ try{ $web = json_decode($web[1], true); for($i=0; $ibackend->store($web[$i]["n"] . "&biaexp=b&eslexp=a&litexp=c&msvrtexp=b&wrap=1", "web", $proxy); continue; } // ignore malformed data if(!isset($web[$i]["t"])){ continue; } $sublinks = []; if(isset($web[$i]["l"])){ for($k=0; $k $this->titledots($this->unescapehtml($web[$i]["l"][$k]["text"])), "date" => null, "description" => isset($web[$i]["l"][$k]["snippet"]) ? $this->titledots($this->unescapehtml($web[$i]["l"][$k]["snippet"])) : null, "url" => $this->sanitizeurl($web[$i]["l"][$k]["targetUrl"]) ] ); } } if( preg_match( '/^PDF<\/span>/', $web[$i]["t"] ) ){ $type = "pdf"; $web[$i]["t"] = str_replace( 'PDF', "", $web[$i]["t"] ); }else{ $type = "web"; } if(isset($web[$i]["e"])){ $date = strtotime($web[$i]["e"]); }else{ $date = null; } array_push( $out["web"], [ "title" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["t"]))), "description" => $this->titledots($this->unescapehtml(strip_tags($web[$i]["a"]))), "url" => isset($web[$i]["u"]) ? $this->sanitizeurl($web[$i]["u"]) : $this->sanitizeurl($web[$i]["c"]), "date" => $date, "type" => $type, "thumb" => [ "url" => null, "ratio" => null ], "sublink" => $sublinks, "table" => [] ] ); } }catch(Exception $e){ // do nothing } } unset($web); /* Get images */ preg_match( '/DDG\.duckbar\.load\(\'images\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U', $js, $images ); if(isset($images[1])){ try{ $images = json_decode($images[1], true); for($i=0; $ibingratio( (int)$images[$i]["width"], (int)$images[$i]["height"] ); array_push( $out["image"], [ "title" => $this->titledots($this->unescapehtml($images[$i]["title"])), "source" => [ [ "url" => $images[$i]["image"], "width" => (int)$images[$i]["width"], "height" => (int)$images[$i]["height"] ], [ "url" => $this->bingimg($images[$i]["thumbnail"]), "width" => $ratio[0], "height" => $ratio[1] ] ], "url" => $this->sanitizeurl($images[$i]["url"]) ] ); } }catch(Exception $e){ // do nothing } } unset($images); /* Get videos */ preg_match( '/DDG\.duckbar\.load\(\'videos\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U', $js, $videos ); if(isset($videos[1])){ try{ $videos = json_decode($videos[1], true); for($i=0; $i $this->titledots($this->unescapehtml($videos[$i]["title"])), "description" => $videos[$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($videos[$i]["description"])), "date" => $videos[$i]["published"] == "" ? null : strtotime($videos[$i]["published"]), "duration" => $videos[$i]["duration"] == 0 ? null : $this->hmstoseconds($videos[$i]["duration"]), "views" => $videos[$i]["statistics"]["viewCount"] == 0 ? null : $videos[$i]["statistics"]["viewCount"], "thumb" => [ "url" => $this->bingimg($videos[$i]["images"][$cachekey]), "ratio" => "16:9" ], "url" => $this->sanitizeurl($videos[$i]["content"]) ] ); } }catch(Exception $e){ // do nothing } } unset($videos); /* Get news */ preg_match( '/DDG\.duckbar\.load\(\'news\', ?{[\s\S]*"results":(\[{"[\s\S]*}]),"vqd"/U', $js, $news ); if(isset($news[1])){ try{ $news = json_decode($news[1], true); for($i=0; $i $this->titledots($this->unescapehtml($news[$i]["title"])), "description" => $this->titledots($this->unescapehtml(strip_tags($news[$i]["excerpt"]))), "date" => isset($news[$i]["date"]) ? (int)$news[$i]["date"] : null, "thumb" => [ "url" => isset($news[$i]["image"]) ? $news[$i]["image"] : null, "ratio" => "16:9" ], "url" => $this->sanitizeurl($news[$i]["url"]) ] ); } }catch(Exception $e){ // do nothing } } return $out; } public function image($get){ if($get["npt"]){ [$npt, $proxy] = $this->backend->get($get["npt"], "images"); try{ $json = json_decode($this->get( $proxy, "https://duckduckgo.com/i.js?" . $npt, [], ddg::req_xhr ), true); }catch(Exception $err){ throw new Exception("Failed to get i.js"); } }else{ $search = $get["s"]; if(strlen($search) === 0){ throw new Exception("Search term is empty!"); } $proxy = $this->backend->get_ip(); $country = $get["country"]; $nsfw = $get["nsfw"]; $date = $get["date"]; $size = $get["size"]; $color = $get["color"]; $type = $get["type"]; $layout = $get["layout"]; $license = $get["license"]; $filter = []; $get_filters = [ "q" => $search, "iax" => "images", "ia" => "images" ]; if($date != "any"){ $filter[] = "time:$date"; } if($size != "any"){ $filter[] = "size:$size"; } if($color != "any"){ $filter[] = "color:$color"; } if($type != "any"){ $filter[] = "type:$type"; } if($layout != "any"){ $filter[] = "layout:$layout"; } if($license != "any"){ $filter[] = "license:$license"; } $filter = implode(",", $filter); if($filter != ""){ $get_filters["iaf"] = $filter; } switch($nsfw){ case "yes": $get_filters["kp"] = "-2"; break; case "no": $get_filters["kp"] = "-1"; break; } try{ $html = $this->get( $proxy, "https://duckduckgo.com", $get_filters, ddg::req_web ); }catch(Exception $err){ throw new Exception("Failed to get html"); } preg_match( '/vqd=([0-9-]+)/', $html, $vqd ); if(!isset($vqd[1])){ throw new Exception("Failed to get vqd token"); } $vqd = $vqd[1]; // @TODO: s param = image offset $js_params = [ "l" => $country, "o" => "json", "q" => $search, "vqd" => $vqd ]; switch($nsfw){ case "yes": $js_params["p"] = "-1"; break; case "no": $js_params["p"] = "1"; break; } if(empty($filter)){ $js_params["f"] = "1"; }else{ $js_params["f"] = $filter; } try{ $json = json_decode($this->get( $proxy, "https://duckduckgo.com/i.js", $js_params, ddg::req_xhr ), true); }catch(Exception $err){ throw new Exception("Failed to get i.js"); } } $out = [ "status" => "ok", "npt" => null, "image" => [] ]; if(isset($json["next"])){ if(!isset($vqd)){ $vqd = array_values($json["vqd"])[0]; } $out["npt"] = $this->backend->store( explode("?", $json["next"])[1] . "&vqd=" . $vqd, "images", $proxy ); } for($i=0; $ibingimg($json["results"][$i]["thumbnail"]); $ratio = $this->bingratio( (int)$json["results"][$i]["width"], (int)$json["results"][$i]["height"] ); $out["image"][] = [ "title" => $this->titledots($this->unescapehtml($json["results"][$i]["title"])), "source" => [ [ "url" => $json["results"][$i]["image"], "width" => (int)$json["results"][$i]["width"], "height" => (int)$json["results"][$i]["height"] ], [ "url" => $bingimg, "width" => $ratio[0], "height" => $ratio[1], ] ], "url" => $this->sanitizeurl($json["results"][$i]["url"]) ]; } return $out; } public function video($get){ if($get["npt"]){ [$npt, $proxy] = $this->backend->get($get["npt"], "videos"); try{ $json = json_decode($this->get( $proxy, "https://duckduckgo.com/v.js?" . $npt, [], ddg::req_xhr ), true); }catch(Exception $err){ throw new Exception("Failed to get v.js"); } }else{ $search = $get["s"]; if(strlen($search) === 0){ throw new Exception("Search term is empty!"); } $proxy = $this->backend->get_ip(); $country = $get["country"]; $nsfw = $get["nsfw"]; $date = $get["date"]; $resolution = $get["resolution"]; $duration = $get["duration"]; $license = $get["license"]; $filter = []; $get_filters = [ "q" => $search, "iax" => "videos", "ia" => "videos" ]; switch($nsfw){ case "yes": $get_filters["kp"] = "-2"; break; case "no": $get_filters["kp"] = "-1"; break; } if($date != "any"){ $filter[] = "publishedAfter:{$date}"; } if($resolution != "any"){ $filter[] = "videoDefinition:{$resolution}"; } if($duration != "any"){ $filter[] = "videoDuration:{$duration}"; } if($license != "any"){ $filter[] = "videoLicense:{$license}"; } $filter = implode(",", $filter); try{ $html = $this->get( $proxy, "https://duckduckgo.com", $get_filters, ddg::req_web ); }catch(Exception $err){ throw new Exception("Failed to get html"); } preg_match( '/vqd=([0-9-]+)/', $html, $vqd ); if(!isset($vqd[1])){ throw new Exception("Failed to get vqd token"); } $vqd = $vqd[1]; try{ $json = json_decode($this->get( $proxy, "https://duckduckgo.com/v.js", [ "l" => "us-en", "o" => "json", "sr" => 1, "q" => $search, "vqd" => $vqd, "f" => $filter, "p" => $get_filters["kp"] ], ddg::req_xhr ), true); }catch(Exception $err){ throw new Exception("Failed to get v.js"); } } $out = [ "status" => "ok", "npt" => null, "video" => [], "author" => [], "livestream" => [], "playlist" => [], "reel" => [] ]; if(isset($json["next"])){ $out["npt"] = $this->backend->store( explode("?", $json["next"])[1], "videos", $proxy ); } for($i=0; $i $this->titledots($this->unescapehtml($json["results"][$i]["title"])), "description" => $json["results"][$i]["description"] == "" ? null : $this->titledots($this->unescapehtml($json["results"][$i]["description"])), "author" => [ "name" => empty($json["results"][$i]["uploader"]) ? null : $this->unescapehtml($json["results"][$i]["uploader"]), "url" => null, "avatar" => null ], "date" => $json["results"][$i]["published"] == "" ? null : strtotime($json["results"][$i]["published"]), "duration" => $json["results"][$i]["duration"] == 0 ? null : $this->hmstoseconds($json["results"][$i]["duration"]), "views" => $json["results"][$i]["statistics"]["viewCount"] == 0 ? null : $json["results"][$i]["statistics"]["viewCount"], "thumb" => [ "url" => $this->bingimg($json["results"][$i]["images"][$cachekey]), "ratio" => "16:9" ], "url" => $this->sanitizeurl($json["results"][$i]["content"]) ] ); } return $out; } public function news($get){ if($get["npt"]){ [$req, $proxy] = $this->backend->get($get["npt"], "news"); try{ $json = json_decode($this->get( $proxy, "https://duckduckgo.com/news.js?" . $req, [], ddg::req_xhr ), true); }catch(Exception $err){ throw new Exception("Failed to get news.js"); } }else{ $search = $get["s"]; if(strlen($search) === 0){ throw new Exception("Search term is empty!"); } $proxy = $this->backend->get_ip(); $country = $get["country"]; $nsfw = $get["nsfw"]; $date = $get["date"]; $get_params = [ "q" => $search, "iar" => "news", "ia" => "news" ]; switch($nsfw){ case "yes": $get_filters["kp"] = "-2"; break; case "maybe": $get_filters["kp"] = "-1"; break; case "no": $get_filters["kp"] = "1"; break; } if($date != "any"){ $get_params["df"] = $date; } try{ $html = $this->get( $proxy, "https://duckduckgo.com", $get_params, ddg::req_web ); }catch(Exception $err){ throw new Exception("Failed to get html"); } preg_match( '/vqd=([0-9-]+)/', $html, $vqd ); if(!isset($vqd[1])){ throw new Exception("Failed to get vqd token"); } $vqd = $vqd[1]; try{ $js_params = [ "l" => $country, "o" => "json", "noamp" => "1", "q" => $search, "vqd" => $vqd, "p" => $get_filters["kp"] ]; if($date != "any"){ $js_params["df"] = $date; }else{ $js_params["df"] = ""; } $json = json_decode($this->get( $proxy, "https://duckduckgo.com/news.js", $js_params, ddg::req_xhr ), true); }catch(Exception $err){ throw new Exception("Failed to get news.js"); } } $out = [ "status" => "ok", "npt" => null, "news" => [] ]; if(isset($json["next"])){ $out["npt"] = $this->backend->store( explode("?", $json["next"])[1], "news", $proxy ); } for($i=0; $i $this->titledots($this->unescapehtml($json["results"][$i]["title"])), "author" => $this->unescapehtml($json["results"][$i]["source"]), "description" => $this->titledots($this->unescapehtml(strip_tags($json["results"][$i]["excerpt"]))), "date" => $json["results"][$i]["date"], "thumb" => [ "url" => isset($json["results"][$i]["image"]) ? $json["results"][$i]["image"] : null, "ratio" => "16:9" ], "url" => $this->sanitizeurl($json["results"][$i]["url"]) ]; } return $out; } private function hmstoseconds($time){ $parts = explode(":", $time, 3); $time = 0; if(count($parts) === 3){ // hours $time = $time + ((int)$parts[0] * 3600); array_shift($parts); } if(count($parts) === 2){ // minutes $time = $time + ((int)$parts[0] * 60); array_shift($parts); } // seconds $time = $time + (int)$parts[0]; return $time; } private function titledots($title){ $substr = substr($title, -3); if( $substr == "..." || $substr == "…" ){ return trim(substr($title, 0, -3)); } return trim($title); } private function unescapehtml($str){ return html_entity_decode( str_replace( [ "
", "
", "
", "
", "
", "
", ], "\n", $str ), ENT_QUOTES | ENT_XML1, 'UTF-8' ); } private function bingimg($url){ $parse = parse_url($url); parse_str($parse["query"], $parts); return "https://" . $parse["host"] . "/th?id=" . urlencode($parts["id"]); } private function appendtext($payload, &$text, &$index){ if(trim($payload) == ""){ return; } if( $index !== 0 && $text[$index - 1]["type"] == "text" ){ $text[$index - 1]["value"] .= preg_replace('/ $/', " ", $payload); }else{ $text[] = [ "type" => "text", "value" => preg_replace('/ $/', " ", $payload) ]; $index++; } } private function stackoverflow_parse($html){ $i = 0; $answer = []; $this->heckhtml->load($html); $tags = $this->heckhtml->getElementsByTagName("*"); if(count($tags) === 0){ return [ [ "type" => "text", "value" => htmlspecialchars_decode($html) ] ]; } foreach($tags as $snippet){ switch($snippet["tagName"]){ case "p": $this->heckhtml->load($snippet["innerHTML"]); $codetags = $this->heckhtml ->getElementsByTagName("*"); $tmphtml = $snippet["innerHTML"]; foreach($codetags as $tag){ if(!isset($tag["outerHTML"])){ continue; } $tmphtml = explode( $tag["outerHTML"], $tmphtml, 2 ); $value = $this->heckhtml->getTextContent($tmphtml[0], false, false); $this->appendtext($value, $answer, $i); $type = null; switch($tag["tagName"]){ case "code": $type = "inline_code"; break; case "em": $type = "italic"; break; case "blockquote": $type = "quote"; break; default: $type = "text"; } if($type !== null){ $value = $this->heckhtml->getTextContent($tag, false, false); if(trim($value) != ""){ $answer[] = [ "type" => $type, "value" => rtrim($value) ]; $i++; } } if(count($tmphtml) === 2){ $tmphtml = $tmphtml[1] . "\n"; }else{ break; } } if(is_array($tmphtml)){ $tmphtml = $tmphtml[0]; } if(strlen($tmphtml) !== 0){ $value = $this->heckhtml->getTextContent($tmphtml, true, false); $this->appendtext($value, $answer, $i); } break; case "img": $answer[] = [ "type" => "image", "url" => $this->heckhtml ->getTextContent( $tag["attributes"]["src"] ) ]; $i++; break; case "pre": switch($answer[$i - 1]["type"]){ case "text": case "italic": $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]); break; } $answer[] = [ "type" => "code", "value" => rtrim( $this->heckhtml ->getTextContent( $snippet, true, false ) ) ]; $i++; break; case "ol": $o = 0; $this->heckhtml->load($snippet); $li = $this->heckhtml ->getElementsByTagName("li"); foreach($li as $elem){ $o++; $this->appendtext( $o . ". " . $this->heckhtml ->getTextContent( $elem ), $answer, $i ); } break; } } if( $i !== 0 && $answer[$i - 1]["type"] == "text" ){ $answer[$i - 1]["value"] = rtrim($answer[$i - 1]["value"]); } return $answer; } private function bstoutf8($bs){ return iconv("UTF-8", "ISO-8859-1//TRANSLIT", $bs); } private function limitnewlines($text){ preg_replace( '/(?:[\n\r] *){2,}/m', "\n\n", $text ); return $text; } private function sanitizeurl($url){ // check for domains w/out first short subdomain (ex: www.) $domain = parse_url($url, PHP_URL_HOST); $subdomain = preg_replace( '/^[A-z0-9]{1,3}\./', "", $domain ); switch($subdomain){ case "ebay.com.au": case "ebay.at": case "ebay.ca": case "ebay.fr": case "ebay.de": case "ebay.com.hk": case "ebay.ie": case "ebay.it": case "ebay.com.my": case "ebay.nl": case "ebay.ph": case "ebay.pl": case "ebay.com.sg": case "ebay.es": case "ebay.ch": case "ebay.co.uk": case "cafr.ebay.ca": case "ebay.com": case "community.ebay.com": case "pages.ebay.com": // remove ebay tracking elements $old_params = parse_url($url, PHP_URL_QUERY); parse_str($old_params, $params); if(isset($params["mkevt"])){ unset($params["mkevt"]); } if(isset($params["mkcid"])){ unset($params["mkcid"]); } if(isset($params["mkrid"])){ unset($params["mkrid"]); } if(isset($params["campid"])){ unset($params["campid"]); } if(isset($params["customid"])){ unset($params["customid"]); } if(isset($params["toolid"])){ unset($params["toolid"]); } if(isset($params["_sop"])){ unset($params["_sop"]); } if(isset($params["_dcat"])){ unset($params["_dcat"]); } if(isset($params["epid"])){ unset($params["epid"]); } if(isset($params["epid"])){ unset($params["oid"]); } $params = http_build_query($params); if(strlen($params) === 0){ $replace = "\?"; }else{ $replace = ""; } $url = preg_replace( "/" . $replace . preg_quote($old_params, "/") . "$/", $params, $url ); break; } return $url; } private function number_format($number){ $number = explode(".", sprintf('%f', $number)); if(count($number) === 1){ return number_format((float)$number[0], 0, ",", "."); } return number_format((float)$number[0], 0, ",", "") . "." . (string)$number[1]; } private function bingratio($width, $height){ $ratio = [ 474 / $width, 474 / $height ]; if($ratio[0] < $ratio[1]){ $ratio = $ratio[0]; }else{ $ratio = $ratio[1]; } return [ floor($width * $ratio), floor($height * $ratio) ]; } }