使用 Greasemonkey 阅读 Twitch Whisper 消息
Reading Twitch Whisper Messages with Greasemonkey
我有一个 greasemonkey 脚本,它根据特定 twitch 频道上来自聊天机器人的耳语消息来调整 Twitch.tv 的某些元素。我的目标是观察耳语聊天,阅读每条新的耳语消息,相应地进行一些调整,然后关闭聊天 window,这样我就看不到我收到耳语了。
一般来说,我对 Greasemonkey、HMTL 或 Javascript 不是很熟悉。我知道这样做的唯一方法是在包含所有耳语 windows (conversation-manager
):
的 class 上使用 observeDOM
// Observe twitch whisper chat
var whisperArea = document.getElementsByClassName('conversations-manager')[0];
observeDOM( whisperArea ,function(){
NewWhisperMessage();
});
据我所知,观察确实有效。现在到没有的部分:我的方法 NewWhisperMessage
本身。
首先,我想描述一下我的第一种方法。一开始我试图找到所有新的耳语。当收到耳语时,耳语 window 打开。耳语 window 本身有多个消息行,如下所示:
<div class="ember-view conversation-chat-line incoming" title="Sat Jul 09 2016 11:52:38 GMT+0200" id="ember2564">
<span style="" class="from">NameOfMessageWriter</span>
<span class="colon">:</span>
<span style="" class="message">
Here is the message!
</span>
</div>
一般来说,此时我只取 class conversation-chat-line
的最后一个元素
var allChatMessages = document.getElementsByClassName("conversation-chat-line");
var newestChatMessage = allChatMessages[allChatMessages.length - 1];
这有时有效,但有点不可靠,因为有时似乎有多个消息到达一个DOM-update(这有意义吗?)。当我注意到这一点时,我想出了一个更复杂的想法,它工作得更可靠,但似乎仍然经常失败。
我的方法大致分为 4 个步骤:
1) 如果这是这个session的第一个新耳语(LastWhisperTitle
仍然是空的)那么必须找到一个起点。为此,我尝试在 twitch 聊天 window 中为新的耳语消息(class new-message-divider
找到分隔符元素。从那里我尝试在此之前获取聊天消息,并且存储 title
属性和此消息的消息文本以唯一标识消息(我不能使用 id
属性,因为它会在 whisper window 关闭和重新打开时发生变化)。步骤只做一次。
2) 现在我向后遍历所有聊天消息,直到到达由两个变量(标题和消息文本)标识的最后一条消息。要获取所有聊天消息,我使用 getElementsByClassName
。我存储这条最新消息的索引并从第 3 步开始。
3) 有了第一条新聊天消息的索引后,我开始向前迭代并按顺序解析每条消息。
4) 阅读新消息时关闭耳语window。 (条件是允许在没有新消息到达时手动打开耳语window)
这是最后的方法:
var LastWhisperTitle = "";
var LastWhisperMessage = "";
var ChannelName = "NameOfChatBot"
function NewWhisperMessage(){
if(LastWhisperTitle == "") {
// New Session: Find first new whisper
// Find new message divider
var newMessageDividerArray = document.getElementsByClassName("new-message-divider");
var newMessageDivider = newMessageDividerArray[newMessageDividerArray.length - 1];
// Find newest message already read
var sibling = newMessageDivider.previousSibling;
while(sibling) {
if(sibling.nodeType == 1) {
if(sibling.className != "undefined"){
if(sibling.className.indexOf("conversation-chat-line") > -1) {
break;
}
}
}
sibling = sibling.previousSibling;
}
if(!sibling){
return;
}
// Store this message as last read whisper
LastWhisperTitle = sibling.title;
LastWhisperMessage = sibling.getElementsByClassName("message")[0].textContent.trim();
}
// Get all messages
var whisperMessageArray = document.getElementsByClassName("conversation-chat-line");
var foundNewMessage = false;
// Find index in array of the first new message
var firstNewMessageIndex = 0;
for(i = whisperMessageArray.length - 1; i >= 0; i--){
var currentWhisper = whisperMessageArray[i];
var currentTitle = currentWhisper.title;
var currentMessage = currentWhisper.getElementsByClassName("message")[0].textContent.trim()
if(currentTitle == LastWhisperTitle && currentMessage == LastWhisperMessage){
// This message was already read -> the message with the previous index is new
if(i == whisperMessageArray.length - 1) {
// No new message
return;
} else {
firstNewMessageIndex = i+1;
break;
}
}
}
if(firstNewMessageIndex == 0){
// No new message
return;
}
// Parse all messages from index to newest message
for( i = firstNewMessageIndex; i < whisperMessageArray.length; i++){
var currentWhisper = whisperMessageArray[i];
var writer = currentWhisper.getElementsByClassName("from")[0].textContent;
var message = currentWhisper.getElementsByClassName("message")[0].textContent.trim();
if(writer == ChannelName){
ParseWhisperMessage(message);
foundNewMessage = true;
}
LastWhisperTitle = currentWhisper.title;
LastWhisperMessage = message;
}
if(foundNewMessage){
// Close Chat Window
CloseWhisperWindow();
}
}
我仍然遇到只有一些或 none 耳语消息被阅读的问题。我的方法可能有什么问题?
我好像找到问题了。我的方法本身基本有效,唯一的问题是当打开多个耳语对话时,所有问题都变得有问题,因为我在整个 document.getElementsByClassName("conversation-chat-line");
上使用 document
这也会从其他用户的对话中获取消息(不是只有抽搐机器人)。这弄乱了 LastWhisperTitle
和 LastWhisperMessage
.
的值
解决办法是把这段代码放在我函数的开头:
var conversationHeaderNames = document.getElementsByClassName('conversation-header-name');
var currentConversation;
// Find the correct conversation window
for(i = 0; i < conversationHeaderNames.length; i++){
if(conversationHeaderNames[i].innerHTML.trim() == ChannelName){
currentConversation = conversationHeaderNames[i].parentNode.parentNode;
break;
}
}
if(!currentConversation) return; // No conversation with chat bot
然后我只需要用 currentConversation
替换 document
就可以了。
我有一个 greasemonkey 脚本,它根据特定 twitch 频道上来自聊天机器人的耳语消息来调整 Twitch.tv 的某些元素。我的目标是观察耳语聊天,阅读每条新的耳语消息,相应地进行一些调整,然后关闭聊天 window,这样我就看不到我收到耳语了。
一般来说,我对 Greasemonkey、HMTL 或 Javascript 不是很熟悉。我知道这样做的唯一方法是在包含所有耳语 windows (conversation-manager
):
observeDOM
// Observe twitch whisper chat
var whisperArea = document.getElementsByClassName('conversations-manager')[0];
observeDOM( whisperArea ,function(){
NewWhisperMessage();
});
据我所知,观察确实有效。现在到没有的部分:我的方法 NewWhisperMessage
本身。
首先,我想描述一下我的第一种方法。一开始我试图找到所有新的耳语。当收到耳语时,耳语 window 打开。耳语 window 本身有多个消息行,如下所示:
<div class="ember-view conversation-chat-line incoming" title="Sat Jul 09 2016 11:52:38 GMT+0200" id="ember2564">
<span style="" class="from">NameOfMessageWriter</span>
<span class="colon">:</span>
<span style="" class="message">
Here is the message!
</span>
</div>
一般来说,此时我只取 class conversation-chat-line
的最后一个元素
var allChatMessages = document.getElementsByClassName("conversation-chat-line");
var newestChatMessage = allChatMessages[allChatMessages.length - 1];
这有时有效,但有点不可靠,因为有时似乎有多个消息到达一个DOM-update(这有意义吗?)。当我注意到这一点时,我想出了一个更复杂的想法,它工作得更可靠,但似乎仍然经常失败。
我的方法大致分为 4 个步骤:
1) 如果这是这个session的第一个新耳语(LastWhisperTitle
仍然是空的)那么必须找到一个起点。为此,我尝试在 twitch 聊天 window 中为新的耳语消息(class new-message-divider
找到分隔符元素。从那里我尝试在此之前获取聊天消息,并且存储 title
属性和此消息的消息文本以唯一标识消息(我不能使用 id
属性,因为它会在 whisper window 关闭和重新打开时发生变化)。步骤只做一次。
2) 现在我向后遍历所有聊天消息,直到到达由两个变量(标题和消息文本)标识的最后一条消息。要获取所有聊天消息,我使用 getElementsByClassName
。我存储这条最新消息的索引并从第 3 步开始。
3) 有了第一条新聊天消息的索引后,我开始向前迭代并按顺序解析每条消息。
4) 阅读新消息时关闭耳语window。 (条件是允许在没有新消息到达时手动打开耳语window)
这是最后的方法:
var LastWhisperTitle = "";
var LastWhisperMessage = "";
var ChannelName = "NameOfChatBot"
function NewWhisperMessage(){
if(LastWhisperTitle == "") {
// New Session: Find first new whisper
// Find new message divider
var newMessageDividerArray = document.getElementsByClassName("new-message-divider");
var newMessageDivider = newMessageDividerArray[newMessageDividerArray.length - 1];
// Find newest message already read
var sibling = newMessageDivider.previousSibling;
while(sibling) {
if(sibling.nodeType == 1) {
if(sibling.className != "undefined"){
if(sibling.className.indexOf("conversation-chat-line") > -1) {
break;
}
}
}
sibling = sibling.previousSibling;
}
if(!sibling){
return;
}
// Store this message as last read whisper
LastWhisperTitle = sibling.title;
LastWhisperMessage = sibling.getElementsByClassName("message")[0].textContent.trim();
}
// Get all messages
var whisperMessageArray = document.getElementsByClassName("conversation-chat-line");
var foundNewMessage = false;
// Find index in array of the first new message
var firstNewMessageIndex = 0;
for(i = whisperMessageArray.length - 1; i >= 0; i--){
var currentWhisper = whisperMessageArray[i];
var currentTitle = currentWhisper.title;
var currentMessage = currentWhisper.getElementsByClassName("message")[0].textContent.trim()
if(currentTitle == LastWhisperTitle && currentMessage == LastWhisperMessage){
// This message was already read -> the message with the previous index is new
if(i == whisperMessageArray.length - 1) {
// No new message
return;
} else {
firstNewMessageIndex = i+1;
break;
}
}
}
if(firstNewMessageIndex == 0){
// No new message
return;
}
// Parse all messages from index to newest message
for( i = firstNewMessageIndex; i < whisperMessageArray.length; i++){
var currentWhisper = whisperMessageArray[i];
var writer = currentWhisper.getElementsByClassName("from")[0].textContent;
var message = currentWhisper.getElementsByClassName("message")[0].textContent.trim();
if(writer == ChannelName){
ParseWhisperMessage(message);
foundNewMessage = true;
}
LastWhisperTitle = currentWhisper.title;
LastWhisperMessage = message;
}
if(foundNewMessage){
// Close Chat Window
CloseWhisperWindow();
}
}
我仍然遇到只有一些或 none 耳语消息被阅读的问题。我的方法可能有什么问题?
我好像找到问题了。我的方法本身基本有效,唯一的问题是当打开多个耳语对话时,所有问题都变得有问题,因为我在整个 document.getElementsByClassName("conversation-chat-line");
上使用 document
这也会从其他用户的对话中获取消息(不是只有抽搐机器人)。这弄乱了 LastWhisperTitle
和 LastWhisperMessage
.
解决办法是把这段代码放在我函数的开头:
var conversationHeaderNames = document.getElementsByClassName('conversation-header-name');
var currentConversation;
// Find the correct conversation window
for(i = 0; i < conversationHeaderNames.length; i++){
if(conversationHeaderNames[i].innerHTML.trim() == ChannelName){
currentConversation = conversationHeaderNames[i].parentNode.parentNode;
break;
}
}
if(!currentConversation) return; // No conversation with chat bot
然后我只需要用 currentConversation
替换 document
就可以了。