NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @namespace https://tampermonkey.myso.kr/ // @name 스마트에디터ONE 보이스 입력기 // @description 네이버 블로그 스마트에디터ONE에서 마이크를 이용해 글을 쓸 수 있게 됩니다. // @copyright 2021, myso (https://tampermonkey.myso.kr) // @license Apache-2.0 // @version 1.0.5 // @updateURL https://github.com/myso-kr/kr.myso.tampermonkey/raw/master/service/com.naver.blog-write.input.voice.user.js // @downloadURL https://github.com/myso-kr/kr.myso.tampermonkey/raw/master/service/com.naver.blog-write.input.voice.user.js // @author Won Choi // @connect naver.com // @connect pstatic.net // @match *://blog.naver.com/*/postwrite* // @match *://blog.naver.com/*Redirect=Write* // @match *://blog.naver.com/*Redirect=Update* // @match *://blog.naver.com/PostWriteForm* // @match *://blog.naver.com/PostUpdateForm* // @match *://blog.editor.naver.com/editor* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/polyfill/Object.fromEntries.js // @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-app.js // @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-add-style.js // @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-add-script.js // @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/vendor/gm-xmlhttp-request-async.js // @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/donation.js // @require https://cdn.jsdelivr.net/npm/kr.myso.tampermonkey@1.0.25/assets/lib/naver-blog.js // @require https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuidv4.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.7.2/bluebird.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js // ==/UserScript== // ==OpenUserJS== // @author myso // ==/OpenUserJS== GM_App(async function main() { GM_donation('#viewTypeSelector, #postListBody, #wrap_blog_rabbit, #writeTopArea, #editor_frame', 0); GM_addStyle(` .se-utils > ul > li > button { margin-top: 14px !important; } .se-util-button-voice.se-util-button-active { border: 1px solid #0f0 !important; } .se-util-button-voice::before { display: inline-block; width: 37px; height: 37px; line-height: 40px; text-align: center; font-size: 16px; color: #666; content: '\\1F399\\FE0F' !important; } .se-utils-item-voice-loading .se-util-button-voice::before { animation: spin1 2s infinite linear; } .se-utils-item-voice[data-process-keyword-info]::after { display: none; position: absolute; z-index: -1; margin:auto; right: 20px; top: -240px; bottom: 0px; margin-bottom: 10px; padding: 15px; width: 300px; height: auto; overflow-y: auto; white-space: pre-line; border: 1px solid #ddd; border-radius: 8px; background-color: #fff; content: attr(data-process-keyword-info); line-height: 1.5rem; } .se-utils-item-voice[data-process-keyword-info]:hover::after { display: block; } `); const uri = new URL(location.href), params = Object.fromEntries(uri.searchParams.entries()); const user = await NB_blogInfo('', 'BlogUserInfo'); if(!user) return; const blog = await NB_blogInfo(user.userId, 'BlogInfo'); if(!blog) return; function toggle() { if(toggle.flag = !toggle.flag) { GM_addScript(async () => { const btn = document.querySelector('.se-util-button-voice'); const se = window.SmartEditor && await window.SmartEditor._editorPromise; if(!se) return; const recognition = window.__recognition || new webkitSpeechRecognition(); recognition.continuous = true; recognition.interimResults = true; recognition.lang="ko-KR"; recognition.start(); recognition.onresult = async (event) => { window.__transcript_reset = clearTimeout(window.__transcript_reset); window.__transcript_timer = clearTimeout(window.__transcript_timer); const transcript = Array.from(event.results).reduce((output, data) => { if(!data.isFinal) se.toastInfo(data[0]['transcript']); return (data.isFinal ? data[0]['transcript'].trim() : null) || output; }, null); if(transcript && window.__transcript_lasts !== transcript && (window.__transcript_lasts = transcript)) { window.__transcript_reset = setTimeout(()=>(recognition.stop(), btn && btn.classList.remove('se-util-button-active')), 30000); window.__transcript_timer = setTimeout(()=>se._editingService.lineBreak(), 3000); se._editingService.writeTextWithSoftLineBreak(`${window.__transcript_lasts} `); se._editingService.erase(); } }; recognition.onerror = async (event) => console.error(event); window.__recognition = recognition; if(btn) btn.classList.toggle('se-util-button-active', true); }); } else { GM_addScript(() => { const btn = document.querySelector('.se-util-button-voice'); const recognition = window.__recognition; if(recognition) recognition.stop(); if(btn) btn.classList.toggle('se-util-button-active', false); }); } return toggle.flag; } async function handler(e) { const mnu = document.querySelector('.se-ultils-list'); if(!mnu) return; const wrp = mnu.querySelector('.se-utils-item.se-utils-item-voice') || document.createElement('li'); wrp.classList.add('se-utils-item', 'se-utils-item-voice'); mnu.prepend(wrp); const btn = wrp.querySelector('button') || document.createElement('button'); btn.classList.add('se-util-button', 'se-util-button-voice'); btn.innerHTML = '<span class="se-utils-text">보이스 입력</span>'; wrp.append(btn); btn.onclick = GM_donationApp(()=>toggle()); } window.addEventListener('keyup', handler, false); window.addEventListener('keydown', handler, false); window.addEventListener('keypress', handler, false); handler(); });