From a7bb2e607df0fc7adef7e84ea94edeef7f15aa1a Mon Sep 17 00:00:00 2001 From: stkrknds Date: Tue, 1 Aug 2023 10:59:15 +0300 Subject: [PATCH 01/10] fix notifications workaround --- source/browser/conversation-list.ts | 52 ++++++++++++++++++----------- source/browser/selectors.ts | 2 +- source/index.ts | 5 +++ 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index 935ed612a..5bed79f4f 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -169,38 +169,50 @@ export async function sendConversationList(): Promise { ipc.callMain('conversations', conversationsToRender); } +function genStringFromNode(element: Element): string { + const cloneElement = element.cloneNode(true) as Element; + let emojiString; + + const images = cloneElement.querySelectorAll('img'); + for (const image of images) { + emojiString = image.alt; + // Replace facebook's thumbs up with emoji + if (emojiString === '(Y)') { + emojiString = '👍'; + } + + if (image.parentNode) { + cloneElement.replaceChild(document.createTextNode(emojiString), image.parentNode); + } + } + + return cloneElement.textContent ?? ''; +} + function countUnread(mutationsList: MutationRecord[]): void { // Look through the mutations for the one with the unread dot - const unreadMutations = mutationsList.filter(mutation => mutation.type === 'childList' && mutation.addedNodes.length > 0 && ((mutation.addedNodes[0] as Element).getAttribute('aria-label') === 'Mark as read')); + const unreadMutations = mutationsList.filter(mutation => + mutation.type === 'childList' + && mutation.addedNodes.length > 0 + && ((mutation.addedNodes[0] as Element).className === 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz') + && ((mutation.addedNodes[0] as Element).parentElement?.className === 'x6s0dn4 x78zum5 xozqiw3') + && !((mutation.addedNodes[0] as Element).previousElementSibling?.className === 'x1fsd2vl')); for (const mutation of unreadMutations) { let curr = (mutation.addedNodes[0] as Element).parentElement; // Find the parent element with the message preview and sender - while (curr?.className !== 'rq0escxv l9j0dhe7 du4w35lb j83agx80 pfnyh3mw i1fnvgqd bp9cbjyn owycx6da btwxx1t3') { + while (curr?.className !== 'x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np') { curr = curr?.parentElement ?? null; } // Get the image data URI from the parent of the author/text - const imgUrl = curr.parentElement?.getElementsByTagName('img')[0].dataset.caprineIcon; + const imgUrl = curr.querySelector('img')?.dataset.caprineIcon; // Get the author and text of the new message - // const titleText = curr.querySelectorAll('.d2edcug0.hpfvmrgz.qv66sw1b.c1et5uql.b0tq1wua.jq4qci2q.a3bd9o3v.lrazzd5p.oo9gr5id')[0].textContent; - let titleTextOptions = curr.querySelectorAll('.a8c37x1j.d2edcug0.ni8dbmo4.ltmttdrg.g0qnabr5'); - if (titleTextOptions.length === 0) { - titleTextOptions = curr.querySelectorAll('.a8c37x1j.ni8dbmo4.stjgntxs.l9j0dhe7.ltmttdrg.g0qnabr5'); - } - - const titleText = titleTextOptions[0].textContent; - let bodyTextOptions = curr.querySelectorAll('.a8c37x1j.ni8dbmo4.stjgntxs.l9j0dhe7.ltmttdrg.g0qnabr5'); - if (bodyTextOptions.length === 0) { - bodyTextOptions = curr.querySelectorAll('.a8c37x1j.d2edcug0.ni8dbmo4.ltmttdrg.g0qnabr5'); - } - - let loc = 0; - if (bodyTextOptions.length >= 2) { - loc = 1; - } + const titleTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x6prxxf.x1s688f.xzsf02u.x1yc453h.x4zkp8e.x41vudc.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft'); + const bodyTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x1nxh6w3.x1xlr1w8.xzsf02u.x4zkp8e.x676frb.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft'); - const bodyText = bodyTextOptions[loc].textContent; + const titleText = (titleTextOptions) ? genStringFromNode(titleTextOptions) : ''; + const bodyText = (bodyTextOptions) ? genStringFromNode(bodyTextOptions) : ''; // Send a notification ipc.callMain('notification', { diff --git a/source/browser/selectors.ts b/source/browser/selectors.ts index ebee9ab1f..7e8534e3c 100644 --- a/source/browser/selectors.ts +++ b/source/browser/selectors.ts @@ -1,7 +1,7 @@ export default { leftSidebar: '[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 xdt5ytf x2lah0s x193iq5w xeuugli xycxndf xexx8yu x18d9i69 xkhd6sd x4uap5"]', // ! Tray icon dependency chatsIcon: '[class="x1i10hfl xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx x2lwn1j xeuugli x4uap5 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x87ps6o x1lku1pv x1a2a7pz x6s0dn4 x1q0g3np xn3w4p2 x1nn3v0j x1120s5i x1av1boa x1lq5wgf xgqcy7u x30kzoy x9jhf4c xdj266r x11i5rnm xat24cr x1mh8g0r x78zum5"]', // ! Tray icon dependency - conversationList: '[role=navigation] [role=grid]', + conversationList: '[role=navigation] [role=grid] [class="x1n2onr6"]', conversationSelector: '[role=main] [role=grid]', notificationCheckbox: '._374b:nth-of-type(4) ._4ng2 input', rightSidebarButtons: '.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.g5gj957u.f4tghd1a.ifue306u.kuivcneq.t63ysoy8 [role=button]', diff --git a/source/index.ts b/source/index.ts index 1332c1d19..b0ed96a8b 100644 --- a/source/index.ts +++ b/source/index.ts @@ -614,6 +614,11 @@ const notifications = new Map(); ipc.answerRenderer( 'notification', ({id, title, body, icon, silent}: {id: number; title: string; body: string; icon: string; silent: boolean}) => { + // Don't send notifications when the window is focused + if (mainWindow.isFocused()) { + return; + } + const notification = new Notification({ title, body: config.get('notificationMessagePreview') ? body : 'You have a new message', From e5a63cdacdc87b6dff0d500212394d855ae51ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Simi=C4=87?= Date: Sun, 3 Sep 2023 10:05:53 +0200 Subject: [PATCH 02/10] Handle lowercase `(y)` for thumbs up --- source/browser/conversation-list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index 5bed79f4f..11c7b4fe5 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -177,7 +177,7 @@ function genStringFromNode(element: Element): string { for (const image of images) { emojiString = image.alt; // Replace facebook's thumbs up with emoji - if (emojiString === '(Y)') { + if (emojiString === '(Y)' || emojiString === '(y)') { emojiString = '👍'; } From 5903a2922ecb0e90c57c7e8ac52772135047341d Mon Sep 17 00:00:00 2001 From: stkrknds Date: Fri, 8 Sep 2023 16:29:08 +0300 Subject: [PATCH 03/10] send notification for every new msg --- source/browser/conversation-list.ts | 61 +++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index 11c7b4fe5..c086bb27e 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -181,35 +181,63 @@ function genStringFromNode(element: Element): string { emojiString = '👍'; } - if (image.parentNode) { - cloneElement.replaceChild(document.createTextNode(emojiString), image.parentNode); - } + image.parentElement?.replaceWith(document.createTextNode(emojiString)); } return cloneElement.textContent ?? ''; } function countUnread(mutationsList: MutationRecord[]): void { + const alreadyChecked: any = []; // Look through the mutations for the one with the unread dot const unreadMutations = mutationsList.filter(mutation => - mutation.type === 'childList' - && mutation.addedNodes.length > 0 - && ((mutation.addedNodes[0] as Element).className === 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz') - && ((mutation.addedNodes[0] as Element).parentElement?.className === 'x6s0dn4 x78zum5 xozqiw3') - && !((mutation.addedNodes[0] as Element).previousElementSibling?.className === 'x1fsd2vl')); - - for (const mutation of unreadMutations) { - let curr = (mutation.addedNodes[0] as Element).parentElement; - // Find the parent element with the message preview and sender - while (curr?.className !== 'x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np') { - curr = curr?.parentElement ?? null; + // When a conversations "becomes unread". + // TODO: Might not be needed. + ( + mutation.type === 'childList' + && mutation.addedNodes.length > 0 + && ((mutation.addedNodes[0] as Element).className === 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz') + && ((mutation.addedNodes[0] as Element).parentElement?.className === 'x6s0dn4 x78zum5 xozqiw3') + && !((mutation.addedNodes[0] as Element).previousElementSibling?.className === 'x1fsd2vl') + ) + // When text is received + || ( + mutation.type === 'characterData' + // Make sure the text corresponds to a conversation + && mutation.target.parentElement?.className === 'x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft' + && mutation.target.parentElement?.parentElement?.parentElement?.className === 'x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + ) + // When an emoji is received, node(s) are added + || (mutation.type === 'childList' + // There is a case where in the current mutation nodes are only removed and in a later one, new ones are added. + // By using this condition we ensure that this is the mutation where nodes are added + && mutation.addedNodes.length > 0 + // Make sure the mutation corresponds to a conversation + && (mutation.target as Element).className === 'x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft' + && mutation.target.parentElement?.parentElement?.className === 'x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy')); + + // Check latest mutation first + for (const mutation of unreadMutations.reverse()) { + // This probably can be done better + const curr = (mutation.type === 'childList') ? (mutation.target as Element).closest('[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]')! + : (mutation.target.parentElement as Element).closest('[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]')!; + + const href = curr.closest('[role="link"]')?.getAttribute('href'); + + // It is possible to have multiple mutations for the same conversation, but we only want one notification. + // So if the current conversation has already been checked, continue. + // Additionally if the conversation is not unread, then also continue. + if (alreadyChecked.includes(href) || curr.querySelector('.x1i10hfl.x1qjc9v5.xjbqb8w.xjqpnuy.xa49m3k.xqeqjp1.x2hbi6w.x13fuv20.xu3j5b3.x1q0q8m5.x26u7qi.x972fbf.xcfux6l.x1qhh985.xm0m39n.x9f619.x1ypdohk.xdl72j9.x2lah0s.xe8uvvx.xdj266r.x11i5rnm.xat24cr.x1mh8g0r.x2lwn1j.xeuugli.xexx8yu.x4uap5.x18d9i69.xkhd6sd.x1n2onr6.x16tdsg8.x1hl2dhg.xggy1nq.x1ja2u2z.x1t137rt.x1o1ewxj.x3x9cwd.x1e5q0jg.x13rtm0m.x1q0g3np.x87ps6o.x1lku1pv.x78zum5.x1a2a7pz')) { + continue; } + alreadyChecked.push(href); + // Get the image data URI from the parent of the author/text const imgUrl = curr.querySelector('img')?.dataset.caprineIcon; // Get the author and text of the new message - const titleTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x6prxxf.x1s688f.xzsf02u.x1yc453h.x4zkp8e.x41vudc.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft'); - const bodyTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x1nxh6w3.x1xlr1w8.xzsf02u.x4zkp8e.x676frb.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft'); + const titleTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x6prxxf.x1s688f.xzsf02u.x1yc453h.x4zkp8e.x41vudc.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft') ?? curr.querySelector('[class="x1lliihq x1plvlek xryxfnj x1n2onr6 x193iq5w xeuugli x13faqbe x1vvkbs x1s928wv xhkezso x1gmr53x x1cpjm7i x1fgarty x1943h6x x10flsy6 x6prxxf xk50ysn xzsf02u x1yc453h x4zkp8e x41vudc xq9mrsl"]'); + const bodyTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x1nxh6w3.x1xlr1w8.xzsf02u.x4zkp8e.x676frb.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft') ?? curr.querySelector('[class="x1lliihq x1plvlek xryxfnj x1n2onr6 x193iq5w xeuugli x13faqbe x1vvkbs x1s928wv xhkezso x1gmr53x x1cpjm7i x1fgarty x1943h6x x10flsy6 x1nxh6w3 x1fcty0u xi81zsa x1tu3fi x3x7a5m xq9mrsl"]'); const titleText = (titleTextOptions) ? genStringFromNode(titleTextOptions) : ''; const bodyText = (bodyTextOptions) ? genStringFromNode(bodyTextOptions) : ''; @@ -251,6 +279,7 @@ window.addEventListener('load', async () => { }); conversationCountObserver.observe(sidebar, { + characterData: true, subtree: true, childList: true, attributes: true, From 0c4fc67e675f084129a95fa768c0c1916411c0fd Mon Sep 17 00:00:00 2001 From: stkrknds Date: Fri, 8 Sep 2023 16:39:56 +0300 Subject: [PATCH 04/10] fix indentation --- source/browser/conversation-list.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index c086bb27e..b7472ef92 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -208,13 +208,15 @@ function countUnread(mutationsList: MutationRecord[]): void { && mutation.target.parentElement?.parentElement?.parentElement?.className === 'x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' ) // When an emoji is received, node(s) are added - || (mutation.type === 'childList' + || ( + mutation.type === 'childList' // There is a case where in the current mutation nodes are only removed and in a later one, new ones are added. // By using this condition we ensure that this is the mutation where nodes are added && mutation.addedNodes.length > 0 // Make sure the mutation corresponds to a conversation && (mutation.target as Element).className === 'x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft' - && mutation.target.parentElement?.parentElement?.className === 'x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy')); + && mutation.target.parentElement?.parentElement?.className === 'x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + )); // Check latest mutation first for (const mutation of unreadMutations.reverse()) { From c03c5e5d85873d7136b7d10441587ea07721956a Mon Sep 17 00:00:00 2001 From: stkrknds Date: Fri, 15 Sep 2023 22:12:29 +0300 Subject: [PATCH 05/10] cleaning --- source/browser/conversation-list.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index b7472ef92..bfc7dbb68 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -191,17 +191,8 @@ function countUnread(mutationsList: MutationRecord[]): void { const alreadyChecked: any = []; // Look through the mutations for the one with the unread dot const unreadMutations = mutationsList.filter(mutation => - // When a conversations "becomes unread". - // TODO: Might not be needed. - ( - mutation.type === 'childList' - && mutation.addedNodes.length > 0 - && ((mutation.addedNodes[0] as Element).className === 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz') - && ((mutation.addedNodes[0] as Element).parentElement?.className === 'x6s0dn4 x78zum5 xozqiw3') - && !((mutation.addedNodes[0] as Element).previousElementSibling?.className === 'x1fsd2vl') - ) // When text is received - || ( + ( mutation.type === 'characterData' // Make sure the text corresponds to a conversation && mutation.target.parentElement?.className === 'x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft' @@ -220,16 +211,14 @@ function countUnread(mutationsList: MutationRecord[]): void { // Check latest mutation first for (const mutation of unreadMutations.reverse()) { - // This probably can be done better - const curr = (mutation.type === 'childList') ? (mutation.target as Element).closest('[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]')! - : (mutation.target.parentElement as Element).closest('[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]')!; + const curr = (mutation.target.parentElement as Element).closest('[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]')!; const href = curr.closest('[role="link"]')?.getAttribute('href'); // It is possible to have multiple mutations for the same conversation, but we only want one notification. // So if the current conversation has already been checked, continue. // Additionally if the conversation is not unread, then also continue. - if (alreadyChecked.includes(href) || curr.querySelector('.x1i10hfl.x1qjc9v5.xjbqb8w.xjqpnuy.xa49m3k.xqeqjp1.x2hbi6w.x13fuv20.xu3j5b3.x1q0q8m5.x26u7qi.x972fbf.xcfux6l.x1qhh985.xm0m39n.x9f619.x1ypdohk.xdl72j9.x2lah0s.xe8uvvx.xdj266r.x11i5rnm.xat24cr.x1mh8g0r.x2lwn1j.xeuugli.xexx8yu.x4uap5.x18d9i69.xkhd6sd.x1n2onr6.x16tdsg8.x1hl2dhg.xggy1nq.x1ja2u2z.x1t137rt.x1o1ewxj.x3x9cwd.x1e5q0jg.x13rtm0m.x1q0g3np.x87ps6o.x1lku1pv.x78zum5.x1a2a7pz')) { + if (alreadyChecked.includes(href) || !curr.querySelector('.x1i10hfl.x1qjc9v5.xjbqb8w.xjqpnuy.xa49m3k.xqeqjp1.x2hbi6w.x13fuv20.xu3j5b3.x1q0q8m5.x26u7qi.x972fbf.xcfux6l.x1qhh985.xm0m39n.x9f619.x1ypdohk.xdl72j9.x2lah0s.xe8uvvx.xdj266r.x11i5rnm.xat24cr.x1mh8g0r.x2lwn1j.xeuugli.xexx8yu.x4uap5.x18d9i69.xkhd6sd.x1n2onr6.x16tdsg8.x1hl2dhg.xggy1nq.x1ja2u2z.x1t137rt.x1o1ewxj.x3x9cwd.x1e5q0jg.x13rtm0m.x1q0g3np.x87ps6o.x1lku1pv.x78zum5.x1a2a7pz')) { continue; } @@ -237,9 +226,10 @@ function countUnread(mutationsList: MutationRecord[]): void { // Get the image data URI from the parent of the author/text const imgUrl = curr.querySelector('img')?.dataset.caprineIcon; + const textOptions = curr.querySelectorAll('[class="x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft"]'); // Get the author and text of the new message - const titleTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x6prxxf.x1s688f.xzsf02u.x1yc453h.x4zkp8e.x41vudc.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft') ?? curr.querySelector('[class="x1lliihq x1plvlek xryxfnj x1n2onr6 x193iq5w xeuugli x13faqbe x1vvkbs x1s928wv xhkezso x1gmr53x x1cpjm7i x1fgarty x1943h6x x10flsy6 x6prxxf xk50ysn xzsf02u x1yc453h x4zkp8e x41vudc xq9mrsl"]'); - const bodyTextOptions = curr.querySelector('.x1lliihq.x1plvlek.xryxfnj.x1n2onr6.x193iq5w.xeuugli.x13faqbe.x1vvkbs.x1s928wv.xhkezso.x1gmr53x.x1cpjm7i.x1fgarty.x1943h6x.x10flsy6.x1nxh6w3.x1xlr1w8.xzsf02u.x4zkp8e.x676frb.xq9mrsl .x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft') ?? curr.querySelector('[class="x1lliihq x1plvlek xryxfnj x1n2onr6 x193iq5w xeuugli x13faqbe x1vvkbs x1s928wv xhkezso x1gmr53x x1cpjm7i x1fgarty x1943h6x x10flsy6 x1nxh6w3 x1fcty0u xi81zsa x1tu3fi x3x7a5m xq9mrsl"]'); + const titleTextOptions = textOptions[0]; + const bodyTextOptions = textOptions[1]; const titleText = (titleTextOptions) ? genStringFromNode(titleTextOptions) : ''; const bodyText = (bodyTextOptions) ? genStringFromNode(bodyTextOptions) : ''; From 17224d646893b7a12c5b1f5e32fabcde1e5b3437 Mon Sep 17 00:00:00 2001 From: Stratos Karakondis Date: Sat, 16 Sep 2023 18:21:30 +0300 Subject: [PATCH 06/10] update class names --- source/browser/conversation-list.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index bfc7dbb68..6dfef7e68 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -195,8 +195,7 @@ function countUnread(mutationsList: MutationRecord[]): void { ( mutation.type === 'characterData' // Make sure the text corresponds to a conversation - && mutation.target.parentElement?.className === 'x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft' - && mutation.target.parentElement?.parentElement?.parentElement?.className === 'x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + && mutation.target.parentElement?.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' ) // When an emoji is received, node(s) are added || ( @@ -205,8 +204,7 @@ function countUnread(mutationsList: MutationRecord[]): void { // By using this condition we ensure that this is the mutation where nodes are added && mutation.addedNodes.length > 0 // Make sure the mutation corresponds to a conversation - && (mutation.target as Element).className === 'x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft' - && mutation.target.parentElement?.parentElement?.className === 'x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + && mutation.target.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' )); // Check latest mutation first From 4853d2877c4c61ec79ce531caefbcf10ddf528ae Mon Sep 17 00:00:00 2001 From: Stratos Karakondis Date: Sun, 17 Sep 2023 15:10:47 +0300 Subject: [PATCH 07/10] add emoji change case --- source/browser/conversation-list.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index 6dfef7e68..778cb26a8 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -205,6 +205,11 @@ function countUnread(mutationsList: MutationRecord[]): void { && mutation.addedNodes.length > 0 // Make sure the mutation corresponds to a conversation && mutation.target.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + ) + // Emoji change + || ( + mutation.type === 'attributes' + && mutation.target.parentElement?.parentElement?.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' )); // Check latest mutation first @@ -273,7 +278,7 @@ window.addEventListener('load', async () => { subtree: true, childList: true, attributes: true, - attributeFilter: ['class'], + attributeFilter: ['src', 'alt'], }); } From 6be8062068a2f0cc8cfa226c8e6a1c8c6f2ec9e0 Mon Sep 17 00:00:00 2001 From: Stratos Karakondis Date: Sun, 17 Sep 2023 22:37:09 +0300 Subject: [PATCH 08/10] re-add unread case --- source/browser/conversation-list.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index 778cb26a8..ecf67e1e4 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -191,8 +191,16 @@ function countUnread(mutationsList: MutationRecord[]): void { const alreadyChecked: any = []; // Look through the mutations for the one with the unread dot const unreadMutations = mutationsList.filter(mutation => - // When text is received + // When a conversations "becomes unread". ( + mutation.type === 'childList' + && mutation.addedNodes.length > 0 + && ((mutation.addedNodes[0] as Element).className === 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz') + && ((mutation.addedNodes[0] as Element).parentElement?.className === 'x6s0dn4 x78zum5 xozqiw3') + && !((mutation.addedNodes[0] as Element).previousElementSibling?.className === 'x1fsd2vl') + ) + // When text is received + || ( mutation.type === 'characterData' // Make sure the text corresponds to a conversation && mutation.target.parentElement?.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' From 0ca861a80c07872bcec883594ea2de13500bf644 Mon Sep 17 00:00:00 2001 From: Stratos Karakondis Date: Tue, 26 Sep 2023 14:33:58 +0300 Subject: [PATCH 09/10] cleaning --- source/browser/conversation-list.ts | 45 +++++++++++++++++------------ 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index ecf67e1e4..4f7830389 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -169,7 +169,7 @@ export async function sendConversationList(): Promise { ipc.callMain('conversations', conversationsToRender); } -function genStringFromNode(element: Element): string { +function genStringFromNode(element: Element): string | undefined { const cloneElement = element.cloneNode(true) as Element; let emojiString; @@ -184,52 +184,55 @@ function genStringFromNode(element: Element): string { image.parentElement?.replaceWith(document.createTextNode(emojiString)); } - return cloneElement.textContent ?? ''; + return cloneElement.textContent ?? undefined; } function countUnread(mutationsList: MutationRecord[]): void { const alreadyChecked: any = []; - // Look through the mutations for the one with the unread dot + + const unreadDot = 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz'; + // Selector for the parent element that has as a child the body text of the conversation + const conversationTextParent = 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy'; + // Generic selector for all the texts of a conversation + const conversationTextSelector = '[class="x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft"]'; + // Selector for the top level element of a single conversation(its children include every text of the conversation and also the conversation image) + const conversationSelector = '[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]'; + const unreadMutations = mutationsList.filter(mutation => // When a conversations "becomes unread". ( mutation.type === 'childList' && mutation.addedNodes.length > 0 - && ((mutation.addedNodes[0] as Element).className === 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz') - && ((mutation.addedNodes[0] as Element).parentElement?.className === 'x6s0dn4 x78zum5 xozqiw3') - && !((mutation.addedNodes[0] as Element).previousElementSibling?.className === 'x1fsd2vl') + && ((mutation.addedNodes[0] as Element).className === unreadDot) ) // When text is received || ( mutation.type === 'characterData' // Make sure the text corresponds to a conversation - && mutation.target.parentElement?.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + && mutation.target.parentElement?.parentElement?.parentElement?.className === conversationTextParent ) // When an emoji is received, node(s) are added || ( mutation.type === 'childList' - // There is a case where in the current mutation nodes are only removed and in a later one, new ones are added. - // By using this condition we ensure that this is the mutation where nodes are added - && mutation.addedNodes.length > 0 // Make sure the mutation corresponds to a conversation - && mutation.target.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + && mutation.target.parentElement?.parentElement?.className === conversationTextParent ) // Emoji change || ( mutation.type === 'attributes' - && mutation.target.parentElement?.parentElement?.parentElement?.parentElement?.className === 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy' + && mutation.target.parentElement?.parentElement?.parentElement?.parentElement?.className === conversationTextParent )); // Check latest mutation first for (const mutation of unreadMutations.reverse()) { - const curr = (mutation.target.parentElement as Element).closest('[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]')!; + const curr = (mutation.target.parentElement as Element).closest(conversationSelector)!; const href = curr.closest('[role="link"]')?.getAttribute('href'); // It is possible to have multiple mutations for the same conversation, but we only want one notification. // So if the current conversation has already been checked, continue. // Additionally if the conversation is not unread, then also continue. - if (alreadyChecked.includes(href) || !curr.querySelector('.x1i10hfl.x1qjc9v5.xjbqb8w.xjqpnuy.xa49m3k.xqeqjp1.x2hbi6w.x13fuv20.xu3j5b3.x1q0q8m5.x26u7qi.x972fbf.xcfux6l.x1qhh985.xm0m39n.x9f619.x1ypdohk.xdl72j9.x2lah0s.xe8uvvx.xdj266r.x11i5rnm.xat24cr.x1mh8g0r.x2lwn1j.xeuugli.xexx8yu.x4uap5.x18d9i69.xkhd6sd.x1n2onr6.x16tdsg8.x1hl2dhg.xggy1nq.x1ja2u2z.x1t137rt.x1o1ewxj.x3x9cwd.x1e5q0jg.x13rtm0m.x1q0g3np.x87ps6o.x1lku1pv.x78zum5.x1a2a7pz')) { + if (alreadyChecked.includes(href) || !curr.querySelector('.' + unreadDot.replace(/ /g, '.'))) { continue; } @@ -237,13 +240,17 @@ function countUnread(mutationsList: MutationRecord[]): void { // Get the image data URI from the parent of the author/text const imgUrl = curr.querySelector('img')?.dataset.caprineIcon; - const textOptions = curr.querySelectorAll('[class="x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft"]'); + const textOptions = curr.querySelectorAll(conversationTextSelector); // Get the author and text of the new message - const titleTextOptions = textOptions[0]; - const bodyTextOptions = textOptions[1]; + const titleTextNode = textOptions[0]; + const bodyTextNode = textOptions[1]; - const titleText = (titleTextOptions) ? genStringFromNode(titleTextOptions) : ''; - const bodyText = (bodyTextOptions) ? genStringFromNode(bodyTextOptions) : ''; + const titleText = genStringFromNode(titleTextNode); + const bodyText = genStringFromNode(bodyTextNode); + + if (!bodyText || !titleText || !imgUrl) { + continue; + } // Send a notification ipc.callMain('notification', { From ac933e4d9f4c9c95ea8c84e3d344ae1be0bdf61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Simi=C4=87?= Date: Tue, 26 Sep 2023 17:02:22 +0200 Subject: [PATCH 10/10] Move selectors to sparate file and remove any type --- source/browser/conversation-list.ts | 28 ++++++++++++---------------- source/browser/selectors.ts | 4 ++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/source/browser/conversation-list.ts b/source/browser/conversation-list.ts index 4f7830389..755b75c32 100644 --- a/source/browser/conversation-list.ts +++ b/source/browser/conversation-list.ts @@ -188,51 +188,47 @@ function genStringFromNode(element: Element): string | undefined { } function countUnread(mutationsList: MutationRecord[]): void { - const alreadyChecked: any = []; - - const unreadDot = 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz'; - // Selector for the parent element that has as a child the body text of the conversation - const conversationTextParent = 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy'; - // Generic selector for all the texts of a conversation - const conversationTextSelector = '[class="x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft"]'; - // Selector for the top level element of a single conversation(its children include every text of the conversation and also the conversation image) - const conversationSelector = '[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]'; + const alreadyChecked: string[] = []; const unreadMutations = mutationsList.filter(mutation => // When a conversations "becomes unread". ( mutation.type === 'childList' && mutation.addedNodes.length > 0 - && ((mutation.addedNodes[0] as Element).className === unreadDot) + && ((mutation.addedNodes[0] as Element).className === selectors.conversationSidebarUnreadDot) ) // When text is received || ( mutation.type === 'characterData' // Make sure the text corresponds to a conversation - && mutation.target.parentElement?.parentElement?.parentElement?.className === conversationTextParent + && mutation.target.parentElement?.parentElement?.parentElement?.className === selectors.conversationSidebarTextParent ) // When an emoji is received, node(s) are added || ( mutation.type === 'childList' // Make sure the mutation corresponds to a conversation - && mutation.target.parentElement?.parentElement?.className === conversationTextParent + && mutation.target.parentElement?.parentElement?.className === selectors.conversationSidebarTextParent ) // Emoji change || ( mutation.type === 'attributes' - && mutation.target.parentElement?.parentElement?.parentElement?.parentElement?.className === conversationTextParent + && mutation.target.parentElement?.parentElement?.parentElement?.parentElement?.className === selectors.conversationSidebarTextParent )); // Check latest mutation first for (const mutation of unreadMutations.reverse()) { - const curr = (mutation.target.parentElement as Element).closest(conversationSelector)!; + const curr = (mutation.target.parentElement as Element).closest(selectors.conversationSidebarSelector)!; const href = curr.closest('[role="link"]')?.getAttribute('href'); + if (!href) { + continue; + } + // It is possible to have multiple mutations for the same conversation, but we only want one notification. // So if the current conversation has already been checked, continue. // Additionally if the conversation is not unread, then also continue. - if (alreadyChecked.includes(href) || !curr.querySelector('.' + unreadDot.replace(/ /g, '.'))) { + if (alreadyChecked.includes(href) || !curr.querySelector('.' + selectors.conversationSidebarUnreadDot.replace(/ /g, '.'))) { continue; } @@ -240,7 +236,7 @@ function countUnread(mutationsList: MutationRecord[]): void { // Get the image data URI from the parent of the author/text const imgUrl = curr.querySelector('img')?.dataset.caprineIcon; - const textOptions = curr.querySelectorAll(conversationTextSelector); + const textOptions = curr.querySelectorAll(selectors.conversationSidebarTextSelector); // Get the author and text of the new message const titleTextNode = textOptions[0]; const bodyTextNode = textOptions[1]; diff --git a/source/browser/selectors.ts b/source/browser/selectors.ts index 7e8534e3c..08ddd9761 100644 --- a/source/browser/selectors.ts +++ b/source/browser/selectors.ts @@ -3,6 +3,10 @@ export default { chatsIcon: '[class="x1i10hfl xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx x2lwn1j xeuugli x4uap5 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x87ps6o x1lku1pv x1a2a7pz x6s0dn4 x1q0g3np xn3w4p2 x1nn3v0j x1120s5i x1av1boa x1lq5wgf xgqcy7u x30kzoy x9jhf4c xdj266r x11i5rnm xat24cr x1mh8g0r x78zum5"]', // ! Tray icon dependency conversationList: '[role=navigation] [role=grid] [class="x1n2onr6"]', conversationSelector: '[role=main] [role=grid]', + conversationSidebarUnreadDot: 'x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx xdj266r x11i5rnm xat24cr x1mh8g0r x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1o1ewxj x3x9cwd x1e5q0jg x13rtm0m x1q0g3np x87ps6o x1lku1pv x78zum5 x1a2a7pz', + conversationSidebarTextParent: 'html-span xdj266r x11i5rnm xat24cr x1mh8g0r xexx8yu x18d9i69 xkhd6sd x1hl2dhg x16tdsg8 x1vvkbs x6s0dn4 x78zum5 x193iq5w xeuugli xg83lxy', // Parent element of the conversation text element (needed for notifications) + conversationSidebarTextSelector: '[class="x1lliihq x193iq5w x6ikm8r x10wlt62 xlyipyv xuxw1ft"]', // Generic selector for the text contents of all conversations + conversationSidebarSelector: '[class="x9f619 x1n2onr6 x1ja2u2z x78zum5 x2lah0s x1qughib x6s0dn4 xozqiw3 x1q0g3np"]', // Selector for the top level element of a single conversation (children contain text content of the conversation and conversation image) notificationCheckbox: '._374b:nth-of-type(4) ._4ng2 input', rightSidebarButtons: '.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.g5gj957u.f4tghd1a.ifue306u.kuivcneq.t63ysoy8 [role=button]', rightSidebarSegments: '.oajrlxb2.gs1a9yip.g5ia77u1.mtkw9kbi.tlpljxtp.qensuy8j.ppp5ayq2.goun2846.ccm00jje.s44p3ltw.mk2mc5f4.rt8b4zig.n8ej3o3l.agehan2d.sk4xxmp2.rq0escxv.nhd2j8a9.mg4g778l.pfnyh3mw.p7hjln8o.kvgmc6g5.cxmmr5t8.oygrvhab.hcukyx3x.tgvbjcpo.hpfvmrgz.jb3vyjys.rz4wbd8a.qt6c0cv9.a8nywdso.l9j0dhe7.i1ao9s8h.esuyzwwr.f1sip0of.du4w35lb.btwxx1t3.abiwlrkh.p8dawk7l.j83agx80.lzcic4wl.beltcj47.p86d2i9g.aot14ch1.kzx2olss',