diff --git a/.gitignore b/.gitignore index 96bfc23..b87dd23 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ typings/ build dist +.idea \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bf3b1b3..e15c854 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6849,6 +6849,12 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true + }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", diff --git a/package.json b/package.json index c2606d3..6a48a7d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "html-webpack-plugin": "3.2.0", "mini-css-extract-plugin": "^0.9.0", "node-sass": "^4.14.0", + "prettier": "^2.0.5", "sass-loader": "^8.0.0", "style-loader": "^0.23.1", "svelte": "^3.21.0", diff --git a/src/js/commander.js b/src/js/commander.js index db0dc2c..cea769f 100644 --- a/src/js/commander.js +++ b/src/js/commander.js @@ -1,125 +1,130 @@ -import { annyang } from "./annyang"; -import { DEBUG } from "./common"; -import { sendMessage, resendMessageIfAvailable } from "./notification"; -import { activeListening, isActiveListening } from './store'; -import { getActiveTab, performActionWithDelay } from './core'; - -const DEFAULT_COMMAND_PRIORITY = 0.5; -const addedCommands = new Set(); - -/** ------- Initialization ------- */ -export function initCommander(allPlugins) { - initRegularCommands(allPlugins); - - chrome.runtime.onMessage.addListener(async (request) => { - if (DEBUG) { - console.log(`Received message: ${JSON.stringify(request)}`); - } - switch (request.type) { - case "TRIGGER": - trigger(); - break; - case "OPEN_URL": - chrome.tabs.create({ - url: request.url - }); - break; - case "QUERY": - annyang.trigger(request.query); - break; - case "CLEAR_NOTIFICATION": - activeListening.set(false); - break; - case "TAB_LOADED": - resendMessageIfAvailable(); - break; - } - }); - - annyang.addCallback("resultMatch", async userSaid => { - sendResultMessage(userSaid); - autoCloseIfNeeded(); - }); - - annyang.addCallback("result", results => { - const result = results[0]; - if (result && isActiveListening()) { - sendMessage({ - type: "PENDING_RESULT", - title: "Listening", - content: result - }); - } - }); - annyang.addCallback("hotwordTrigger", () => { - trigger(); - }); -} - -function sendResultMessage(userSaid) { - if (isActiveListening()) { - sendMessage({ - type: "RESULT", - title: "Received command", - content: userSaid - }); - } -} - -/** ------- Helper functions to perform actions ------- */ -async function trigger() { - const activeTab = await getActiveTab(); - if (!activeTab.url || activeTab.url.startsWith("chrome")) { - // We can't use content script on chrome URLs, so need to create a new tab. - chrome.tabs.create({ url: "https://www.google.com" }, () => { - activeListening.set(true); - }); - } else { - activeListening.set(true); - } -} - -function addCommands( - commandList, - commandFunction, - { priority = DEFAULT_COMMAND_PRIORITY } = {} -) { - for (const cmd of commandList) { - const command = cmd.toLowerCase(); - if (DEBUG) { - if (command.trim() != command) { - console.error(`${command} is not trimmed`); - } - if (addedCommands.has(command)) { - console.error(`${command} has already been added`); - } - addedCommands.add(command); - } - annyang.addCommands({ [command]: commandFunction }, priority) - } -} - -/** ------- Handle regular commands ------- */ -function initRegularCommands(allPlugins) { - for (const command of allPlugins) { - if (DEBUG) { - const keys = Object.keys(command); - if (!keys.includes("commands") || !keys.includes("callback")) { - console.error(`Invalid command in plugin: `, command); - } - } - addCommands(command.commands, command.callback, { - priority: command.priority || DEFAULT_COMMAND_PRIORITY - }); - } -} - -function autoCloseIfNeeded() { - performActionWithDelay(() => { - chrome.storage.local.get(["autoOff"], result => { - if (result.autoOff) { - activeListening.set(false); - } - }); - }) -} +import { annyang } from "./annyang"; +import { DEBUG } from "./common"; +import { sendMessage, resendMessageIfAvailable } from "./notification"; +import { activeListening, isActiveListening } from './store'; +import { getActiveTab, performActionWithDelay } from './core'; +import enData from './langs/en.json'; + +const DEFAULT_COMMAND_PRIORITY = 0.5; +const addedCommands = new Set(); + +/** ------- Initialization ------- */ +export function initCommander(allPlugins) { + initRegularCommands(allPlugins); + + chrome.runtime.onMessage.addListener(async (request) => { + if (DEBUG) { + console.log(`Received message: ${JSON.stringify(request)}`); + } + switch (request.type) { + case "TRIGGER": + trigger(); + break; + case "OPEN_URL": + chrome.tabs.create({ + url: request.url + }); + break; + case "QUERY": + annyang.trigger(request.query); + break; + case "CLEAR_NOTIFICATION": + activeListening.set(false); + break; + case "TAB_LOADED": + resendMessageIfAvailable(); + break; + } + }); + + annyang.addCallback("resultMatch", async userSaid => { + sendResultMessage(userSaid); + autoCloseIfNeeded(); + }); + + annyang.addCallback("result", results => { + const result = results[0]; + if (result && isActiveListening()) { + sendMessage({ + type: "PENDING_RESULT", + title: "Listening", + content: result + }); + } + }); + annyang.addCallback("hotwordTrigger", () => { + trigger(); + }); +} + +function sendResultMessage(userSaid) { + if (isActiveListening()) { + sendMessage({ + type: "RESULT", + title: "Received command", + content: userSaid + }); + } +} + +/** ------- Helper functions to perform actions ------- */ +async function trigger() { + const activeTab = await getActiveTab(); + if (!activeTab.url || activeTab.url.startsWith("chrome")) { + // We can't use content script on chrome URLs, so need to create a new tab. + chrome.tabs.create({ url: "https://www.google.com" }, () => { + activeListening.set(true); + }); + } else { + activeListening.set(true); + } +} + +function addCommands( + commandList, + commandFunction, + { priority = DEFAULT_COMMAND_PRIORITY } = {} +) { + for (const cmd of commandList) { + const command = cmd.toLowerCase(); + if (DEBUG) { + if (command.trim() !== command) { + console.error(`${command} is not trimmed`); + } + if (addedCommands.has(command)) { + console.error(`${command} has already been added`); + } + addedCommands.add(command); + } + annyang.addCommands({ [command]: commandFunction }, priority) + } +} + +/** ------- Handle regular commands ------- */ +function initRegularCommands(allPlugins) { + const actionToCommand = {}; + for (const command of allPlugins) { + if (DEBUG) { + const keys = Object.keys(command); + if (!keys.includes("action") || keys.includes('commands') || !keys.includes("callback")) { + console.error(`Invalid command in plugin: `, command); + } + } + actionToCommand[command.action] = command; + } + for (const [action, commands] of Object.entries(enData)) { + addCommands(commands, actionToCommand[action].callback, { + priority: actionToCommand[action].priority || DEFAULT_COMMAND_PRIORITY + }); + } +} + +function autoCloseIfNeeded() { + performActionWithDelay(() => { + chrome.storage.local.get(["autoOff"], result => { + if (result.autoOff) { + activeListening.set(false); + } + }); + }) +} diff --git a/src/js/langs/en.json b/src/js/langs/en.json new file mode 100644 index 0000000..9a50557 --- /dev/null +++ b/src/js/langs/en.json @@ -0,0 +1,522 @@ +{ + "BOOKMARK_ADD": [ + "bookmark", + "bookmark (this) page", + "add (this) (page) (to) bookmark" + ], + "BOOKMARK_REMOVE": [ + "remove (from) bookmark", + "remove this bookmark", + "remove the bookmark" + ], + "EXTENSION_CLOSE": ["bye", "bye bye", "goodbye", "good bye", "close"], + "EXTENSION_HELP": ["see supported commands", "what can you do"], + "EXTENSION_CANCEL": ["*query no", "*query cancel", "*query stop"], + "EXTENSION_SUBMIT": ["submit", "enter"], + "EXTENSION_WRITE": ["write *query"], + "MEDIA_PAUSE": ["pause", "stop"], + "MEDIA_PLAY": ["play", "resume"], + "MEDIA_VOLUME_UP": ["increase volume", "volume up"], + "MEDIA_VOLUME_DOWN": ["decrease volume", "volume down"], + "MEDIA_VOLUME_LOUD": ["(make it) loud"], + "MEDIA_VOLUME_VERY_LOUD": ["(make it) very loud"], + "MEDIA_VOLUME_QUIET": ["(make it) quiet"], + "MEDIA_FORWARD_SECONDS": [ + "skip *query seconds", + "(go) forward *query seconds" + ], + "MEDIA_BACKWARD_SECONDS": ["(go) back *query seconds"], + "MEDIA_FORWARD_MINUTES": [ + "skip *query minutes", + "(go) forward *query minutes" + ], + "MEDIA_BACKWARD_MINUTES": ["(go) back *query minutes"], + "MEDIA_FORWARD_1_MINUTE": [ + "skip 1 minute", + "(go) forward 1 minute", + "skip one minute", + "(go) forward one minute", + "skip a minute", + "(go) forward a minute" + ], + "MEDIA_BACKWARD_1_MINUTE": [ + "(go) back 1 minute", + "(go) back one minute", + "(go) back a minute" + ], + "MEDIA_TO_BEGINNING": ["(go) back to (the) beginning"], + "MEDIA_TO_END": ["(go) to (the) end"], + "NAVIGATION_BACK": ["go back"], + "NAVIGATION_FORWARD": ["go forward"], + "NAVIGATION_RELOAD": [ + "reload", + "reload tab", + "reload this tab", + "reload the tab", + "refresh", + "refresh tab", + "refresh this tab", + "refresh the tab" + ], + "NAVIGATION_SELECT_1": [ + "best", + "best result", + "best results", + "best link", + "best answer", + "best response", + "first", + "first result", + "first results", + "first link", + "first answer", + "first response", + "top", + "top result", + "top results", + "top link", + "top answer", + "top response", + "1st", + "1st result", + "1st results", + "1st link", + "1st answer", + "1st response" + ], + "NAVIGATION_SELECT_2": [ + "second", + "second result", + "second results", + "second link", + "second answer", + "second response", + "2nd", + "2nd result", + "2nd results", + "2nd link", + "2nd answer", + "2nd response" + ], + "NAVIGATION_SELECT_3": [ + "third", + "third result", + "third results", + "third link", + "third answer", + "third response", + "3rd", + "3rd result", + "3rd results", + "3rd link", + "3rd answer", + "3rd response" + ], + "NAVIGATION_SELECT_4": [ + "fourth", + "fourth result", + "fourth results", + "fourth link", + "fourth answer", + "fourth response", + "4th", + "4th result", + "4th results", + "4th link", + "4th answer", + "4th response" + ], + "NAVIGATION_SELECT_5": [ + "fifth", + "fifth result", + "fifth results", + "fifth link", + "fifth answer", + "fifth response", + "5th", + "5th result", + "5th results", + "5th link", + "5th answer", + "5th response" + ], + "NAVIGATION_SELECT_6": [ + "sixth", + "sixth result", + "sixth results", + "sixth link", + "sixth answer", + "sixth response", + "6th", + "6th result", + "6th results", + "6th link", + "6th answer", + "6th response" + ], + "NAVIGATION_SELECT_7": [ + "seventh", + "seventh result", + "seventh results", + "seventh link", + "seventh answer", + "seventh response", + "7th", + "7th result", + "7th results", + "7th link", + "7th answer", + "7th response" + ], + "NAVIGATION_SELECT_8": [ + "eighth", + "eighth result", + "eighth results", + "eighth link", + "eighth answer", + "eighth response", + "8th", + "8th result", + "8th results", + "8th link", + "8th answer", + "8th response" + ], + "NAVIGATION_SELECT_9": [ + "ninth", + "ninth result", + "ninth results", + "ninth link", + "ninth answer", + "ninth response", + "9th", + "9th result", + "9th results", + "9th link", + "9th answer", + "9th response" + ], + "NAVIGATION_SELECT_10": [ + "tenth", + "tenth result", + "tenth results", + "tenth link", + "tenth answer", + "tenth response", + "10th", + "10th result", + "10th results", + "10th link", + "10th answer", + "10th response" + ], + "PAGE_ZOOM_IN": [ + "zoom in", + "zoom", + "enlarge", + "make (it) larger", + "make (it) bigger" + ], + "PAGE_ZOOM_OUT": ["zoom out", "make (it) smaller"], + "PAGE_ZOOM_RESET": ["reset zoom", "make (it) normal size"], + "PAGE_FIND": ["find *query", "look for *query"], + "PAGE_SCROLL_DOWN": ["scroll down", "(go) down"], + "PAGE_PAGE_DOWN": ["page down"], + "PAGE_SCROLL_UP": ["scroll up", "(go) up"], + "PAGE_PAGE_UP": ["page up"], + "PAGE_TO_TOP": ["go to (the) top", "scroll to (the) top"], + "PAGE_TO_BOTTOM": ["go to (the) bottom", "scroll to (the) bottom"], + "QUERY_SEARCH_IMAGE": [ + "image of *query", + "photo of *query", + "picture of *query", + "images of *query", + "photos of *query", + "pictures of *query", + "show me image of *query", + "show me a image of *query", + "show me the image of *query", + "show me photo of *query", + "show me a photo of *query", + "show me the photo of *query", + "show me picture of *query", + "show me a picture of *query", + "show me the picture of *query", + "show me images of *query", + "show me a images of *query", + "show me the images of *query", + "show me photos of *query", + "show me a photos of *query", + "show me the photos of *query", + "show me pictures of *query", + "show me a pictures of *query", + "show me the pictures of *query" + ], + "QUERY_SEARCH_NEWS": [ + "news of *query", + "news about *query", + "show me news of *query", + "show me a news of *query", + "show me the news of *query", + "show me news about *query", + "show me a news about *query", + "show me the news about *query" + ], + "QUERY_NEWS": [ + "news", + "news today", + "today news", + "today's news", + "show me news", + "show me a news", + "show me the news", + "show me news today", + "show me a news today", + "show me the news today", + "show me today news", + "show me a today news", + "show me the today news", + "show me today's news", + "show me a today's news", + "show me the today's news" + ], + "QUERY_SEARCH_MAP": [ + "map of *query", + "map to *query", + "how to get to *query", + "how do I get to *query", + "show me map of *query", + "show me a map of *query", + "show me the map of *query", + "show me map to *query", + "show me a map to *query", + "show me the map to *query", + "show me how to get to *query", + "show me a how to get to *query", + "show me the how to get to *query", + "show me how do I get to *query", + "show me a how do I get to *query", + "show me the how do I get to *query" + ], + "QUERY_SEARCH_DIRECTION_FROM_TO": [ + "direction from *from to *to", + "directions from *from to *to", + "show me direction from *from to *to", + "show me a direction from *from to *to", + "show me the direction from *from to *to", + "show me directions from *from to *to", + "show me a directions from *from to *to", + "show me the directions from *from to *to" + ], + "QUERY_SEARCH_DIRECTION_FROM": [ + "direction from *query", + "directions from *query", + "show me direction from *query", + "show me a direction from *query", + "show me the direction from *query", + "show me directions from *query", + "show me a directions from *query", + "show me the directions from *query" + ], + "QUERY_SEARCH_DIRECTION_TO": [ + "direction to *query", + "directions to *query", + "show me direction to *query", + "show me a direction to *query", + "show me the direction to *query", + "show me directions to *query", + "show me a directions to *query", + "show me the directions to *query" + ], + "QUERY_GO_TO_WIKIPEDIA": ["wikipedia"], + "QUERY_SEARCH_WIKIPEDIA": [ + "about *query", + "wiki about *query", + "wiki *query", + "wikipedia about *query", + "wikipedia *query" + ], + "QUERY_GO_TO_VIDEO": [ + "play video", + "video", + "play videos", + "videos", + "youtube" + ], + "QUERY_SEARCH_VIDEO": [ + "video of *query", + "video about *query", + "video *query", + "videos of *query", + "videos about *query", + "videos *query", + "watch *query", + "show me video of *query", + "show me a video of *query", + "show me the video of *query", + "show me video about *query", + "show me a video about *query", + "show me the video about *query", + "show me video *query", + "show me a video *query", + "show me the video *query", + "show me videos of *query", + "show me a videos of *query", + "show me the videos of *query", + "show me videos about *query", + "show me a videos about *query", + "show me the videos about *query", + "show me videos *query", + "show me a videos *query", + "show me the videos *query", + "show me watch *query", + "show me a watch *query", + "show me the watch *query" + ], + "QUERY_GO_TO_MUSIC": ["play music", "music"], + "QUERY_GO_TO_SHOPPING": ["shopping", "buy something", "amazon"], + "QUERY_SEARCH_SHOPPING": [ + "shop for a *query", + "shop for *query", + "buy a *query", + "buy *query" + ], + "QUERY_GO_TO_DOWNLOADS": [ + "go to download", + "open download", + "go to downloads", + "open downloads" + ], + "QUERY_GO_TO_BOOKMARKS": [ + "go to bookmark", + "open bookmark", + "go to bookmarks", + "open bookmarks" + ], + "QUERY_GO_TO_HISTORY": ["go to history", "open history"], + "QUERY_GO_TO_QUERY": ["go to *query", "open *query", "take me to *query"], + "QUERY_SEARCH_QUERY_ON_BING": [ + "search for *query on Bing", + "search for *query at Bing", + "*query on Bing", + "*query at Bing" + ], + "QUERY_SEARCH_QUERY_ON_AOL": [ + "search for *query on AOL", + "search for *query at AOL", + "*query on AOL", + "*query at AOL" + ], + "QUERY_SEARCH_QUERY_ON_YAHOO": [ + "search for *query on Yahoo", + "search for *query at Yahoo", + "*query on Yahoo", + "*query at Yahoo" + ], + "QUERY_SEARCH_QUERY_ON_AMAZON": [ + "search for *query on Amazon", + "search for *query at Amazon", + "*query on Amazon", + "*query at Amazon" + ], + "QUERY_SEARCH_QUERY_ON_WALMART": [ + "search for *query on Walmart", + "search for *query at Walmart", + "*query on Walmart", + "*query at Walmart" + ], + "QUERY_SEARCH_QUERY_ON_TARGET": [ + "search for *query on Target", + "search for *query at Target", + "*query on Target", + "*query at Target" + ], + "QUERY_SEARCH_QUERY_ON_YOUTUBE": [ + "search for *query on YouTube", + "search for *query at YouTube", + "*query on YouTube", + "*query at YouTube" + ], + "QUERY_SEARCH_QUERY_ON_BAIDU": [ + "search for *query on Baidu", + "search for *query at Baidu", + "*query on Baidu", + "*query at Baidu" + ], + "QUERY_SEARCH_QUERY_ON_WIKIPEDIA": [ + "search for *query on Wikipedia", + "search for *query at Wikipedia", + "*query on Wikipedia", + "*query at Wikipedia" + ], + "QUERY_SEARCH_QUERY_ON_SITE": [ + "search for *query on *site", + "search for *query at *site", + "*query on *site", + "*query at *site" + ], + "QUERY_SEARCH_QUERY": ["search for *query", "google *query", "*query"], + + "TABS_NEW_TAB": ["open new tab", "new tab"], + "TABS_CLOSE_TAB": ["close tab", "close this tab", "close the tab"], + "TABS_CLOSE_OTHER_TABS": [ + "close other tab", + "close other tabs", + "close the other tab", + "close the other tabs", + "close all (tabs) but this (tab)" + ], + "TABS_CLOSE_RIGHT_TABS": [ + "close tab to the right", + "close tabs to the right" + ], + "TABS_CLOSE_LEFT_TABS": ["close tab to the left", "close tabs to the left"], + "TABS_CLOSE_ALL_TABS": [ + "close all tab", + "close all tabs", + "close all the tab", + "close all the tabs", + "close window", + "close this window", + "exit window" + ], + "TABS_PIN_TAB": ["pin", "pin tab", "pin this tab", "pin the tab"], + "TABS_UNPIN_TAB": ["unpin", "unpin tab", "unpin this tab", "unpin the tab"], + "TABS_MUTE_TAB": ["mute", "mute tab"], + "TABS_UNMUTE_TAB": ["unmute", "unmute tab"], + "TABS_MUTE_ALL_TABS": ["mute all (the) tab", "mute all (the) tabs"], + "TABS_UNMUTE_ALL_TABS": ["unmute all (the) tab", "unmute all (the) tabs"], + "TABS_MUTE_OTHER_TABS": ["mute other (the) tab", "mute other (the) tabs"], + "TABS_UNMUTE_OTHER_TABS": [ + "unmute other (the) tab", + "unmute other (the) tabs" + ], + "TABS_MAXIMIZE_WINDOW": ["maximize", "maximize window"], + "TABS_MINIMIZE_WINDOW": ["minimize", "minimize window"], + "TABS_SIDE_BY_SIDE": ["side by side"], + "TABS_ENTER_FULLSCREEN": [ + "fullscreen", + "full screen", + "full-screen", + "enter fullscreen", + "enter full screen", + "enter full-screen" + ], + "TABS_EXIT_FULLSCREEN": [ + "exit fullscreen", + "exit full screen", + "exit full-screen" + ], + "TABS_NEXT_TAB": [ + "next tab", + "switch (to) right tab", + "switch tab", + "right tab" + ], + "TABS_PREVIOUS_TAB": [ + "previous tab", + "last tab", + "switch (to) left tab", + "left tab" + ] +} diff --git a/src/js/plugins/bookmark.js b/src/js/plugins/bookmark.js index d16e965..fcbace4 100644 --- a/src/js/plugins/bookmark.js +++ b/src/js/plugins/bookmark.js @@ -1,65 +1,57 @@ -import { sendPermissionRequest } from "../notification"; -import { getActiveTab } from '../core'; - -const commands = [ - { - commands: [ - "bookmark", - "bookmark (this) page", - "add (this) (page) (to) bookmark" - ], - callback: () => { - const bookmarkHandler = async () => { - const activeTab = await getActiveTab(); - chrome.bookmarks.create({ - title: activeTab.title, - url: activeTab.url - }); - }; - if (!chrome.bookmarks) { - sendPermissionRequest( - ["bookmarks"], - "bookmark", - "Hey Buddy needs permission to manage your bookmarks.", - bookmarkHandler - ); - } else { - bookmarkHandler(); - } - } - }, - { - commands: [ - "remove (from) bookmark", - "remove this bookmark", - "remove the bookmark" - ], - callback: () => { - const unbookmarkHandler = async () => { - const activeTab = await getActiveTab(); - chrome.bookmarks.search( - { - url: activeTab.url - }, - results => { - if (results.length > 0) { - chrome.bookmarks.remove(results[0].id); - } - } - ); - }; - if (!chrome.bookmarks) { - sendPermissionRequest( - ["bookmarks"], - "remove bookmark", - "Hey Buddy needs permission to manage your bookmarks.", - unbookmarkHandler - ); - } else { - unbookmarkHandler(); - } - } - } -]; - -export default commands; +import { sendPermissionRequest } from "../notification"; +import { getActiveTab } from '../core'; + +const commands = [ + { + action: 'BOOKMARK_ADD', + callback: () => { + const bookmarkHandler = async () => { + const activeTab = await getActiveTab(); + chrome.bookmarks.create({ + title: activeTab.title, + url: activeTab.url + }); + }; + if (!chrome.bookmarks) { + sendPermissionRequest( + ["bookmarks"], + "bookmark", + "Hey Buddy needs permission to manage your bookmarks.", + bookmarkHandler + ); + } else { + bookmarkHandler(); + } + } + }, + { + action: "BOOKMARK_REMOVE", + callback: () => { + const unbookmarkHandler = async () => { + const activeTab = await getActiveTab(); + chrome.bookmarks.search( + { + url: activeTab.url + }, + results => { + if (results.length > 0) { + chrome.bookmarks.remove(results[0].id); + } + } + ); + }; + if (!chrome.bookmarks) { + sendPermissionRequest( + ["bookmarks"], + "remove bookmark", + "Hey Buddy needs permission to manage your bookmarks.", + unbookmarkHandler + ); + } else { + unbookmarkHandler(); + } + } + } +]; + +export default commands; diff --git a/src/js/plugins/extension.js b/src/js/plugins/extension.js index e6237b3..c02fc40 100644 --- a/src/js/plugins/extension.js +++ b/src/js/plugins/extension.js @@ -1,82 +1,80 @@ -import { executeScripts, openTabWithUrl } from '../core'; -import { activeListening } from '../store'; - -const commands = [ - { - commands: ["bye", "bye bye", "goodbye", "good bye", "close"], - callback: () => { - activeListening.set(false); - } - }, - { - commands: ["see supported commands", "what can you do"], - callback: () => { - openTabWithUrl("https://bewisse.com/heybuddy/commands/"); - } - }, - { - commands: ["*query no", "*query cancel", "*query stop"], - callback: () => { - - } - }, - { - commands: ["submit", "enter"], - callback: () => { - executeScripts(` - const focusedElement = document.activeElement; - if (focusedElement.form) { - focusedElement.form.submit(); - } else { - focusedElement.dispatchEvent(new KeyboardEvent("keydown", { - view: window, - keyCode: 13, - bubbles: true, - cancelable: true - })); - } - `); - } - }, - { - commands: ["write *query"], - callback: query => { - query = query.replace("'", "\\'"); - executeScripts(` - const focusedElement = document.activeElement; - const tagName = focusedElement.tagName.toLowerCase(); - const contenteditable = focusedElement.getAttribute('contenteditable') != ''; - let value = '${query}'; - if (tagName == 'input') { - if (document.activeElement.value) { - value = ' ' + value; - } - document.activeElement.value += value; - } else if (tagName == 'textarea') { - value = value.charAt(0).toUpperCase() + value.substr(1); - value += '. '; - const startPos = focusedElement.selectionStart; - const endPos = focusedElement.selectionEnd; - focusedElement.value = focusedElement.value.substring(0, startPos) - + value - + focusedElement.value.substring(endPos, focusedElement.value.length); - focusedElement.selectionEnd = startPos + value.length; - } else if (contenteditable) { - value = value.charAt(0).toUpperCase() + value.substr(1); - value += '. '; - const sel = window.getSelection(); - if (sel.getRangeAt && sel.rangeCount) { - const range = sel.getRangeAt(0); - range.deleteContents(); - const newValueNode = document.createTextNode(value); - range.insertNode(newValueNode); - range.setStart(newValueNode, value.length); - range.setEnd(newValueNode, value.length); - } - } - `); - } - } -]; - -export default commands; +import { executeScripts, openTabWithUrl } from '../core'; +import { activeListening } from '../store'; + +const commands = [ + { + action: 'EXTENSION_CLOSE', + callback: () => { + activeListening.set(false); + } + }, + { + action: 'EXTENSION_HELP', + callback: () => { + openTabWithUrl("https://bewisse.com/heybuddy/commands/"); + } + }, + { + action: 'EXTENSION_CANCEL', + callback: () => {} + }, + { + action: 'EXTENSION_SUBMIT', + callback: () => { + executeScripts(` + const focusedElement = document.activeElement; + if (focusedElement.form) { + focusedElement.form.submit(); + } else { + focusedElement.dispatchEvent(new KeyboardEvent("keydown", { + view: window, + keyCode: 13, + bubbles: true, + cancelable: true + })); + } + `); + } + }, + { + action: 'EXTENSION_WRITE', + callback: query => { + query = query.replace("'", "\\'"); + executeScripts(` + const focusedElement = document.activeElement; + const tagName = focusedElement.tagName.toLowerCase(); + const contenteditable = focusedElement.getAttribute('contenteditable') != ''; + let value = '${query}'; + if (tagName == 'input') { + if (document.activeElement.value) { + value = ' ' + value; + } + document.activeElement.value += value; + } else if (tagName == 'textarea') { + value = value.charAt(0).toUpperCase() + value.substr(1); + value += '. '; + const startPos = focusedElement.selectionStart; + const endPos = focusedElement.selectionEnd; + focusedElement.value = focusedElement.value.substring(0, startPos) + + value + + focusedElement.value.substring(endPos, focusedElement.value.length); + focusedElement.selectionEnd = startPos + value.length; + } else if (contenteditable) { + value = value.charAt(0).toUpperCase() + value.substr(1); + value += '. '; + const sel = window.getSelection(); + if (sel.getRangeAt && sel.rangeCount) { + const range = sel.getRangeAt(0); + range.deleteContents(); + const newValueNode = document.createTextNode(value); + range.insertNode(newValueNode); + range.setStart(newValueNode, value.length); + range.setEnd(newValueNode, value.length); + } + } + `); + } + } +]; + +export default commands; diff --git a/src/js/plugins/media.js b/src/js/plugins/media.js index 324b8ce..92a08dd 100644 --- a/src/js/plugins/media.js +++ b/src/js/plugins/media.js @@ -1,202 +1,191 @@ -import { executeScripts } from '../core'; - -function forAllVideos(perVideoCommand) { - return ( - `const videos = document.getElementsByTagName('video'); - for (let video of videos) { - ${perVideoCommand} - }` - ); -} - -function forAllAudios(perAudioCommand) { - return ( - `const audios = document.getElementsByTagName('audio'); - for (let audio of audios) { - ${perAudioCommand} - }` - ); -} - -const commands = [ - { - commands: ["pause", "stop"], - callback: () => { - executeScripts( - forAllVideos("video.pause();") + forAllAudios("audio.pause();") - ); - } - }, - - { - commands: ["play", "resume"], - callback: () => { - executeScripts( - forAllVideos("video.play();") + forAllAudios("audio.play();") - ); - } - }, - - { - commands: ["increase volume", "volume up"], - callback: () => { - executeScripts( - forAllVideos("video.volume = Math.min(1, video.volume + .2);") + - forAllAudios("audio.volume = Math.min(1, audio.volume + .2);") - ); - } - }, - - { - commands: ["decrease volume", "volume down"], - callback: () => { - executeScripts( - forAllVideos("video.volume = Math.max(0, video.volume - .2);") + - forAllAudios("audio.volume = Math.max(0, audio.volume - .2);") - ); - } - }, - - { - commands: ["(make it) loud"], - callback: () => { - executeScripts( - forAllVideos("video.volume = .8;") + - forAllAudios("audio.volume = .8;") - ); - } - }, - - { - commands: ["(make it) very loud"], - callback: () => { - executeScripts( - forAllVideos("video.volume = 1;") + - forAllAudios("audio.volume = 1;") - ); - } - }, - - { - commands: ["(make it) quiet"], - callback: () => { - executeScripts( - forAllVideos("video.volume = .2;") + - forAllAudios("audio.volume = .2;") - ); - } - }, - - { - commands: ["skip *query seconds", "(go) forward *query seconds"], - callback: query => { - let seconds = parseFloat(query); - if (!(seconds > 0)) { - seconds = 10; - } - executeScripts( - forAllVideos("video.currentTime += " + seconds + ";") + - forAllAudios("audio.currentTime += " + seconds + ";") - ); - } - }, - - { - commands: ["(go) back *query seconds"], - callback: query => { - let seconds = parseFloat(query); - if (!(seconds > 0)) { - seconds = 10; - } - executeScripts( - forAllVideos("video.currentTime -= " + seconds + ";") + - forAllAudios("audio.currentTime -= " + seconds + ";") - ); - } - }, - - { - commands: ["skip *query minutes", "(go) forward *query minutes"], - callback: query => { - let minutes = parseFloat(query); - if (!(minutes > 0)) { - minutes = 1; - } - let seconds = minutes * 60; - executeScripts( - forAllVideos("video.currentTime += " + seconds + ";") + - forAllAudios("audio.currentTime += " + seconds + ";") - ); - } - }, - - { - commands: ["(go) back *query minutes"], - callback: query => { - let minutes = parseFloat(query); - if (!(minutes > 0)) { - minutes = 1; - } - let seconds = minutes * 60; - executeScripts( - forAllVideos("video.currentTime -= " + seconds + ";") + - forAllAudios("audio.currentTime -= " + seconds + ";") - ); - } - }, - - { - commands: [ - "skip 1 minute", - "(go) forward 1 minute", - "skip one minute", - "(go) forward one minute", - "skip a minute", - "(go) forward a minute" - ], - callback: () => { - let seconds = 60; - executeScripts( - forAllVideos("video.currentTime += " + seconds + ";") + - forAllAudios("audio.currentTime += " + seconds + ";") - ); - } - }, - - { - commands: [ - "(go) back 1 minute", - "(go) back one minute", - "(go) back a minute" - ], - callback: () => { - let seconds = 60; - executeScripts( - forAllVideos("video.currentTime -= " + seconds + ";") + - forAllAudios("audio.currentTime -= " + seconds + ";") - ); - } - }, - - { - commands: ["(go) back to (the) beginning"], - callback: () => { - executeScripts( - forAllVideos("video.currentTime = 0;") + - forAllAudios("audio.currentTime = 0;") - ); - } - }, - - { - commands: ["(go) to (the) end"], - callback: () => { - executeScripts( - forAllVideos("video.currentTime = video.duration;") + - forAllAudios("audio.currentTime = audio.duration;") - ); - } - } -]; - -export default commands; +import { executeScripts } from '../core'; + +function forAllVideos(perVideoCommand) { + return ( + `const videos = document.getElementsByTagName('video'); + for (let video of videos) { + ${perVideoCommand} + }` + ); +} + +function forAllAudios(perAudioCommand) { + return ( + `const audios = document.getElementsByTagName('audio'); + for (let audio of audios) { + ${perAudioCommand} + }` + ); +} + +const commands = [ + { + action: 'MEDIA_PAUSE', + callback: () => { + executeScripts( + forAllVideos("video.pause();") + forAllAudios("audio.pause();") + ); + } + }, + + { + action: 'MEDIA_PLAY', + callback: () => { + executeScripts( + forAllVideos("video.play();") + forAllAudios("audio.play();") + ); + } + }, + + { + action: 'MEDIA_VOLUME_UP', + callback: () => { + executeScripts( + forAllVideos("video.volume = Math.min(1, video.volume + .2);") + + forAllAudios("audio.volume = Math.min(1, audio.volume + .2);") + ); + } + }, + + { + action: 'MEDIA_VOLUME_DOWN', + callback: () => { + executeScripts( + forAllVideos("video.volume = Math.max(0, video.volume - .2);") + + forAllAudios("audio.volume = Math.max(0, audio.volume - .2);") + ); + } + }, + + { + action: 'MEDIA_VOLUME_LOUD', + callback: () => { + executeScripts( + forAllVideos("video.volume = .8;") + + forAllAudios("audio.volume = .8;") + ); + } + }, + + { + action: 'MEDIA_VOLUME_VERY_LOUD', + callback: () => { + executeScripts( + forAllVideos("video.volume = 1;") + + forAllAudios("audio.volume = 1;") + ); + } + }, + + { + action: 'MEDIA_VOLUME_QUIET', + callback: () => { + executeScripts( + forAllVideos("video.volume = .2;") + + forAllAudios("audio.volume = .2;") + ); + } + }, + + { + action: 'MEDIA_FORWARD_SECONDS', + callback: query => { + let seconds = parseFloat(query); + if (!(seconds > 0)) { + seconds = 10; + } + executeScripts( + forAllVideos("video.currentTime += " + seconds + ";") + + forAllAudios("audio.currentTime += " + seconds + ";") + ); + } + }, + + { + action: 'MEDIA_BACKWARD_SECONDS', + callback: query => { + let seconds = parseFloat(query); + if (!(seconds > 0)) { + seconds = 10; + } + executeScripts( + forAllVideos("video.currentTime -= " + seconds + ";") + + forAllAudios("audio.currentTime -= " + seconds + ";") + ); + } + }, + + { + action: 'MEDIA_FORWARD_MINUTES', + callback: query => { + let minutes = parseFloat(query); + if (!(minutes > 0)) { + minutes = 1; + } + let seconds = minutes * 60; + executeScripts( + forAllVideos("video.currentTime += " + seconds + ";") + + forAllAudios("audio.currentTime += " + seconds + ";") + ); + } + }, + + { + action: 'MEDIA_BACKWARD_MINUTES', + callback: query => { + let minutes = parseFloat(query); + if (!(minutes > 0)) { + minutes = 1; + } + let seconds = minutes * 60; + executeScripts( + forAllVideos("video.currentTime -= " + seconds + ";") + + forAllAudios("audio.currentTime -= " + seconds + ";") + ); + } + }, + + { + action: 'MEDIA_FORWARD_1_MINUTE', + callback: () => { + let seconds = 60; + executeScripts( + forAllVideos("video.currentTime += " + seconds + ";") + + forAllAudios("audio.currentTime += " + seconds + ";") + ); + } + }, + + { + action: 'MEDIA_BACKWARD_1_MINUTE', + callback: () => { + let seconds = 60; + executeScripts( + forAllVideos("video.currentTime -= " + seconds + ";") + + forAllAudios("audio.currentTime -= " + seconds + ";") + ); + } + }, + + { + action: 'MEDIA_TO_BEGINNING', + callback: () => { + executeScripts( + forAllVideos("video.currentTime = 0;") + + forAllAudios("audio.currentTime = 0;") + ); + } + }, + + { + action: 'MEDIA_TO_END', + callback: () => { + executeScripts( + forAllVideos("video.currentTime = video.duration;") + + forAllAudios("audio.currentTime = audio.duration;") + ); + } + } +]; + +export default commands; diff --git a/src/js/plugins/navigation.js b/src/js/plugins/navigation.js index a0bc659..532f7f1 100644 --- a/src/js/plugins/navigation.js +++ b/src/js/plugins/navigation.js @@ -1,90 +1,57 @@ -import { executeScripts } from '../core'; - -const selectResultScript = ` -function getElementByXpath(path) { - return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; -} - -function selectResult(index) { - const anchor = getElementByXpath( - '((//*[not(ancestor::*[contains(@class,"related-question-pair")])][@class="r"]/a)' - + '|(//a[@id="thumbnail"][not(ancestor::*[@hidden])]))' - + '[' + (index + 1) + ']'); - if (anchor && anchor.href) { - if (anchor.href.startsWith('https://www.amazon.com/')) { - const url = new URL(anchor.href); - url.searchParams.set('tag', 'bewisse-20'); - window.location.href = url.href; - } else { - window.location.href = anchor.href; - } - } -} -`; - -const commands = [ - { - commands: ["go back"], - callback: () => { - executeScripts("window.history.back();"); - } - }, - - { - commands: ["go forward"], - callback: () => { - executeScripts("window.history.forward();"); - } - }, - - { - commands: [ - "reload", - "reload tab", - "reload this tab", - "reload the tab", - "refresh", - "refresh tab", - "refresh this tab", - "refresh the tab" - ], - callback: () => { - chrome.tabs.reload({}); - } - }, -]; - -const wordToIndices = [ - { words: ['best', 'first', 'top', '1st'], index: 0 }, - { words: ['second', '2nd'], index: 1 }, - { words: ['third', '3rd'], index: 2 }, - { words: ['fourth', '4th'], index: 3 }, - { words: ['fifth', '5th'], index: 4 }, - { words: ['sixth', '6th'], index: 5 }, - { words: ['seventh', '7th'], index: 6 }, - { words: ['eighth', '8th'], index: 7 }, - { words: ['ninth', '9th'], index: 8 }, - { words: ['tenth', '10th'], index: 9 }, -]; - -for (const wordToIndex of wordToIndices) { - const words = []; - for (const word of wordToIndex.words) { - words.push(...[ - word, - `${word} result`, - `${word} results`, - `${word} link`, - `${word} answer`, - `${word} response` - ]); - } - commands.push({ - commands: words, - callback: () => { - executeScripts(selectResultScript + `selectResult(${wordToIndex.index});`) - } - }); -} - -export default commands; +import { executeScripts } from '../core'; + +const selectResultScript = ` +function getElementByXpath(path) { + return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; +} + +function selectResult(index) { + const anchor = getElementByXpath( + '((//*[not(ancestor::*[contains(@class,"related-question-pair")])][@class="r"]/a)' + + '|(//a[@id="thumbnail"][not(ancestor::*[@hidden])]))' + + '[' + (index + 1) + ']'); + if (anchor && anchor.href) { + if (anchor.href.startsWith('https://www.amazon.com/')) { + const url = new URL(anchor.href); + url.searchParams.set('tag', 'bewisse-20'); + window.location.href = url.href; + } else { + window.location.href = anchor.href; + } + } +} +`; + +const commands = [ + { + action: 'NAVIGATION_BACK', + callback: () => { + executeScripts("window.history.back();"); + } + }, + + { + action: 'NAVIGATION_FORWARD', + callback: () => { + executeScripts("window.history.forward();"); + } + }, + + { + action: 'NAVIGATION_RELOAD', + callback: () => { + chrome.tabs.reload({}); + } + }, +]; + +for (let i = 0; i < 10; i++) { + commands.push({ + action: `NAVIGATION_SELECT_${i + 1}`, + callback: () => { + executeScripts(selectResultScript + `selectResult(${i});`); + } + }); +} + +export default commands; diff --git a/src/js/plugins/page.js b/src/js/plugins/page.js index 8f30bc6..0a82bb7 100644 --- a/src/js/plugins/page.js +++ b/src/js/plugins/page.js @@ -1,120 +1,114 @@ -import { executeScripts, getActiveTab } from '../core'; - -const ZOOM_LEVEL = [ - 0.25, - 0.33, - 0.5, - 0.67, - 0.75, - 0.8, - 0.9, - 1, - 1.1, - 1.25, - 1.5, - 1.75, - 2, - 2.5, - 3, - 4, - 5 -]; - -const commands = [ - { - commands: [ - "zoom in", - "zoom", - "enlarge", - "make (it) larger", - "make (it) bigger" - ], - callback: async () => { - const activeTab = await getActiveTab(); - chrome.tabs.getZoom(activeTab.id, zoomFactor => { - for (let zoomLevel of ZOOM_LEVEL) { - if (zoomFactor + 0.01 < zoomLevel) { - chrome.tabs.setZoom(activeTab.id, zoomLevel); - break; - } - } - }); - } - }, - - { - commands: ["zoom out", "make (it) smaller"], - callback: async () => { - const activeTab = await getActiveTab(); - chrome.tabs.getZoom(activeTab.id, zoomFactor => { - for (let i = ZOOM_LEVEL.length - 1; i >= 0; i--) { - const zoomLevel = ZOOM_LEVEL[i]; - if (zoomFactor - 0.01 > zoomLevel) { - chrome.tabs.setZoom(activeTab.id, zoomLevel); - break; - } - } - }); - } - }, - { - commands: ["reset zoom", "make (it) normal size"], - callback: async () => { - const activeTab = await getActiveTab(); - chrome.tabs.setZoom(activeTab.id, 1); - } - }, - - { - commands: ["find *query", "look for *query"], - callback: query => { - executeScripts(`window.find('${query}');`); - } - }, - - { - commands: ["scroll down", "(go) down"], - callback: () => { - executeScripts("window.scrollBy(0, 250);"); - } - }, - - { - commands: ["page down"], - callback: () => { - executeScripts("window.scrollBy(0, 1000);"); - } - }, - - { - commands: ["scroll up", "(go) up"], - callback: () => { - executeScripts("window.scrollBy(0, -250);"); - } - }, - - { - commands: ["page up"], - callback: () => { - executeScripts("window.scrollBy(0, -1000);"); - } - }, - - { - commands: ["go to (the) top", "scroll to (the) top"], - callback: () => { - executeScripts("window.scrollTo(0, 0);"); - } - }, - - { - commands: ["go to (the) bottom", "scroll to (the) bottom"], - callback: () => { - executeScripts( - "window.scrollTo(0, document.body.scrollHeight);" - ); - } - } -]; - -export default commands; +import { executeScripts, getActiveTab } from '../core'; + +const ZOOM_LEVEL = [ + 0.25, + 0.33, + 0.5, + 0.67, + 0.75, + 0.8, + 0.9, + 1, + 1.1, + 1.25, + 1.5, + 1.75, + 2, + 2.5, + 3, + 4, + 5 +]; + +const commands = [ + { + action: 'PAGE_ZOOM_IN', + callback: async () => { + const activeTab = await getActiveTab(); + chrome.tabs.getZoom(activeTab.id, zoomFactor => { + for (let zoomLevel of ZOOM_LEVEL) { + if (zoomFactor + 0.01 < zoomLevel) { + chrome.tabs.setZoom(activeTab.id, zoomLevel); + break; + } + } + }); + } + }, + + { + action: 'PAGE_ZOOM_OUT', + callback: async () => { + const activeTab = await getActiveTab(); + chrome.tabs.getZoom(activeTab.id, zoomFactor => { + for (let i = ZOOM_LEVEL.length - 1; i >= 0; i--) { + const zoomLevel = ZOOM_LEVEL[i]; + if (zoomFactor - 0.01 > zoomLevel) { + chrome.tabs.setZoom(activeTab.id, zoomLevel); + break; + } + } + }); + } + }, + { + action: 'PAGE_ZOOM_RESET', + callback: async () => { + const activeTab = await getActiveTab(); + chrome.tabs.setZoom(activeTab.id, 1); + } + }, + + { + action: 'PAGE_FIND', + callback: query => { + executeScripts(`window.find('${query}');`); + } + }, + + { + action: 'PAGE_SCROLL_DOWN', + callback: () => { + executeScripts("window.scrollBy(0, 250);"); + } + }, + + { + action: 'PAGE_PAGE_DOWN', + callback: () => { + executeScripts("window.scrollBy(0, 1000);"); + } + }, + + { + action: 'PAGE_SCROLL_UP', + callback: () => { + executeScripts("window.scrollBy(0, -250);"); + } + }, + + { + action: 'PAGE_PAGE_UP', + callback: () => { + executeScripts("window.scrollBy(0, -1000);"); + } + }, + + { + action: 'PAGE_TO_TOP', + callback: () => { + executeScripts("window.scrollTo(0, 0);"); + } + }, + + { + action: 'PAGE_TO_BOTTOM', + callback: () => { + executeScripts( + "window.scrollTo(0, document.body.scrollHeight);" + ); + } + } +]; + +export default commands; diff --git a/src/js/plugins/query.js b/src/js/plugins/query.js index df18b81..5978301 100644 --- a/src/js/plugins/query.js +++ b/src/js/plugins/query.js @@ -1,289 +1,212 @@ -import cheerio from "cheerio"; -import axios from "axios"; -import { activeListening } from '../store'; -import { openTabWithUrl, performActionWithDelay } from '../core'; - -/** ------- Search query ------- */ -const prependQueryPhrase = queries => { - let updatedQueries = Array.from(queries); - for (let query of queries) { - updatedQueries.push("show me " + query); - updatedQueries.push("show me a " + query); - updatedQueries.push("show me the " + query); - } - return updatedQueries; -}; - -async function loadAndParsePage(url) { - const response = await axios.get(url); - const parsedResponse = cheerio.load(response.data); - return parsedResponse; -} - -async function generateGoogleLuckyUrl(query) { - const parsedResponse = await loadAndParsePage("https://www.google.com/"); - const sxsrf = parsedResponse("input[name='sxsrf']").attr("value"); - const ei = parsedResponse("input[name='ei']").attr("value"); - const iflsig = parsedResponse("input[name='iflsig']").attr("value"); - const source = parsedResponse("input[name='source']").attr("value"); - const url = new URL("https://www.google.com/search"); - url.searchParams.set("sxsrf", sxsrf); - url.searchParams.set("ei", ei); - url.searchParams.set("iflsig", iflsig); - url.searchParams.set("source", source); - url.searchParams.set("btnI", "I'm Feeling Lucky"); - url.searchParams.set("q", query); - return url.href; -} - -const siteToUrl = { - Bing: "https://www.bing.com/search?q=", - AOL: "https://search.aol.com/aol/search?q=", - Yahoo: "https://search.yahoo.com/search?p=", - Amazon: "https://www.amazon.com/s?tag=bewisse-20&k=", - Walmart: "https://www.walmart.com/search/?query=", - Target: "https://www.target.com/s?searchTerm=", - YouTube: "https://www.youtube.com/results?search_query=", - Baidu: "http://www.baidu.com/s?wd=", - Wikipedia: "https://www.wikipedia.org/wiki/" -}; - -const searchCommands = []; -for (const key in siteToUrl) { - searchCommands.push({ - commands: [ - "search for *query on " + key, - "search for *query at " + key, - "*query on " + key, - "*query at " + key - ], - callback: query => { - openTabWithUrl(siteToUrl[key] + query); - }, - priority: 0.3 - }); -} - -const commands = [ - { - commands: prependQueryPhrase([ - "image of *query", - "photo of *query", - "picture of *query", - "images of *query", - "photos of *query", - "pictures of *query" - ]), - callback: query => { - openTabWithUrl( - "https://www.google.com/search?tbm=isch&q=" + query - ); - } - }, - { - commands: prependQueryPhrase(["news of *query", "news about *query"]), - callback: query => { - openTabWithUrl( - "https://www.google.com/search?tbm=nws&q=" + query - ); - } - }, - { - commands: prependQueryPhrase([ - "news", - "news today", - "today news", - "today's news" - ]), - callback: () => { - openTabWithUrl("https://news.google.com/"); - } - }, - { - commands: prependQueryPhrase([ - "map of *query", - "map to *query", - "how to get to *query", - "how do I get to *query" - ]), - callback: query => { - openTabWithUrl("https://www.google.com/maps?q=" + query); - } - }, - - { - commands: prependQueryPhrase([ - "direction from *from to *to", - "directions from *from to *to" - ]), - callback: (from, to) => { - performActionWithDelay(() => { - openTabWithUrl( - "https://www.google.com/maps/dir/" + from + "/" + to - ); - }); - } - }, - - { - commands: prependQueryPhrase([ - "direction from *query", - "directions from *query" - ]), - callback: query => { - openTabWithUrl("https://www.google.com/maps/dir/" + query); - } - }, - - { - commands: prependQueryPhrase([ - "direction to *query", - "directions to *query" - ]), - callback: query => { - openTabWithUrl("https://www.google.com/maps/dir//" + query); - } - }, - { - commands: ["wikipedia"], - callback: () => { - openTabWithUrl("https://wikipedia.org/"); - } - }, - { - commands: [ - "about *query", - "wiki about *query", - "wiki *query", - "wikipedia about *query", - "wikipedia *query" - ], - callback: query => { - openTabWithUrl( - "https://en.wikipedia.org/wiki/Special:Search/" + query - ); - } - }, - { - commands: ["play video", "video", "play videos", "videos"], - callback: () => { - openTabWithUrl("https://www.youtube.com/"); - } - }, - { - commands: prependQueryPhrase([ - "video of *query", - "video about *query", - "video *query", - "videos of *query", - "videos about *query", - "videos *query", - "watch *query" - ]), - callback: query => { - openTabWithUrl( - "https://www.youtube.com/results?search_query=" + query - ); - } - }, - { - commands: ["play music", "music"], - callback: () => { - openTabWithUrl("https://play.google.com/music/listen"); - } - }, - { - commands: ["shopping", "buy something"], - callback: () => { - openTabWithUrl("https://www.amazon.com/?tag=bewisse-20"); - } - }, - { - commands: [ - "shop for a *query", - "shop for *query", - "buy a *query", - "buy *query" - ], - callback: query => { - openTabWithUrl( - "https://www.amazon.com/s?tag=bewisse-20&k=" + query - ); - } - }, - { - commands: [ - "go to download", - "open download", - "go to downloads", - "open downloads" - ], - callback: query => { - openTabWithUrl("chrome://downloads"); - } - }, - - { - commands: [ - "go to bookmark", - "open bookmark", - "go to bookmarks", - "open bookmarks" - ], - callback: query => { - openTabWithUrl("chrome://bookmarks"); - } - }, - - { - commands: ["go to history", "open history"], - callback: query => { - openTabWithUrl("chrome://history"); - } - }, - - { - commands: ["go to *query", "open *query", "take me to *query"], - callback: async query => { - openTabWithUrl(await generateGoogleLuckyUrl(query)); - } - }, - ...searchCommands, - { - commands: [ - "search for *query on *site", - "search for *query at *site", - "*query on *site", - "*query at *site" - ], - callback: async (query, site) => { - openTabWithUrl( - await generateGoogleLuckyUrl(query + " on " + site) - ); - }, - priority: 0.3 - }, - { - commands: ["search for *query", "google *query", "*query"], - callback: query => { - chrome.storage.local.get(["tts"], result => { - // If TTS is enabled, we need to clear notifications to avoid the TTS to feedback into the command. - if (result.tts) { - openTabWithUrl( - "https://www.google.com/search?gs_ivs=1&q=" + - encodeURIComponent(query) - ); - activeListening.set(false); - } else { - openTabWithUrl( - "https://www.google.com/search?q=" + - encodeURIComponent(query) - ); - } - }); - }, - priority: 0.2 - } -]; - -export default commands; +import cheerio from "cheerio"; +import axios from "axios"; +import { activeListening } from '../store'; +import { openTabWithUrl, performActionWithDelay } from '../core'; + +async function loadAndParsePage(url) { + const response = await axios.get(url); + return cheerio.load(response.data); +} + +async function generateGoogleLuckyUrl(query) { + const parsedResponse = await loadAndParsePage("https://www.google.com/"); + const sxsrf = parsedResponse("input[name='sxsrf']").attr("value"); + const ei = parsedResponse("input[name='ei']").attr("value"); + const iflsig = parsedResponse("input[name='iflsig']").attr("value"); + const source = parsedResponse("input[name='source']").attr("value"); + const url = new URL("https://www.google.com/search"); + url.searchParams.set("sxsrf", sxsrf); + url.searchParams.set("ei", ei); + url.searchParams.set("iflsig", iflsig); + url.searchParams.set("source", source); + url.searchParams.set("btnI", "I'm Feeling Lucky"); + url.searchParams.set("q", query); + return url.href; +} + +const siteToUrl = { + Bing: "https://www.bing.com/search?q=", + AOL: "https://search.aol.com/aol/search?q=", + Yahoo: "https://search.yahoo.com/search?p=", + Amazon: "https://www.amazon.com/s?tag=bewisse-20&k=", + Walmart: "https://www.walmart.com/search/?query=", + Target: "https://www.target.com/s?searchTerm=", + YouTube: "https://www.youtube.com/results?search_query=", + Baidu: "http://www.baidu.com/s?wd=", + Wikipedia: "https://www.wikipedia.org/wiki/" +}; + +const searchCommands = []; +for (const key in siteToUrl) { + searchCommands.push({ + action: `QUERY_SEARCH_QUERY_ON_${key.toUpperCase()}`, + callback: query => { + openTabWithUrl(siteToUrl[key] + query); + }, + priority: 0.3 + }); +} + +const commands = [ + { + action: 'QUERY_SEARCH_IMAGE', + callback: query => { + openTabWithUrl( + "https://www.google.com/search?tbm=isch&q=" + query + ); + } + }, + { + action: 'QUERY_SEARCH_NEWS', + callback: query => { + openTabWithUrl( + "https://www.google.com/search?tbm=nws&q=" + query + ); + } + }, + { + action: 'QUERY_NEWS', + callback: () => { + openTabWithUrl("https://news.google.com/"); + } + }, + { + action: 'QUERY_SEARCH_MAP', + callback: query => { + openTabWithUrl("https://www.google.com/maps?q=" + query); + } + }, + + { + action: 'QUERY_SEARCH_DIRECTION_FROM_TO', + callback: (from, to) => { + performActionWithDelay(() => { + openTabWithUrl( + "https://www.google.com/maps/dir/" + from + "/" + to + ); + }); + } + }, + + { + action: 'QUERY_SEARCH_DIRECTION_FROM', + callback: query => { + openTabWithUrl("https://www.google.com/maps/dir/" + query); + } + }, + + { + action: 'QUERY_SEARCH_DIRECTION_TO', + callback: query => { + openTabWithUrl("https://www.google.com/maps/dir//" + query); + } + }, + { + action: 'QUERY_GO_TO_WIKIPEDIA', + callback: () => { + openTabWithUrl("https://wikipedia.org/"); + } + }, + { + action: 'QUERY_SEARCH_WIKIPEDIA', + callback: query => { + openTabWithUrl( + "https://en.wikipedia.org/wiki/Special:Search/" + query + ); + } + }, + { + action: 'QUERY_GO_TO_VIDEO', + callback: () => { + openTabWithUrl("https://www.youtube.com/"); + } + }, + { + action: 'QUERY_SEARCH_VIDEO', + callback: query => { + openTabWithUrl( + "https://www.youtube.com/results?search_query=" + query + ); + } + }, + { + action: 'QUERY_GO_TO_MUSIC', + callback: () => { + openTabWithUrl("https://play.google.com/music/listen"); + } + }, + { + action: 'QUERY_GO_TO_SHOPPING', + callback: () => { + openTabWithUrl("https://www.amazon.com/?tag=bewisse-20"); + } + }, + { + action: 'QUERY_SEARCH_SHOPPING', + callback: query => { + openTabWithUrl( + "https://www.amazon.com/s?tag=bewisse-20&k=" + query + ); + } + }, + { + action: 'QUERY_GO_TO_DOWNLOADS', + callback: () => { + openTabWithUrl("chrome://downloads"); + } + }, + + { + action: 'QUERY_GO_TO_BOOKMARKS', + callback: () => { + openTabWithUrl("chrome://bookmarks"); + } + }, + + { + action: 'QUERY_GO_TO_HISTORY', + callback: () => { + openTabWithUrl("chrome://history"); + } + }, + + { + action: 'QUERY_GO_TO_QUERY', + callback: async query => { + openTabWithUrl(await generateGoogleLuckyUrl(query)); + } + }, + ...searchCommands, + { + action: 'QUERY_SEARCH_QUERY_ON_SITE', + callback: async (query, site) => { + openTabWithUrl( + await generateGoogleLuckyUrl(query + " on " + site) + ); + }, + priority: 0.3 + }, + { + action: 'QUERY_SEARCH_QUERY', + callback: query => { + chrome.storage.local.get(["tts"], result => { + // If TTS is enabled, we need to clear notifications to avoid the TTS to feedback into the command. + if (result.tts) { + openTabWithUrl( + "https://www.google.com/search?gs_ivs=1&q=" + + encodeURIComponent(query) + ); + activeListening.set(false); + } else { + openTabWithUrl( + "https://www.google.com/search?q=" + + encodeURIComponent(query) + ); + } + }); + }, + priority: 0.2 + } +]; + +export default commands; diff --git a/src/js/plugins/tab.js b/src/js/plugins/tab.js index 9dd2bd4..98111d7 100644 --- a/src/js/plugins/tab.js +++ b/src/js/plugins/tab.js @@ -1,344 +1,326 @@ -import { performActionWithDelay } from '../core'; - -const commands = [ - { - /** ------- Tab management commands ------- */ - commands: ["open new tab"], - callback: () => { - performActionWithDelay(() => { - chrome.tabs.create({}); - }); - } - }, - { - commands: [ - "close tab", - "close this tab", - "close the tab", - ], - callback: () => { - performActionWithDelay(() => { - chrome.tabs.query( - { - active: true - }, - tabs => { - if (tabs.length > 0) { - chrome.tabs.remove(tabs[0].id); - } - } - ); - }); - }, - priority: 1 - }, - { - commands: [ - "close other tab", - "close other tabs", - "close the other tab", - "close the other tabs", - "close all (tabs) but this (tab)" - ], - callback: () => { - chrome.tabs.query( - { - active: false - }, - tabs => { - for (let tab of tabs) { - chrome.tabs.remove(tab.id); - } - } - ); - } - }, - - { - commands: ["close tab to the right", "close tabs to the right"], - callback: () => { - chrome.tabs.query({}, tabs => { - let activeTabIndex = -1; - for (let tab of tabs) { - if (tab.active) { - activeTabIndex = tab.index; - } - } - for (let tab of tabs) { - if (tab.index > activeTabIndex) { - chrome.tabs.remove(tab.id); - } - } - }); - } - }, - - { - commands: ["close tab to the left", "close tabs to the left"], - callback: () => { - chrome.tabs.query({}, tabs => { - let activeTabIndex = -1; - for (let tab of tabs) { - if (tab.active) { - activeTabIndex = tab.index; - } - } - for (let tab of tabs) { - if (tab.index < activeTabIndex) { - chrome.tabs.remove(tab.id); - } - } - }); - } - }, - { - commands: [ - "close all tab", - "close all tabs", - "close all the tab", - "close all the tabs", - "close window", - "close this window", - "exit window" - ], - callback: () => { - performActionWithDelay(() => { - chrome.windows.getCurrent({}, window => { - chrome.windows.remove(window.id); - }); - }); - } - }, - - { - commands: ["pin", "pin tab", "pin this tab", "pin the tab"], - callback: () => { - chrome.tabs.query( - { - active: true - }, - tabs => { - if (tabs.length > 0) { - chrome.tabs.update(tabs[0].id, { pinned: true }); - } - } - ); - } - }, - - { - commands: ["unpin", "unpin tab", "unpin this tab", "unpin the tab"], - callback: () => { - chrome.tabs.query( - { - active: true - }, - tabs => { - if (tabs.length > 0) { - chrome.tabs.update(tabs[0].id, { pinned: false }); - } - } - ); - } - }, - - { - commands: ["mute", "mute tab"], - callback: () => { - chrome.tabs.query( - { - active: true - }, - tabs => { - if (tabs.length > 0) { - chrome.tabs.update(tabs[0].id, { muted: true }); - } - } - ); - } - }, - - { - commands: ["unmute", "unmute tab"], - callback: () => { - chrome.tabs.query( - { - active: true - }, - tabs => { - if (tabs.length > 0) { - chrome.tabs.update(tabs[0].id, { muted: false }); - } - } - ); - } - }, - - { - commands: ["mute all (the) tab", "mute all (the) tabs"], - callback: () => { - chrome.tabs.query({}, tabs => { - for (let tab of tabs) { - chrome.tabs.update(tab.id, { muted: true }); - } - }); - } - }, - - { - commands: ["unmute all (the) tab", "unmute all (the) tabs"], - callback: () => { - chrome.tabs.query({}, tabs => { - for (let tab of tabs) { - chrome.tabs.update(tab.id, { muted: false }); - } - }); - } - }, - - { - commands: ["mute other (the) tab", "mute other (the) tabs"], - callback: () => { - chrome.tabs.query({}, tabs => { - for (let tab of tabs) { - if (!tab.active) { - chrome.tabs.update(tab.id, { muted: true }); - } - } - }); - } - }, - - { - commands: ["unmute other (the) tab", "unmute other (the) tabs"], - callback: () => { - chrome.tabs.query({}, tabs => { - for (let tab of tabs) { - if (!tab.active) { - chrome.tabs.update(tab.id, { muted: false }); - } - } - }); - } - }, - - { - commands: ["maximize", "maximize window"], - callback: () => { - chrome.windows.getCurrent({}, window => { - chrome.windows.update(window.id, { state: "maximized" }); - }); - } - }, - - { - commands: ["minimize", "minimize window"], - callback: () => { - chrome.windows.getCurrent({}, window => { - chrome.windows.update(window.id, { state: "minimized" }); - }); - } - }, - { - commands: ["side by side"], - callback: () => { - chrome.tabs.query({}, (tabs => { - if (tabs.length >= 2) { - tabs.sort((a, b) => { - if (a.currentWindow && a.active) { - return -1; - } - if (b.currentWindow && b.active) { - return 1; - } - return b.index - a.index - }); - const screen = window.screen; - const tabA = tabs[0]; - const tabB = tabs[1]; - if (tabA.windowId === tabB.windowId) { - chrome.windows.create({ - tabId: tabB.id, - left: Math.ceil(screen.availWidth / 2), - top: 0, - width: screen.availWidth / 2, - height: screen.availHeight - }) - } else { - chrome.windows.update(tabB.windowId, { - left: Math.ceil(screen.availWidth / 2), - top: 0, - width: screen.availWidth / 2, - height: screen.availHeight - }); - } - - chrome.windows.update(tabA.windowId, { - left: 0, - top: 0, - width: Math.ceil(screen.availWidth / 2), - height: screen.availHeight, - focused: true - }); - } - })) - } - }, - { - commands: [ - "fullscreen", - "full screen", - "full-screen", - "enter fullscreen", - "enter full screen", - "enter full-screen" - ], - callback: () => { - chrome.windows.getCurrent({}, window => { - chrome.windows.update(window.id, { state: "fullscreen" }); - }); - } - }, - - { - commands: ["exit fullscreen", "exit full screen", "exit full-screen"], - callback: () => { - chrome.windows.getCurrent({}, window => { - chrome.windows.update(window.id, { state: "normal" }); - }); - } - }, - - { - commands: ["next tab", "switch (to) right tab", "switch tab", "right tab"], - callback: () => { - chrome.tabs.query({}, tabs => { - let activeTabIndex = -1; - for (const tab of tabs) { - if (tab.active) { - activeTabIndex = tab.index; - } - } - const nextTabIndex = activeTabIndex + 1 < tabs.length ? activeTabIndex + 1 : 0; - chrome.tabs.update(tabs[nextTabIndex].id, { active: true }); - }); - } - }, - { - commands: ["previous tab", "last tab", "switch (to) left tab", "left tab"], - callback: () => { - chrome.tabs.query({}, tabs => { - let activeTabIndex = -1; - for (const tab of tabs) { - if (tab.active) { - activeTabIndex = tab.index; - } - } - const nextTabIndex = activeTabIndex - 1 > 0 ? activeTabIndex - 1 : tabs.length - 1; - chrome.tabs.update(tabs[nextTabIndex].id, { active: true }); - }); - } - } -]; - -export default commands; +import { performActionWithDelay } from '../core'; + +const commands = [ + { + /** ------- Tab management commands ------- */ + action: 'TABS_NEW_TAB', + callback: () => { + performActionWithDelay(() => { + chrome.tabs.create({}); + }); + } + }, + { + action: 'TABS_CLOSE_TAB', + callback: () => { + performActionWithDelay(() => { + chrome.tabs.query( + { + active: true + }, + tabs => { + if (tabs.length > 0) { + chrome.tabs.remove(tabs[0].id); + } + } + ); + }); + }, + priority: 1 + }, + { + action: 'TABS_CLOSE_OTHER_TABS', + callback: () => { + chrome.tabs.query( + { + active: false + }, + tabs => { + for (let tab of tabs) { + chrome.tabs.remove(tab.id); + } + } + ); + } + }, + + { + action: 'TABS_CLOSE_RIGHT_TABS', + callback: () => { + chrome.tabs.query({}, tabs => { + let activeTabIndex = -1; + for (let tab of tabs) { + if (tab.active) { + activeTabIndex = tab.index; + } + } + for (let tab of tabs) { + if (tab.index > activeTabIndex) { + chrome.tabs.remove(tab.id); + } + } + }); + } + }, + + { + action: 'TABS_CLOSE_LEFT_TABS', + callback: () => { + chrome.tabs.query({}, tabs => { + let activeTabIndex = -1; + for (let tab of tabs) { + if (tab.active) { + activeTabIndex = tab.index; + } + } + for (let tab of tabs) { + if (tab.index < activeTabIndex) { + chrome.tabs.remove(tab.id); + } + } + }); + } + }, + { + action: 'TABS_CLOSE_ALL_TABS', + callback: () => { + performActionWithDelay(() => { + chrome.windows.getCurrent({}, window => { + chrome.windows.remove(window.id); + }); + }); + } + }, + + { + action: 'TABS_PIN_TAB', + callback: () => { + chrome.tabs.query( + { + active: true + }, + tabs => { + if (tabs.length > 0) { + chrome.tabs.update(tabs[0].id, { pinned: true }); + } + } + ); + } + }, + + { + action: 'TABS_UNPIN_TAB', + callback: () => { + chrome.tabs.query( + { + active: true + }, + tabs => { + if (tabs.length > 0) { + chrome.tabs.update(tabs[0].id, { pinned: false }); + } + } + ); + } + }, + + { + action: 'TABS_MUTE_TAB', + callback: () => { + chrome.tabs.query( + { + active: true + }, + tabs => { + if (tabs.length > 0) { + chrome.tabs.update(tabs[0].id, { muted: true }); + } + } + ); + } + }, + + { + action: 'TABS_UNMUTE_TAB', + callback: () => { + chrome.tabs.query( + { + active: true + }, + tabs => { + if (tabs.length > 0) { + chrome.tabs.update(tabs[0].id, { muted: false }); + } + } + ); + } + }, + + { + action: 'TABS_MUTE_ALL_TABS', + callback: () => { + chrome.tabs.query({}, tabs => { + for (let tab of tabs) { + chrome.tabs.update(tab.id, { muted: true }); + } + }); + } + }, + + { + action: 'TABS_UNMUTE_ALL_TABS', + callback: () => { + chrome.tabs.query({}, tabs => { + for (let tab of tabs) { + chrome.tabs.update(tab.id, { muted: false }); + } + }); + } + }, + + { + action: 'TABS_MUTE_OTHER_TABS', + callback: () => { + chrome.tabs.query({}, tabs => { + for (let tab of tabs) { + if (!tab.active) { + chrome.tabs.update(tab.id, { muted: true }); + } + } + }); + } + }, + + { + + action: 'TABS_UNMUTE_OTHER_TABS', + callback: () => { + chrome.tabs.query({}, tabs => { + for (let tab of tabs) { + if (!tab.active) { + chrome.tabs.update(tab.id, { muted: false }); + } + } + }); + } + }, + + { + + action: 'TABS_MAXIMIZE_WINDOW', + callback: () => { + chrome.windows.getCurrent({}, window => { + chrome.windows.update(window.id, { state: "maximized" }); + }); + } + }, + + { + action: 'TABS_MINIMIZE_WINDOW', + callback: () => { + chrome.windows.getCurrent({}, window => { + chrome.windows.update(window.id, { state: "minimized" }); + }); + } + }, + { + + action: 'TABS_SIDE_BY_SIDE', + callback: () => { + chrome.tabs.query({}, (tabs => { + if (tabs.length >= 2) { + tabs.sort((a, b) => { + if (a.currentWindow && a.active) { + return -1; + } + if (b.currentWindow && b.active) { + return 1; + } + return b.index - a.index + }); + const screen = window.screen; + const tabA = tabs[0]; + const tabB = tabs[1]; + if (tabA.windowId === tabB.windowId) { + chrome.windows.create({ + tabId: tabB.id, + left: Math.ceil(screen.availWidth / 2), + top: 0, + width: screen.availWidth / 2, + height: screen.availHeight + }) + } else { + chrome.windows.update(tabB.windowId, { + left: Math.ceil(screen.availWidth / 2), + top: 0, + width: screen.availWidth / 2, + height: screen.availHeight + }); + } + + chrome.windows.update(tabA.windowId, { + left: 0, + top: 0, + width: Math.ceil(screen.availWidth / 2), + height: screen.availHeight, + focused: true + }); + } + })) + } + }, + { + + action: 'TABS_ENTER_FULLSCREEN', + callback: () => { + chrome.windows.getCurrent({}, window => { + chrome.windows.update(window.id, { state: "fullscreen" }); + }); + } + }, + + { + + action: 'TABS_EXIT_FULLSCREEN', + callback: () => { + chrome.windows.getCurrent({}, window => { + chrome.windows.update(window.id, { state: "normal" }); + }); + } + }, + + { + + action: 'TABS_NEXT_TAB', + callback: () => { + chrome.tabs.query({}, tabs => { + let activeTabIndex = -1; + for (const tab of tabs) { + if (tab.active) { + activeTabIndex = tab.index; + } + } + const nextTabIndex = activeTabIndex + 1 < tabs.length ? activeTabIndex + 1 : 0; + chrome.tabs.update(tabs[nextTabIndex].id, { active: true }); + }); + } + }, + { + + action: 'TABS_PREVIOUS_TAB', + callback: () => { + chrome.tabs.query({}, tabs => { + let activeTabIndex = -1; + for (const tab of tabs) { + if (tab.active) { + activeTabIndex = tab.index; + } + } + const nextTabIndex = activeTabIndex - 1 > 0 ? activeTabIndex - 1 : tabs.length - 1; + chrome.tabs.update(tabs[nextTabIndex].id, { active: true }); + }); + } + } +]; + +export default commands;