/
SentinelReporter.user.js
146 lines (123 loc) · 6.06 KB
/
SentinelReporter.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// ==UserScript==
// @name Sentinel Reporter
// @namespace https://github.com/SOBotics
// @version 1.5
// @description Quick feedback to Natty directly from Sentinel's post page
// @author Filnor
// @contributor geisterfurz007
// @contributor double-beep
// @include https://sentinel.erwaysoftware.com/*
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @downloadURL https://github.com/SOBotics/Userscripts/raw/master/Natty/SentinelReporter.user.js
// @updateURL https://github.com/SOBotics/Userscripts/raw/master/Natty/SentinelReporter.user.js
// ==/UserScript==
(function() {
function init() {
'use strict';
function getFeedbackButton(type, title, icon) {
const button = document.createElement('button');
button.classList.add('fb-button');
button.id = `feedback-${type}`
button.title = `${type} - ${title}`;
button.innerText = icon;
button.type = 'button';
return button;
}
const soboticsRoomId = 111347;
const seboticsRoomId = 54445;
const getFeedbackString = (postUrl, feedbackType) => `@Natty feedback ${postUrl} ${feedbackType}`;
const answerUrl = document.querySelector('h3 a').href;
const askubuntuRegex = /^https?:\/\/(www\.)?askubuntu/;
const isAskUbuntuPost = askubuntuRegex.test(answerUrl);
const chatHost = `chat.stack${isAskUbuntuPost ? 'exchange' : 'overflow'}.com`;
const roomId = isAskUbuntuPost ? seboticsRoomId : soboticsRoomId;
const postChatUrl = `https://${chatHost}/chats/${roomId}/messages/new`;
const chatRoomUrl = `https://${chatHost}/rooms/${roomId}`;
function handleEvent(text) {
const newline = document.createElement('br');
const eventSpan = document.querySelector('#sentinel-reporter-event-span');
eventSpan.append(text, newline);
eventSpan.parentElement.style.display = 'block';
}
function sendChatMessage(fkey, messageToSend) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'POST',
url: postChatUrl,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: `text=${encodeURIComponent(messageToSend)}&fkey=${fkey}`,
onload: function(response) {
if (response.status === 200) {
const parsedResponse = JSON.parse(response.responseText);
parsedResponse.id && parsedResponse.time
? resolve(true)
: reject('Failed to send chat message: conflict!'); // if both are null, it's a conflict
} else {
reject('Failed to send chat message: ' + response.status + '.' + response.responseText);
}
},
onerror: function(error) {
reject('Error while trying to send chat message: ' + error.status + '. See console for more details or retry.');
console.error(error.responseText);
}
});
});
}
function getFkeyFromChat() {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: chatRoomUrl,
onload: function (response) {
const domParser = new DOMParser();
const parsedHtml = domParser.parseFromString(response.responseText, 'text/html');
const fkey = parsedHtml.querySelector('#fkey').value;
resolve(fkey);
},
onerror: function(error) {
console.error(error);
reject(`Error ${error.status} while trying to fetch fkey from chat. See console for more details.`);
}
});
});
}
const feedbackWrapper = document.createElement('p');
feedbackWrapper.id = 'feedback-line';
const addFeedback = document.createElement('b');
addFeedback.innerText = 'Add feedback:';
const tpButton = getFeedbackButton('tp', 'true positive', '✔️');
const fpButton = getFeedbackButton('fp', 'false positive', '❌');
const neButton = getFeedbackButton('ne', 'needs editing', '✏️');
feedbackWrapper.append(addFeedback, ' ', tpButton, fpButton, neButton);
const eventHistoryHtml = document.createElement('div');
eventHistoryHtml.classList.add('alert', 'alert-info');
eventHistoryHtml.style.display = 'none';
const eventHistorySpan = document.createElement('span');
eventHistorySpan.id = 'sentinel-reporter-event-span';
eventHistoryHtml.appendChild(eventHistorySpan);
GM_addStyle('.fb-button { background: none; border: 0px solid black; }');
document.querySelector('h3').after(feedbackWrapper); // insert buttons
document.querySelector('#feedback-line').after(eventHistoryHtml);
document.querySelectorAll('.fb-button').forEach(button => {
button.addEventListener('click', async function() {
const feedbackType = button.id.split('-')[1];
try {
const chatFkey = await getFkeyFromChat();
const chatMessage = getFeedbackString(answerUrl, feedbackType);
await sendChatMessage(chatFkey, chatMessage);
} catch(error) {
handleEvent(`❌ ${error}`);
return;
}
handleEvent('✔️ Feedback sent.');
});
});
}
const postsRegex = /\/posts\/\d+/;
if (postsRegex.test(location.href)) init();
document.addEventListener('turbolinks:load', () => {
const newUrl = location.href;
if (postsRegex.test(newUrl)) init();
});
})();