[YouTube-Chat] Words Typing

try to take over the world!

当前为 2022-11-25 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         [YouTube-Chat] Words Typing
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  try to take over the world!
// @author       You
// @include        https://www.youtube.com/live_chat*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @license MIT
// @require https://greasyfork.org/scripts/455302-youtube-auto-add-text-in-chatbox/code/%5BYouTube%5D%20Auto%20add%20Text%20in%20ChatBox.js?version=1121194
// @grant        none
// ==/UserScript==

let chatTextBox
let wordArea
let setWords = ['test','sample','word']
let inputEvent
let separator = " "
let searchTime = new Date().getTime()
let roundTypeCounter = 0
let typeCounter = 0
let JpWords

/**
 *@Description 単語集をロードする。
*/

class loadJpWords {

	constructor(searchBox) {
		this.txtUrls = [
			"https://dl.dropboxusercontent.com/s/hme8y6jx87fe0ri/3letter.txt?dl=0",
			"https://dl.dropboxusercontent.com/s/tyd629mownzv009/4letter.txt?dl=0",
			"https://dl.dropboxusercontent.com/s/g5zuaz6wjj4itmf/5letter.txt?dl=0",
			"https://dl.dropboxusercontent.com/s/4xfr7p4un9r38eh/6letter.txt?dl=0"
		]
	}


	async loadWords(text){
	document.getElementById("top").insertAdjacentHTML("afterend",`
<div id="load-words">loading words...</div>
<style>
  #load-words{
      margin-left: 48px;
      white-space: nowrap;
  }
</style>`)
		//here our function should be implemented

		for(let i=0;i<this.txtUrls.length;i++){
			text = await fetch(this.txtUrls[i])
			text = await text.text()
			this[(i+3)+"words"] = text.split("\r\n")

		}

		htmlSetUp()
		return;
	}

}

JpWords = new loadJpWords()
JpWords.loadWords()

function htmlSetUp(){

	const shortcutKeys = `
Esc: Word skip
Tab: Switch Text Box
`
	document.getElementById("load-words").style.display = "none"
	document.getElementById("top").insertAdjacentHTML("afterend",`
<div id="minimum-dictionary">
  <div id="word-table">#<span id="wordarea">${setWords.join(separator).toLowerCase()}</span></div>
  <div id="word-tools">
    <input id="word-search-box" maxlength="6" autocomplete="off" value="" placeholder="[?] Search">
    <span><span id="word-match">0</span> word</span>
    <span id="shortcut-keys" title="${shortcutKeys}">⌨</span>
    <span><span id="typing-count">0</span> types</span>
    <span><span id="typing-speed">0.0</span> k/s</span>
  </div>
</div>
<style>
  #minimum-dictionary{
      margin-left: 48px;
      white-space: nowrap;
  }
  #word-search-box{
  background: rgb(0 0 0 / 10%);
      border: #000000 thin;
      border-top: none;
      outline: solid thin #ffffffa6;
      color: rgb(255 255 255 / 87%);
      width: 8rem;
  }
  #minimum-dictionary > div {
    margin-bottom: 1rem;
  }
  #word-tools > *{
    margin-right: 0.9rem;
  }
  #shortcut-keys:hover{
    text-decoration: underline;
    cursor: help;
  }

</style>`)


	chatTextBox = document.querySelector("#input > #input")
	wordArea = document.getElementById("wordarea")
    inputEvent = new typingCheck()

	chatTextBox.addEventListener("input", inputEvent.typeCheck.bind(inputEvent))
	chatTextBox.addEventListener("keydown", inputEvent.enterSubmitWord.bind(inputEvent))
	chatTextBox.addEventListener("focus", e => {
		if(!e.target.textContent){
			addText("#")
		}else{
			moveEndCaret(chat)
		}
	})

	document.getElementById("word-search-box").addEventListener("keydown",wordSearch)
	document.getElementById("word-search-box").addEventListener("focus",e => {
		e.target.value = ""
	})
	setInterval(updateTypingSpeed,1000)
}



class typingCheck {

	constructor() {
		this.typelog = "";
	}


    /**
     *@Description inputイベントで入力ワードを比較する。
    */
	typeCheck(event){

		const c = new RegExp(`^${chatTextBox.textContent.slice(1)}`,"i")
		const match = setWords[0] ? setWords[0].match(c) : ""
		const matchLength = (match ? match[0].length : 0)

		if(event.data && /insertCompositionText|insertText/.test(event.inputType) && this.typelog.length < event.target.textContent.length){
			document.getElementById("typing-count").textContent = ++typeCounter;
			updateTypingSpeed()
		}

		this.typelog = event.target.textContent || "";

		if(match){
			wordArea.textContent = (setWords[0].slice(matchLength) + separator + setWords.slice(1).join(separator)).toLowerCase()
        }else if(!chatTextBox.textContent){
            wordArea.textContent = setWords.join(separator).toLowerCase()
        }

	}



    /**
     *@Description チャットテキストボックスのkeydownイベント。

     *@note
     *Enterで送信時、入力したワードを評価。
     *Escでワードスキップ
	 *Tabでテキストボックスフォーカス切り替え
    */
	enterSubmitWord(event){

        if(event.key == "Enter"){

            if(setWords[0].toLowerCase() == chatTextBox.textContent.slice(1).toLowerCase()){
                setWords = setWords.slice(1)
				document.getElementById("word-match").textContent = setWords.length
            }

			if(chatTextBox.textContent != "#" && chatTextBox.textContent){
				document.getElementById("typing-count").textContent = ++typeCounter;
				updateTypingSpeed()
			}

            wordArea.textContent = setWords.join(separator).toLowerCase()

        }else if(event.key == "Escape"){

            setWords = setWords.slice(1)
			document.getElementById("word-match").textContent = setWords.length
            wordArea.textContent = setWords.join(separator).toLowerCase()

        }else if(event.key == "Tab"){

			document.getElementById("word-search-box").focus()
			event.preventDefault()

		}
	}

}




/**
 *@Description ?でワードを検索する。
*/
async function wordSearch(event){

	//検索ボックスにフォーカスしている状態でEnterを押した
	if(event.key == "Enter"){
		//Enterを押した検索ボックスの要素
		const searchBox = event.target

		if(/?/.test(searchBox.value)){
			separator = " "
			//文字列で正規表現を作成
			const RegText = `^${searchBox.value.replace(/[?]/g, "\\D")}$`
			//文字列を正規表現に変換
			const Reg = new RegExp(RegText ,"i")
			//正規表現にマッチする単語のみを絞り込む
			setWords = JpWords[`${searchBox.value.length}words`].filter(word => Reg.test(word)).slice(0,200);

		}else{
			separator = " "
			setWords = await new onelook(searchBox).getSearchWords()
		}


		//結果を出力
		wordArea.textContent = setWords.join(separator).toLowerCase()

		//結果件数を表示
		document.getElementById("word-match").textContent = setWords.length

		//チャットにフォーカス
		chatTextBox.focus()
		moveEndCaret(chat)

		//打鍵速度計測用時間・測定用打鍵数を設定
		searchTime = new Date().getTime()
		roundTypeCounter = new Number(typeCounter)
		document.getElementById("typing-speed").textContent = (0).toFixed(1)

	}else if(event.key == "Tab"){

		chatTextBox.focus()

		event.preventDefault()

	}

}



class onelook {

	constructor(searchBox) {
		this.result = [];
		this.searchBox = searchBox;
	}


	async getSearchWords(html){
		//here our function should be implemented

		for(let i=1;i<=101;i+=100){
			html = await fetch(`https://www.onelook.com//?w=${this.searchBox.value}&ssbp=1&first=${i}`)
			html = await html.text()

			//wordの前後のいちを取得
			const start = html.search(/<td width=20% valign=top>/)
			const end = html.search(/<\/TR><\/TABLE>/)


			//word要素のみを取り出す
			const wordsElement = html.slice(start,end)

			//取得したワードを配列に結合
			this.result = this.result.concat(wordsElement.match(/(?<=\>)[a-zA-Z]+(?=\<)/g))
		}

		return this.result;

	}

}



/**
 *@Description 打鍵速度を更新する
*/
function updateTypingSpeed(){

	document.getElementById("typing-speed").textContent = ((typeCounter - roundTypeCounter) / ((new Date().getTime()-searchTime)/1000)).toFixed(1)

}