From f76c91fe0318bb92c036173cc5d9c09d5c487e0a Mon Sep 17 00:00:00 2001 From: Matthew Sherrill Date: Sat, 7 Oct 2023 14:53:46 -0600 Subject: [PATCH 1/4] update logging, remove onInstall event - ms --- src/ts/background.ts | 15 ++++++--------- src/ts/common.ts | 14 ++++++++------ src/ts/options.ts | 14 ++++++++------ src/ts/voiceDim.ts | 45 ++++++++++++++++++++++---------------------- 4 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/ts/background.ts b/src/ts/background.ts index db5c4a6..6d6f7c4 100644 --- a/src/ts/background.ts +++ b/src/ts/background.ts @@ -1,7 +1,8 @@ import { infoLog } from './common'; +const tag = 'background'; chrome.commands.onCommand.addListener((command: any) => { - infoLog('voice dim', `Command "${command}" triggered`); + infoLog(tag, `Command "${command}" triggered`); sendDimTabMessage({ dimShortcutPressed: true }); }); @@ -23,30 +24,26 @@ async function getDimTabId(): Promise { async function sendDimTabMessage(message: any) { const dimTabId = await getDimTabId(); if (dimTabId) { - infoLog('message', 'sending', message); + infoLog(tag, 'sending', message); chrome.tabs.sendMessage(dimTabId, message, (response: any) => { - infoLog('voice dim', { response }); + infoLog(tag, { response }); }); } } chrome.runtime.onMessage.addListener((data: any, sender: chrome.runtime.MessageSender) => { - infoLog('voice dim', { data }); + infoLog(tag, { data }); if (data === 'showOptions') { openOptionsPage(); } }); -chrome.runtime.onInstalled.addListener(() => { - openOptionsPage(); -}); - async function openOptionsPage() { const [optionsTab] = await chrome.tabs.query({ url: `chrome-extension://${chrome.runtime.id}\/html\/options.html`, }); - infoLog('voice dim', { optionsTab }); + infoLog(tag, { optionsTab }); if (!optionsTab) chrome.tabs.create({ url: '../html/options.html' }); else { chrome.tabs.update(optionsTab.id!, { active: true }); diff --git a/src/ts/common.ts b/src/ts/common.ts index 9c6bb87..5d16124 100644 --- a/src/ts/common.ts +++ b/src/ts/common.ts @@ -1,5 +1,7 @@ export const logs: Log[] = []; +const commonTag = 'common'; + export type Log = { tag: string; message: unknown; @@ -7,12 +9,12 @@ export type Log = { }; export function infoLog(tag: string, message: unknown, ...args: unknown[]) { - console.log(`[${tag}]`, message, ...args); + console.log(`[voice dim - ${tag}]`, message, ...args); logs.push({ tag: `[${tag} info]`, message, args }); } export function debugLog(tag: string, message: unknown, ...args: unknown[]) { - console.debug(`[${tag}]`, message, ...args); + console.debug(`[voice dim - ${tag}]`, message, ...args); logs.push({ tag: `[${tag} debug]`, message, args }); } @@ -71,7 +73,7 @@ export async function waitForElementToDisplay( } else { setTimeout(function () { if (timeoutInMs && Date.now() - startTimeInMs > timeoutInMs) { - debugLog('voice dim', "couldn't find", selector); + debugLog(commonTag, "couldn't find", selector); return reject(); } loopSearch(); @@ -104,7 +106,7 @@ export const DEFAULT_ALWAYS_LISTENING: AlwaysListening = { export function store(key: string, value: T) { chrome.storage.local.set({ [key]: value }, () => { - infoLog('voice dim', 'Stored', key, value); + infoLog(commonTag, 'Stored', key, value); }); } @@ -115,12 +117,12 @@ export function retrieve(key: string, defaultValue: T): Promise { console.error(chrome.runtime.lastError.message); reject(chrome.runtime.lastError.message); } - infoLog('voice dim', { result }); + infoLog(commonTag, { result }); if (Object.keys(result).length == 0) { store(key, defaultValue); resolve(defaultValue); } - infoLog('voice dim', 'Found', result[key]); + infoLog(commonTag, 'Found', result[key]); resolve(result[key]); }); }); diff --git a/src/ts/options.ts b/src/ts/options.ts index 5589256..359605f 100644 --- a/src/ts/options.ts +++ b/src/ts/options.ts @@ -10,6 +10,8 @@ import { store, } from './common'; +const tag = 'options'; + function onCommandChange() { const commands: Record = {}; Object.keys(DEFAULT_COMMANDS).forEach((command) => { @@ -24,7 +26,7 @@ function onCommandChange() { const dimTabs = tabs.filter((tab) => tab.url?.match(/destinyitemmanager\.com.*inventory/)); if (dimTabs && dimTabs[0].id) chrome.tabs.sendMessage(dimTabs[0].id, 'shortcut updated', (response) => { - infoLog('voice dim', { response }); + infoLog(tag, { response }); }); }); } @@ -34,13 +36,13 @@ function sendListenOptionsMessage() { const dimTab = tabs.filter((tab) => tab.url?.match(/destinyitemmanager\.com.*inventory/))[0]; if (dimTab.id) chrome.tabs.sendMessage(dimTab.id, 'listening options updated', (response) => { - infoLog('voice dim', { response }); + infoLog(tag, { response }); }); }); } function onActivationPhraseChange() { - infoLog('voice dim', 'updating activation phrase'); + infoLog(tag, 'updating activation phrase'); const activationPhrase = document.getElementById('activationPhrase'); const listeningToggle = document.getElementById('alwaysListeningToggle'); @@ -56,7 +58,7 @@ function onActivationPhraseChange() { } function onAlwaysListeningChange(listeningOptions: AlwaysListening) { - infoLog('voice dim', 'updating alwaysListening'); + infoLog(tag, 'updating alwaysListening'); updateSaveText(true, 'Saved!'); setTimeout(() => updateSaveText(false), 3000); @@ -115,7 +117,7 @@ function downloadLogsButtonClicked() { const dimTabs = tabs.filter((tab) => tab.url?.match(/destinyitemmanager\.com.*inventory/)); if (dimTabs && dimTabs[0].id) chrome.tabs.sendMessage(dimTabs[0].id, 'get logs', (response: { ack: string; logs: Log[] }) => { - infoLog('voice dim', { response }); + infoLog(tag, { response }); const { logs } = response; createDownloadableFile(transformLogs(logs)); @@ -156,7 +158,7 @@ window.onload = function () { const activationPhraseInput = document.getElementById('activationPhrase'); activationPhraseInput?.addEventListener('keydown', debounce(onActivationPhraseChange)); const commandInputs = document.querySelectorAll('.commands input'); - infoLog('voice dim', { commandInputs }); + infoLog(tag, { commandInputs }); commandInputs.forEach((input) => { input.addEventListener('keydown', debounce(onCommandChange)); }); diff --git a/src/ts/voiceDim.ts b/src/ts/voiceDim.ts index 0f0711e..6956b29 100644 --- a/src/ts/voiceDim.ts +++ b/src/ts/voiceDim.ts @@ -15,6 +15,7 @@ import { const annyang = require('annyang'); +const tag = 'main'; // Keyboard and Mouse Events const uiEvents = { singleClick: new MouseEvent('click', { @@ -192,12 +193,12 @@ async function parseSpeech(this: any, transcript: string) { const closestMatch = getClosestMatch(Object.keys(mappedCommands), query); if (!closestMatch) { - infoLog('voice dim', "Couldn't determine correct action"); + infoLog(tag, "Couldn't determine correct action"); return; } const closestAction = getClosestMatch(Object.keys(potentialActions), mappedCommands[closestMatch.match]); if (!closestAction) { - infoLog('voice dim', "Couldn't determine correct action"); + infoLog(tag, "Couldn't determine correct action"); return; } @@ -244,9 +245,9 @@ function getCurrentCharacterClass(): string { return ''; } async function handleItemMovement(query: string, action: string): Promise { - infoLog('voice dim', 'in handleItemMovement', { query, action }); + infoLog(tag, 'in handleItemMovement', { query, action }); const itemToMove = await getItemToMove(query); - debugLog('voice dim', { itemToMove }); + debugLog(tag, { itemToMove }); if (!itemToMove) { await clearSearchBar(); return; @@ -277,7 +278,7 @@ async function getItemToMove(query: string): Promise { const itemToGet = getClosestMatch(Object.keys(availableItems), splitQuery[0]); if (!itemToGet) return null; const fullName = availableItems[itemToGet.match].name; - debugLog('voice dim', { itemToGet }); + debugLog(tag, { itemToGet }); await populateSearchBar(`${perkQuery} name:"${fullName}"`.trim()); const visibleItems = getVisibleItems(); itemToMove = visibleItems[0]; @@ -344,7 +345,7 @@ function getPerkQuery(query: string) { } async function handleStartFarmingMode() { - infoLog('voice dim', 'Starting farming mode'); + infoLog(tag, 'Starting farming mode'); await openCurrentCharacterLoadoutMenu(); const farmingSpan = document.querySelector('.loadout-menu ul li span'); farmingSpan?.dispatchEvent(uiEvents.singleClick); @@ -375,7 +376,7 @@ async function openCurrentCharacterLoadoutMenu() { } async function handleEquipLoadout(loadoutName: string) { - infoLog('voice dim', 'Equipping loadout', loadoutName); + infoLog(tag, 'Equipping loadout', loadoutName); await openCurrentCharacterLoadoutMenu(); const availableLoadoutNames = getLoadoutNames(); const loadoutToEquip = getClosestMatch(availableLoadoutNames, loadoutName); @@ -444,18 +445,18 @@ function getClosestMatch(availableItems: string[], query: string): FuseMatch | n }; const fuse = new Fuse(availableItems, options); const result = fuse.search(query); - debugLog('voice dim', { result, query }); + debugLog(tag, { result, query }); if (isAcceptableResult(result)) { return { toReplace: query, match: result[0].item }; } - debugLog('voice dim', "Couldn't find a match. Trying to find match by splitting the current query."); + debugLog(tag, "Couldn't find a match. Trying to find match by splitting the current query."); const splitQuery = query.split(' '); for (const split of splitQuery) { const splitResult = fuse.search(split); - debugLog('voice dim', { splitResult, split }); + debugLog(tag, { splitResult, split }); return isAcceptableResult(splitResult) ? { toReplace: split, match: splitResult[0].item } : { toReplace: '', match: '' }; @@ -474,7 +475,7 @@ async function populateSearchBar(searchInput: string): Promise { const count = getVisibleItems().length; const newValue = `${searchBar.value} ${searchInput.trim()}`.trim(); searchBar.value = newValue; - infoLog('voice dim', 'Populating search bar with', searchBar.value); + infoLog(tag, 'Populating search bar with', searchBar.value); await simulateSearchInput(); await waitForSearchToUpdate(count); @@ -490,7 +491,7 @@ async function simulateSearchInput() { } async function clearSearchBar() { - infoLog('voice dim', 'Clearing search bar'); + infoLog(tag, 'Clearing search bar'); const clearButton = document.querySelector('.filter-bar-button[title^=Clear]'); const initialCount = getVisibleItems().length; let waitForUpdate = false; @@ -528,9 +529,9 @@ function updateMicIcon(newMode: string) { function initializeShortcutListening() { annyang.addCallback('result', (userSaid: string[]) => { - debugLog('shortcut', userSaid); + debugLog(`${tag} shortcut`, userSaid); const transcript = userSaid[0].trim().toLowerCase(); - infoLog('voice dim', 'Heard', transcript); + infoLog(tag, 'Heard', transcript); updateUiTranscript(transcript, true); parseSpeech(transcript); updateMicIcon('notListening'); @@ -544,7 +545,7 @@ function initializeShortcutListening() { function initializeAlwaysListening() { annyang.start({ autoRestart: listeningOptions.active, continuous: listeningOptions.active }); annyang.addCallback('result', (userSaid?: string[] | undefined) => { - debugLog('voice dim', { userSaid }); + debugLog(tag, { userSaid }); if (userSaid) { let actionPerformed = false; for (let said of userSaid) { @@ -558,7 +559,7 @@ function initializeAlwaysListening() { // include a space intentionally if (said.includes(`${phrase} `)) { const transcript = said.split(`${phrase} `)[1]; - infoLog('voice dim', 'Heard', transcript); + infoLog(tag, 'Heard', transcript); updateUiTranscript(transcript, true); parseSpeech(transcript); actionPerformed = true; @@ -573,7 +574,7 @@ function initializeAlwaysListening() { } chrome.runtime.onMessage.addListener(async function (request, sender, sendResponse) { - infoLog('voice dim', 'Message received', { request }); + infoLog(tag, 'Message received', { request }); if (request.dimShortcutPressed && !listeningOptions.active) { handleShortcutPress(); } @@ -584,11 +585,11 @@ chrome.runtime.onMessage.addListener(async function (request, sender, sendRespon await getAlwaysListeningOptions(); } if (request === 'not on inventory page') { - infoLog('voice dim', 'no longer on inventory page'); + infoLog(tag, 'no longer on inventory page'); stopVoiceDim(); } if (request === 'on inventory page') { - infoLog('voice dim', 'on inventory page'); + infoLog(tag, 'on inventory page'); init(); } if (request === 'get logs') { @@ -604,7 +605,7 @@ chrome.runtime.onMessage.addListener(async function (request, sender, sendRespon async function getCustomCommands() { const commands = await retrieve('commands', DEFAULT_COMMANDS); mappedCommands = reverseMapCustomCommands(commands); - infoLog('voice dim', { commands, mappedCommands }); + infoLog(tag, { commands, mappedCommands }); } function reverseMapCustomCommands(commands: Record) { @@ -620,7 +621,7 @@ function reverseMapCustomCommands(commands: Record) { async function getAlwaysListeningOptions() { listeningOptions = await retrieve('alwaysListening', DEFAULT_ALWAYS_LISTENING); - infoLog('voice dim', { listeningOptions }); + infoLog(tag, { listeningOptions }); startListening(); // annyang.debug(true); } @@ -678,7 +679,7 @@ async function getPerks() { 'https://raw.githubusercontent.com/DestinyItemManager/d2ai-module/master/voice-dim-valid-perks.json' ); knownPerks = await response.json(); - infoLog('voice dim', { knownPerks }); + infoLog(tag, { knownPerks }); } function init() { From 457ad23add5103f157c4f0ba3fc760492270f36c Mon Sep 17 00:00:00 2001 From: Matthew Sherrill Date: Tue, 23 Jul 2024 06:31:20 -0500 Subject: [PATCH 2/4] fix --- src/ts/voiceDim.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ts/voiceDim.ts b/src/ts/voiceDim.ts index 6956b29..0ceb3ec 100644 --- a/src/ts/voiceDim.ts +++ b/src/ts/voiceDim.ts @@ -347,7 +347,7 @@ function getPerkQuery(query: string) { async function handleStartFarmingMode() { infoLog(tag, 'Starting farming mode'); await openCurrentCharacterLoadoutMenu(); - const farmingSpan = document.querySelector('.loadout-menu ul li span'); + const farmingSpan = document.querySelector('[class^=LoadoutPopup] ul li span'); farmingSpan?.dispatchEvent(uiEvents.singleClick); } @@ -370,9 +370,9 @@ async function handleEquipMaxPower() { } async function openCurrentCharacterLoadoutMenu() { - const currentCharacter = document.querySelector('.character.current'); + const currentCharacter = document.querySelector('[class*=m_current]'); currentCharacter?.dispatchEvent(uiEvents.singleClick); - await waitForElementToDisplay('.loadout-menu'); + await waitForElementToDisplay('[class^=LoadoutPopup]'); } async function handleEquipLoadout(loadoutName: string) { @@ -380,13 +380,13 @@ async function handleEquipLoadout(loadoutName: string) { await openCurrentCharacterLoadoutMenu(); const availableLoadoutNames = getLoadoutNames(); const loadoutToEquip = getClosestMatch(availableLoadoutNames, loadoutName); - const loadoutToEquipSpan = document.querySelector(`.loadout-menu span[title="${loadoutToEquip?.match}"]`); + const loadoutToEquipSpan = document.querySelector(`[class^=LoadoutPopup] span[title="${loadoutToEquip?.match}"]`); loadoutToEquipSpan?.dispatchEvent(uiEvents.singleClick); } function getLoadoutNames(): string[] { const loadoutNames: string[] = []; - const loadoutSpans = document.querySelectorAll('.loadout-menu li > span[title]:first-child'); + const loadoutSpans = document.querySelectorAll('[class^=LoadoutPopup] li > span[title]:first-child'); loadoutSpans.forEach((span) => { if (span.textContent) loadoutNames.push(span.textContent); }); From 83a7040cc80ba90c5e6e06bb3abf66881d9ded75 Mon Sep 17 00:00:00 2001 From: Matthew Sherrill Date: Sat, 27 Jul 2024 16:33:06 -0500 Subject: [PATCH 3/4] get stuff to work with the normal app instead of just beta --- src/ts/voiceDim.ts | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/ts/voiceDim.ts b/src/ts/voiceDim.ts index 0ceb3ec..6de66ee 100644 --- a/src/ts/voiceDim.ts +++ b/src/ts/voiceDim.ts @@ -347,7 +347,14 @@ function getPerkQuery(query: string) { async function handleStartFarmingMode() { infoLog(tag, 'Starting farming mode'); await openCurrentCharacterLoadoutMenu(); - const farmingSpan = document.querySelector('[class^=LoadoutPopup] ul li span'); + const xpath = "//span[contains(text(),'Farming Mode')]"; + const farmingSpan = document.evaluate( + xpath, + document, + null, + XPathResult.FIRST_ORDERED_NODE_TYPE, + null + ).singleNodeValue; farmingSpan?.dispatchEvent(uiEvents.singleClick); } @@ -370,9 +377,16 @@ async function handleEquipMaxPower() { } async function openCurrentCharacterLoadoutMenu() { - const currentCharacter = document.querySelector('[class*=m_current]'); + let currentCharacter; + const isBeta = window.location.hostname.startsWith('beta'); + + if (isBeta) { + currentCharacter = document.querySelector('[class*=m_current]'); + } else { + currentCharacter = document.querySelector("[style*='f2b4c4b2a13d732f7c54008169ecc62b.jpg']"); // indicator in top left of current character + } currentCharacter?.dispatchEvent(uiEvents.singleClick); - await waitForElementToDisplay('[class^=LoadoutPopup]'); + await waitForElementToDisplay('[placeholder*="Search loadout"]', 50, 5000); } async function handleEquipLoadout(loadoutName: string) { @@ -380,15 +394,20 @@ async function handleEquipLoadout(loadoutName: string) { await openCurrentCharacterLoadoutMenu(); const availableLoadoutNames = getLoadoutNames(); const loadoutToEquip = getClosestMatch(availableLoadoutNames, loadoutName); - const loadoutToEquipSpan = document.querySelector(`[class^=LoadoutPopup] span[title="${loadoutToEquip?.match}"]`); + const loadoutToEquipSpan = document.querySelector(`span[title="${loadoutToEquip?.match}"]`); loadoutToEquipSpan?.dispatchEvent(uiEvents.singleClick); } function getLoadoutNames(): string[] { const loadoutNames: string[] = []; - const loadoutSpans = document.querySelectorAll('[class^=LoadoutPopup] li > span[title]:first-child'); - loadoutSpans.forEach((span) => { - if (span.textContent) loadoutNames.push(span.textContent); + const editLoadoutButtons = document.querySelectorAll('[title="Edit Loadout"]'); + + editLoadoutButtons.forEach((button) => { + const prevSibling = button.previousSibling; + + if (prevSibling?.textContent) { + loadoutNames.push(prevSibling.textContent); + } }); return loadoutNames; } From 21c60c43f84e62b9c9524016aab938c86a766378 Mon Sep 17 00:00:00 2001 From: Matthew Sherrill Date: Sat, 27 Jul 2024 20:16:21 -0500 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 5 +++++ package.json | 4 ++-- public/manifest.chrome.json | 2 +- public/manifest.firefox.json | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b1d08..d2d8c88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### 1.3.0 - 2024-07-27 - The One Where It Actually Works When Not Using the Beta + +- Fixed Farming mode +- Fixed basically anything that used the character menu when not using the Beta app + ### 1.2.7 - 2023-04-20 - The One About Troubleshooting (and Strand) - Add `Download Logs` button to the options page diff --git a/package.json b/package.json index 723425c..888f4cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "voice-dim", - "version": "1.2.7", + "version": "1.3.0", "description": "Perform common DIM actions by using speech recognition.", "main": "dist/chrome/js/voice-dim.js", "scripts": { @@ -46,4 +46,4 @@ "webpack-cli": "^4.10.0", "webpack-visualizer-plugin2": "^1.0.0" } -} +} \ No newline at end of file diff --git a/public/manifest.chrome.json b/public/manifest.chrome.json index c8ac081..9ebab06 100644 --- a/public/manifest.chrome.json +++ b/public/manifest.chrome.json @@ -1,7 +1,7 @@ { "name": "Voice DIM", "description": "Control DIM with your voice.", - "version": "1.2.7", + "version": "1.3.0", "manifest_version": 3, "background": { "service_worker": "js/background.js" diff --git a/public/manifest.firefox.json b/public/manifest.firefox.json index 2cb2d89..3d4d0c1 100644 --- a/public/manifest.firefox.json +++ b/public/manifest.firefox.json @@ -1,7 +1,7 @@ { "name": "Voice DIM", "description": "Control DIM with your voice.", - "version": "1.2.7", + "version": "1.3.0", "manifest_version": 2, "background": { "scripts": ["js/background.js"]