(async _ => { if ( !/https?:\/\/manmanlai-online\.ru\/user\/control\/user\/update\/id\/\d+/ .test(window?.location?.href ?? ``) ) { return } function waitForElm(selector) { return new Promise((resolve, reject) => { if (!selector) { console.error("waitForElm called without selector") return reject("No selector provided") } if (document.querySelector(selector)) { return resolve(document.querySelector(selector)) } let observer = new MutationObserver( mutations => { if (document.querySelector(selector)) { observer.disconnect() resolve(document.querySelector(selector)) } } ) observer.observe( document.body, { childList: true, subtree: true } ) }) } class TalkMeButton { constructor() { this.isDisabled = false this.openUrl = this.openUrl.bind(this) this.button = document.createElement(`button`) Object.assign(this.button.style, { display: 'flex', alignItems: 'center', justifyContent: 'space-around', width: '100%', height: '80px', backgroundColor: 'lightseagreen', border: 'none', transition: 'background-color 0.3s' }) this.button.addEventListener('mouseenter', () => { if (!this.isDisabled) { this.button.style.cursor = 'pointer' this.button.style.backgroundColor = 'black' } else { this.button.style.cursor = 'not-allowed' } }) this.button.addEventListener('mouseleave', () => { if (!this.isDisabled) { this.button.style.backgroundColor = 'lightseagreen' } }) this.logo = document.createElement(`img`) this.logo.src = `https://fs.getcourse.ru/fileservice/file/download/a/555832/sc/314/h/ae9e28977fba3a2e8944e952b68acf4e.png` Object.assign(this.logo.style, { width: '4rem', height: '4rem' }) this.label = document.createElement(`span`) this.label.textContent = `Открыть чат в Talk-Me` Object.assign(this.label.style, { fontSize: '1rem', fontWeight: 'bold', padding: '0.25rem', whiteSpace: 'pre', color: 'black', backgroundColor: 'gold' }) this.button.appendChild(this.logo) this.button.appendChild(this.label) this.applyDisabledStyles() this.loadingSpinner = document.createElement(`div`) Object.assign(this.loadingSpinner.style, { position: 'relative', display: 'inline-block', boxSizing: 'border-box', width: '48px', height: '48px', border: '3px dotted #ffffff', borderStyle: 'solid solid dotted dotted', borderRadius: '50%' }) this.spinnerInner = document.createElement('div') Object.assign(this.spinnerInner.style, { position: 'absolute', top: '0', right: '0', bottom: '0', left: '0', boxSizing: 'border-box', width: '24px', height: '24px', margin: 'auto', border: '3px dotted #ff3d00', borderStyle: 'solid solid dotted', borderRadius: '50%' }) this.loadingSpinner.appendChild(this.spinnerInner) this.check().catch(err => console.error("Initial check failed:", err)) setInterval(_ => { this.check().catch(err => console.error("Interval check failed:", err)) }, 10000) } applyDisabledStyles() { this.isDisabled = true this.button.style.cursor = 'not-allowed' this.button.style.backgroundColor = 'grey' } applyEnabledStyles() { this.isDisabled = false this.button.style.cursor = 'pointer' this.button.style.backgroundColor = 'lightseagreen' } async check() { try { this.url = await this.getUrl(this.getUserQuery()) if (this.url != null) { this.enable() } else { this.disable() } } catch (err) { console.error("Error inside check():", err) this.disable() } } enable() { this.applyEnabledStyles() this.button.removeEventListener('click', this.openUrl) this.button.addEventListener('click', this.openUrl) } disable() { this.applyDisabledStyles() this.button.removeEventListener('click', this.openUrl) } startLoading() { if (this.button.contains(this.logo)) this.button.removeChild(this.logo) if (this.button.contains(this.label)) this.button.removeChild(this.label) this.button.appendChild(this.loadingSpinner) const outerAnim = this.loadingSpinner.animate( [ {transform: 'rotate(0deg)'}, {transform: 'rotate(360deg)'} ], { duration: 2000, iterations: Infinity, easing: 'linear' } ) const innerAnim = this.spinnerInner.animate( [ {transform: 'rotate(0deg)'}, {transform: 'rotate(-360deg)'} ], { duration: 1000, iterations: Infinity, easing: 'linear' } ) this.button.removeEventListener('click', this.openUrl) setTimeout(_ => { outerAnim.cancel() innerAnim.cancel() if (this.button.contains(this.loadingSpinner)) this.button.removeChild(this.loadingSpinner) this.button.appendChild(this.logo) this.button.appendChild(this.label) this.button.addEventListener('click', this.openUrl) }, 5000) } openUrl(e) { if (e) e.preventDefault() if (this.url == null) { console.error("Attempted to open URL, but this.url is null") this.disable() return } this.startLoading() window.open(this.url) } async getUrl(userQuery) { if (userQuery == null) { return null } const URL = "https://manmanlai-proxy.ru" try { let res res = await fetch( `${URL}/?mars=${atob(`d2VhcmVnb2luZ3RvbWFycw==`)}&url=${ encodeURIComponent( `https://lcab.talk-me.ru/json/v1.0/chat/client/search` ) }`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userQuery) } ) if (!res.ok) { console.error(`Fetch error: ${res.status} ${res.statusText}`) return null } res = await res.json() const client = res?.[`result`]?.clients?.find( client => client?.[`applicationLink`] != null ) if (!client) { console.warn("No client with applicationLink found in response") console.log(res) return null } return client[`applicationLink`] } catch (err) { console.error("Ошибка при получении запроса от Talk-Me:", err) return null } } getUserQuery() { let phone = document .querySelector('[name="User[phone]"]') ?.value let email = Array.from( document .querySelector(`.user-email`) ?.children ?? [] ) .find( child => /@/.test(child.textContent) ) ?.textContent ?.trim() if (phone != null) { return {phone} } if (email != null) { return {email} } console.error("Не удалось найти почту или номер телефона.") return null } } let talkMeBtn = new TalkMeButton() document.addEventListener( 'DOMContentLoaded', _ => { document .querySelector('body') ?.appendChild(talkMeBtn.button) } ) waitForElm('.user-info') .then(elm => { elm.appendChild(talkMeBtn.button) }) .catch(err => { console.error("Could not find element .user-info:", err) }) })()