/extension/searching.js

https://github.com/mozilla/firefox-voice · JavaScript · 88 lines · 71 code · 9 blank · 8 comment · 7 complexity · 2f28d9f25536e05905f52108e3e60b1d MD5 · raw file

  1. import { metadata } from "../services/metadata.js";
  2. const luckySearchRemovals = /\.(com|net|org)$/i;
  3. export function googleSearchUrl(query, feelingLucky = false) {
  4. const searchUrl = new URL("https://www.google.com/search");
  5. if (feelingLucky) {
  6. query = query.replace(luckySearchRemovals, "");
  7. } else {
  8. query = fixCalculations(query);
  9. }
  10. searchUrl.searchParams.set("q", query);
  11. if (feelingLucky) {
  12. searchUrl.searchParams.set("btnI", "");
  13. }
  14. // From https://moz.com/blog/the-ultimate-guide-to-the-google-search-parameters
  15. searchUrl.searchParams.set("safe", "active");
  16. return searchUrl.href;
  17. }
  18. function fixCalculations(query) {
  19. // Google is weird about adding and subtracting:
  20. // If you search for "5 - 2" it won't work, but "5-2" will work
  21. // Addition also sometimes (but not always) fails, such as "5 + 2"
  22. return query.replace(
  23. /(\d+)\s+([+-])\s+(\d+)/,
  24. (all, first, second, third) => first + second + third
  25. );
  26. }
  27. export async function ddgEntitySearch(query) {
  28. const response = await fetch(
  29. `https://api.duckduckgo.com/?q=${encodeURIComponent(
  30. query
  31. )}&format=json&pretty=1&skip_disambig=1`
  32. );
  33. const ddgData = await response.json();
  34. if (!ddgData.AbstractText) {
  35. // the response from DDG was null, and there was no matching Instant Answer result
  36. return null;
  37. }
  38. // Selecting a subset of the key/value pairs that the DuckDuckGo API returns for use in the card (https://stackoverflow.com/questions/17781472/how-to-get-a-subset-of-a-javascript-objects-properties)
  39. const cardData = (({
  40. Heading,
  41. AbstractText,
  42. AbstractSource,
  43. AbstractURL,
  44. Image,
  45. }) => ({ Heading, AbstractText, AbstractSource, AbstractURL, Image }))(
  46. ddgData
  47. );
  48. return cardData;
  49. }
  50. // See https://duckduckgo.com/bang for a list of potential services
  51. // FIXME: this should be removed and serviceMetadata.js preferred.
  52. const SERVICE_BANG_ALIASES = {};
  53. for (const id in metadata.search) {
  54. for (const name of metadata.search[id].names) {
  55. SERVICE_BANG_ALIASES[name] = metadata.search[id].serviceSearch;
  56. }
  57. }
  58. export function ddgBangServiceName(name) {
  59. const bang = SERVICE_BANG_ALIASES[name.toLowerCase().trim()];
  60. if (!bang) {
  61. throw new Error(`Unknown service name: ${JSON.stringify(name)}`);
  62. }
  63. return bang;
  64. }
  65. export async function ddgServiceSearchUrl(query, service) {
  66. const bang = ddgBangServiceName(service);
  67. const response = await fetch(
  68. `https://api.duckduckgo.com/?q=!${encodeURIComponent(
  69. bang
  70. )}+${encodeURIComponent(query)}&format=json&pretty=1&no_redirect=1`
  71. );
  72. const json = await response.json();
  73. return json.Redirect;
  74. }
  75. export function amazonSearchUrl(query) {
  76. const searchURL = new URL("https://www.amazon.com/s");
  77. searchURL.searchParams.set("k", query);
  78. return searchURL.href;
  79. }