Compare commits

...
8 Commits
10 changed files with 1456 additions and 1329 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+539 -439
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -8,8 +8,8 @@ import os
import re
import distutils.dir_util
VERSION_SHORT = "v.31f"
VERSION_FULL = VERSION_SHORT + ", released 20 November 2023"
VERSION_SHORT = "v.31g"
VERSION_FULL = VERSION_SHORT + ", released 25 December 2023"
EXEC_UGLIFYJS_WIN = "{2}/lib/uglifyjs.cmd --parse bare_returns --compress --output \"{1}\" \"{0}\""
EXEC_UGLIFYJS_AUTO = "uglifyjs --parse bare_returns --compress --output \"{1}\" \"{0}\""
+3 -1
View File
@@ -61,9 +61,11 @@ const onTrackingContinued = function(anyNewMessages) {
let action = null;
if (!DISCORD.hasMoreMessages()) {
console.debug("[DHT] Reached first message.");
action = SETTINGS.afterFirstMsg;
}
if (isNoAction(action) && !anyNewMessages) {
console.debug("[DHT] No new messages.");
action = SETTINGS.afterSavedMsg;
}
@@ -106,7 +108,7 @@ const onMessagesUpdated = async messages => {
isSending = true;
try {
await STATE.addDiscordChannel(info.server, info.channel);
STATE.addDiscordChannel(info.server, info.channel);
} catch (e) {
onError(e);
return;
+88 -64
View File
@@ -1,5 +1,23 @@
// noinspection JSUnresolvedVariable
// noinspection LocalVariableNamingConventionJS
class DISCORD {
// https://discord.com/developers/docs/resources/channel#channel-object-channel-types
static CHANNEL_TYPE = {
DM: 1,
GROUP_DM: 3,
ANNOUNCEMENT_THREAD: 10,
PUBLIC_THREAD: 11,
PRIVATE_THREAD: 12
};
// https://discord.com/developers/docs/resources/channel#message-object-message-types
static MESSAGE_TYPE = {
DEFAULT: 0,
REPLY: 19,
THREAD_STARTER: 21
};
static getMessageOuterElement() {
return DOM.queryReactClass("messagesWrapper");
}
@@ -28,46 +46,11 @@ class DISCORD {
* Calls the provided function with a list of messages whenever the currently loaded messages change.
*/
static setupMessageCallback(callback) {
let skipsLeft = 0;
let waitForCleanup = false;
const previousMessages = new Set();
const timer = window.setInterval(() => {
if (skipsLeft > 0) {
--skipsLeft;
return;
}
const view = this.getMessageOuterElement();
if (!view) {
skipsLeft = 2;
return;
}
const anyMessage = DOM.queryReactClass("message", this.getMessageOuterElement());
const messageCount = anyMessage ? anyMessage.parentElement.children.length : 0;
if (messageCount > 300) {
if (waitForCleanup) {
return;
}
skipsLeft = 3;
waitForCleanup = true;
window.setTimeout(() => {
const view = this.getMessageScrollerElement();
// noinspection JSUnusedGlobalSymbols
view.scrollTop = view.scrollHeight / 2;
}, 1);
}
else {
waitForCleanup = false;
}
const messages = this.getMessages();
const hasChanged = messages.some(message => !previousMessages.has(message.id)) || !this.hasMoreMessages();
const onMessageElementsChanged = function() {
const messages = DISCORD.getMessages();
const hasChanged = messages.some(message => !previousMessages.has(message.id)) || !DISCORD.hasMoreMessages();
if (!hasChanged) {
return;
@@ -79,24 +62,74 @@ class DISCORD {
}
callback(messages);
}, 200);
};
window.DHT_ON_UNLOAD.push(() => window.clearInterval(timer));
let debounceTimer;
/**
* Do not trigger the callback too often due to autoscrolling.
*/
const onMessageElementsChangedLater = function() {
window.clearTimeout(debounceTimer);
debounceTimer = window.setTimeout(onMessageElementsChanged, 100);
};
const observer = new MutationObserver(function () {
onMessageElementsChangedLater();
});
let skipsLeft = 0;
let observedElement = null;
const observerTimer = window.setInterval(() => {
if (skipsLeft > 0) {
--skipsLeft;
return;
}
const view = this.getMessageOuterElement();
if (!view) {
skipsLeft = 1;
return;
}
if (observedElement !== null && observedElement.isConnected) {
return;
}
observedElement = view.querySelector("[data-list-id='chat-messages']");
if (observedElement) {
console.debug("[DHT] Observed message container.");
observer.observe(observedElement, { childList: true });
onMessageElementsChangedLater();
}
}, 400);
window.DHT_ON_UNLOAD.push(() => {
observer.disconnect();
observedElement = null;
window.clearInterval(observerTimer);
});
}
/**
* Returns the property object of a message element.
* @returns { null | { message: DiscordMessage, channel: Object } }
* Returns the message from a message element.
* @returns { null | DiscordMessage } }
*/
static getMessageElementProps(ele) {
static getMessageFromElement(ele) {
const props = DOM.getReactProps(ele);
if (props.children && props.children.length) {
for (let i = 3; i < props.children.length; i++) {
const childProps = props.children[i].props;
if (props && Array.isArray(props.children)) {
for (const child of props.children) {
if (!(child instanceof Object)) {
continue;
}
if (childProps && "message" in childProps && "channel" in childProps) {
return childProps;
const childProps = child.props;
if (childProps instanceof Object && "message" in childProps) {
return childProps.message;
}
}
}
@@ -113,10 +146,10 @@ class DISCORD {
for (const ele of this.getMessageElements()) {
try {
const props = this.getMessageElementProps(ele);
const message = this.getMessageFromElement(ele);
if (props != null) {
messages.push(props.message);
if (message != null) {
messages.push(message);
}
} catch (e) {
console.error("[DHT] Error extracing message data, skipping it.", e, ele, DOM.tryGetReactProps(ele));
@@ -137,7 +170,7 @@ class DISCORD {
*/
static getSelectedChannel() {
try {
let obj;
let obj = null;
try {
for (const child of DOM.getReactProps(DOM.queryReactClass("chatContent")).children) {
@@ -148,15 +181,6 @@ class DISCORD {
}
} catch (e) {
console.error("[DHT] Error retrieving selected channel from 'chatContent' element.", e);
for (const ele of this.getMessageElements()) {
const props = this.getMessageElementProps(ele);
if (props != null) {
obj = props.channel;
break;
}
}
}
if (!obj || typeof obj.id !== "string") {
@@ -185,8 +209,8 @@ class DISCORD {
// https://discord.com/developers/docs/resources/channel#channel-object-channel-types
switch (obj.type) {
case 1: type = "DM"; break;
case 3: type = "GROUP"; break;
case DISCORD.CHANNEL_TYPE.DM: type = "DM"; break;
case DISCORD.CHANNEL_TYPE.GROUP_DM: type = "GROUP"; break;
default: return null;
}
@@ -224,7 +248,7 @@ class DISCORD {
}
};
if (obj.parent_id) {
if (obj.type === DISCORD.CHANNEL_TYPE.ANNOUNCEMENT_THREAD || obj.type === DISCORD.CHANNEL_TYPE.PUBLIC_THREAD || obj.type === DISCORD.CHANNEL_TYPE.PRIVATE_THREAD) {
channel["extra"]["parent"] = obj.parent_id;
}
else {
+2 -2
View File
@@ -204,12 +204,12 @@ ${btn("close", "X")}`);
<label><input id='dht-cfg-autoscroll' type='checkbox'> Autoscroll</label><br>
<br>
<label>After reaching the first message in channel...</label><br>
${radio("afm", "nothing", "Do Nothing")}
${radio("afm", "nothing", "Continue Tracking")}
${radio("afm", "pause", "Pause Tracking")}
${radio("afm", "switch", "Switch to Next Channel")}
<br>
<label>After reaching a previously saved message...</label><br>
${radio("asm", "nothing", "Do Nothing")}
${radio("asm", "nothing", "Continue Tracking")}
${radio("asm", "pause", "Pause Tracking")}
${radio("asm", "switch", "Switch to Next Channel")}
<p id='dht-cfg-note'>
+2 -4
View File
@@ -269,11 +269,9 @@ class SAVEFILE{
addMessagesFromDiscord(discordMessageArray){
var hasNewMessages = false;
for(var discordMessage of discordMessageArray){
var type = discordMessage.type;
// https://discord.com/developers/docs/resources/channel#message-object-message-reference-structure
if ((type === 0 || type === 19) && discordMessage.state === "SENT" && this.addMessage(discordMessage.channel_id, discordMessage.id, this.convertToMessageObject(discordMessage))){
for(var discordMessage of discordMessageArray){
if (this.addMessage(discordMessage.channel_id, discordMessage.id, this.convertToMessageObject(discordMessage))){
hasNewMessages = true;
}
}
+2 -1
View File
@@ -122,12 +122,13 @@ const STATE = (function() {
this._triggerStateChanged("data", "channel");
}
}
// Right. Upstream desktop `bootstrap.js` expects an `async` here. I think it's fine.
/*
* Adds all messages from the array to the specified channel. Returns true if the savefile was updated.
*/
addDiscordMessages(discordMessageArray){
discordMessageArray = discordMessageArray.filter(msg => (msg.type === DISCORD.MESSAGE_TYPE.DEFAULT || msg.type === DISCORD.MESSAGE_TYPE.REPLY || msg.type === DISCORD.MESSAGE_TYPE.THREAD_STARTER) && msg.state === "SENT");
if (this.getSavefile().addMessagesFromDiscord(discordMessageArray)){
this._triggerStateChanged("data", "messages");
return true;
+4 -2
View File
@@ -1,7 +1,8 @@
var DISCORD = (function(){
var REGEX = {
formatBold: /\*\*([\s\S]+?)\*\*(?!\*)/g,
formatItalic: /(.)?\*([\s\S]+?)\*(?!\*)/g,
formatItalic1: /\*([\s\S]+?)\*(?!\*)/g,
formatItalic2: /_([\s\S]+?)_(?!_)\b/g,
formatUnderline: /__([\s\S]+?)__(?!_)/g,
formatStrike: /~~([\s\S]+?)~~(?!~)/g,
formatCodeInline: /(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/g,
@@ -54,7 +55,8 @@ var DISCORD = (function(){
.replace(REGEX.specialEscapedSingle, escapeHtmlMatch)
.replace(REGEX.specialEscapedDouble, full => full.replace(/\\/g, "").replace(/(.)/g, escapeHtmlMatch))
.replace(REGEX.formatBold, "<b>$1</b>")
.replace(REGEX.formatItalic, (full, pre, match) => pre === '\\' ? full : (pre || "")+"<i>"+match+"</i>")
.replace(REGEX.formatItalic1, "<i>$1</i>")
.replace(REGEX.formatItalic2, "<i>$1</i>")
.replace(REGEX.formatUnderline, "<u>$1</u>")
.replace(REGEX.formatStrike, "<s>$1</s>");
}