1
0
mirror of https://github.com/chylex/Discord-History-Tracker.git synced 2024-11-25 22:42:51 +01:00

Compare commits

..

3 Commits

Author SHA1 Message Date
will-ca
72b23ee4f8
Merge 274f40636e into ce87901088 2023-11-23 06:50:04 +00:00
will-ca
274f40636e Remove superfluous comments. 2023-11-23 06:49:48 +00:00
will-ca
bc438753a3 Use code from the desktop app. 2023-11-23 06:27:02 +00:00
16 changed files with 4397 additions and 1470 deletions

View File

@ -31,7 +31,6 @@ After you've done changes to the source code, you will need to build it. Before
Now open the folder that contains `build.py` in a command line, and run `python build.py` to create a build with default settings. The following files will be created: Now open the folder that contains `build.py` in a command line, and run `python build.py` to create a build with default settings. The following files will be created:
* `bld/track.js` is the raw tracker script that can be pasted into a browser console * `bld/track.js` is the raw tracker script that can be pasted into a browser console
* `bld/track.html` is the tracker script but sanitized for inclusion in HTML (see `web/index.php` for examples)
* `bld/viewer.html` is the complete offline viewer * `bld/viewer.html` is the complete offline viewer
You can tweak the build process using the following flags: You can tweak the build process using the following flags:

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

101
build.py
View File

@ -11,8 +11,8 @@ import distutils.dir_util
VERSION_SHORT = "v.31f" VERSION_SHORT = "v.31f"
VERSION_FULL = VERSION_SHORT + ", released 20 November 2023" VERSION_FULL = VERSION_SHORT + ", released 20 November 2023"
EXEC_UGLIFYJS_WIN = "{2}/lib/uglifyjs.cmd --parse bare_returns --compress --output \"{1}\" \"{0}\"" EXEC_UGLIFYJS_WIN = "{2}/lib/uglifyjs.cmd --parse bare_returns --compress --mangle toplevel --mangle-props keep_quoted,reserved=[{3}] --output \"{1}\" \"{0}\""
EXEC_UGLIFYJS_AUTO = "uglifyjs --parse bare_returns --compress --output \"{1}\" \"{0}\"" EXEC_UGLIFYJS_AUTO = "uglifyjs --parse bare_returns --compress --mangle toplevel --mangle-props keep_quoted,reserved=[{3}] --output \"{1}\" \"{0}\""
USE_UGLIFYJS = "--nominify" not in sys.argv USE_UGLIFYJS = "--nominify" not in sys.argv
USE_MINIFICATION = "--nominify" not in sys.argv USE_MINIFICATION = "--nominify" not in sys.argv
@ -32,13 +32,17 @@ else:
USE_UGLIFYJS = False USE_UGLIFYJS = False
print("Could not find 'uglifyjs', JS minification will be disabled") print("Could not find 'uglifyjs', JS minification will be disabled")
if USE_UGLIFYJS:
with open("reserve.txt", "r") as reserved:
RESERVED_PROPS = ",".join(line.strip() for line in reserved.readlines())
# File Utilities # File Utilities
def combine_files(input_pattern, output_file): def combine_files(input_pattern, output_file):
is_first_file = True is_first_file = True
with fileinput.input(sorted(glob.glob(input_pattern))) as stream: with fileinput.input(sorted(glob.glob(input_pattern, recursive=True))) as stream:
for line in stream: for line in stream:
if stream.isfirstline(): if stream.isfirstline():
if is_first_file: if is_first_file:
@ -49,23 +53,6 @@ def combine_files(input_pattern, output_file):
output_file.write(line.replace("{{{version:full}}}", VERSION_FULL)) output_file.write(line.replace("{{{version:full}}}", VERSION_FULL))
def combine_files_to_str(input_pattern):
is_first_file = True
output = []
with fileinput.input(sorted(glob.glob(input_pattern))) as stream:
for line in stream:
if stream.isfirstline():
if is_first_file:
is_first_file = False
else:
output.append("\n")
output.append(line.replace("{{{version:full}}}", VERSION_FULL))
return "".join(output)
def minify_css(input_file, output_file): def minify_css(input_file, output_file):
if not USE_MINIFICATION: if not USE_MINIFICATION:
if input_file != output_file: if input_file != output_file:
@ -90,34 +77,24 @@ def minify_css(input_file, output_file):
# Build System # Build System
def build_tracker(): def build_tracker_html():
output_file_raw = "bld/track.js" output_file_raw = "bld/track.js"
output_file_html = "bld/track.html" output_file_html = "bld/track.html"
output_file_userscript = "bld/track.user.js"
with open("src/tracker/styles/controller.css", "r") as f: output_file_tmp = "bld/track.tmp.js"
controller_css = f.read() input_pattern = "src/tracker/**/*.js"
with open("src/tracker/styles/settings.css", "r") as f:
settings_css = f.read()
with open("src/tracker/bootstrap.js", "r") as f:
bootstrap_js = f.read()
combined_tracker_js = combine_files_to_str("src/tracker/scripts/*.js")
combined_tracker_js = combined_tracker_js.replace("/*[CSS-CONTROLLER]*/", controller_css)
combined_tracker_js = combined_tracker_js.replace("/*[CSS-SETTINGS]*/", settings_css)
full_tracker_js = bootstrap_js.replace("/*[IMPORTS]*/", combined_tracker_js)
minified_tracker_js = full_tracker_js
with open(output_file_raw, "w") as out: with open(output_file_raw, "w") as out:
out.write(full_tracker_js) if not USE_UGLIFYJS:
out.write("(function(){\n")
combine_files(input_pattern, out)
if not USE_UGLIFYJS:
out.write("})()")
if USE_UGLIFYJS: if USE_UGLIFYJS:
output_file_tmp = "bld/track.tmp.js" os.system(EXEC_UGLIFYJS.format(output_file_raw, output_file_tmp, WORKING_DIR, RESERVED_PROPS))
os.system(EXEC_UGLIFYJS.format(output_file_raw, output_file_tmp, WORKING_DIR))
with open(output_file_raw, "w") as out: with open(output_file_raw, "w") as out:
out.write("javascript:(function(){") out.write("javascript:(function(){")
@ -129,29 +106,26 @@ def build_tracker():
os.remove(output_file_tmp) os.remove(output_file_tmp)
with open(output_file_raw, "r") as raw: with open(output_file_raw, "r") as raw:
minified_tracker_js = raw.read() script_contents = raw.read().replace("&", "&amp;").replace('"', "&quot;").replace("'", "&#x27;").replace("<", "&lt;").replace(">", "&gt;")
write_tracker_html(output_file_html, minified_tracker_js) with open(output_file_html, "w") as out:
write_tracker_userscript(output_file_userscript, full_tracker_js) out.write(script_contents)
def write_tracker_html(output_file, tracker_js): def build_tracker_userscript():
tracker_js = tracker_js.replace("&", "&amp;").replace('"', "&quot;").replace("'", "&#x27;").replace("<", "&lt;").replace(">", "&gt;") output_file = "bld/track.user.js"
input_pattern = "src/tracker/**/*.js"
userscript_base = "src/base/track.user.js"
with open(userscript_base, "r") as base:
userscript_contents = base.read().replace("{{{version}}}", VERSION_SHORT).split("{{{contents}}}")
with open(output_file, "w") as out: with open(output_file, "w") as out:
out.write(tracker_js) out.write(userscript_contents[0])
combine_files(input_pattern, out)
out.write(userscript_contents[1])
def write_tracker_userscript(output_file, full_tracker_js):
with open("src/base/track.user.js", "r") as f:
userscript_js = f.read()
userscript_js = userscript_js.replace("{{{version}}}", VERSION_SHORT)
userscript_js = userscript_js.replace("{{{contents}}}", full_tracker_js)
with open(output_file, "w") as out:
out.write(userscript_js)
def build_viewer(): def build_viewer():
@ -176,7 +150,7 @@ def build_viewer():
combine_files(input_js_pattern, out) combine_files(input_js_pattern, out)
if USE_UGLIFYJS: if USE_UGLIFYJS:
os.system(EXEC_UGLIFYJS.format(tmp_js_file_combined, tmp_js_file_minified, WORKING_DIR)) os.system(EXEC_UGLIFYJS.format(tmp_js_file_combined, tmp_js_file_minified, WORKING_DIR, RESERVED_PROPS))
else: else:
shutil.copyfile(tmp_js_file_combined, tmp_js_file_minified) shutil.copyfile(tmp_js_file_combined, tmp_js_file_minified)
@ -228,8 +202,11 @@ def build_website():
os.makedirs("bld", exist_ok = True) os.makedirs("bld", exist_ok = True)
print("Building tracker...") print("Building tracker html...")
build_tracker() build_tracker_html()
print("Building tracker userscript...")
build_tracker_userscript()
print("Building viewer...") print("Building viewer...")
build_viewer() build_viewer()

74
reserve.txt Normal file
View File

@ -0,0 +1,74 @@
autoscroll
_autoscroll
afterFirstMsg
_afterFirstMsg
afterSavedMsg
_afterSavedMsg
enableImagePreviews
_enableImagePreviews
enableFormatting
_enableFormatting
enableAnimatedEmoji
_enableAnimatedEmoji
enableUserAvatars
_enableUserAvatars
DHT_LOADED
DHT_EMBEDDED
meta
data
users
userindex
servers
channels
u
t
m
f
e
a
t
te
d
r
re
c
n
an
tag
avatar
author
type
state
name
position
topic
nsfw
id
username
bot
discriminator
timestamp
content
editedTimestamp
mentions
embeds
attachments
title
description
reply
reactions
emoji
count
animated
ext
toDate
memoizedProps
props
children
channel
messages
msSaveBlob
messageReference
message_id
guild_id
guild

View File

@ -0,0 +1,5 @@
**STOP!**
These files must be kept in sync with the upstream desktop app branch in order for future changes/fixes to the desktop script to be cleanly merged here.
Changes for the browser version should be done in another file to maintain mergeablity.

View File

@ -1,3 +1,6 @@
// NOTE: Currently unused. See `.createStyle()` calls in `gui.js`.
/*
const css_controller = `
#app-mount { #app-mount {
height: calc(100% - 48px) !important; height: calc(100% - 48px) !important;
} }
@ -30,8 +33,5 @@
display: inline-block; display: inline-block;
margin: 14px 12px; margin: 14px 12px;
} }
`
#dht-ctrl-close { */
margin: 8px 8px 8px 0 !important;
float: right;
}

View File

@ -1,3 +1,6 @@
// NOTE: Currently unused. See `.createStyle()` calls in `gui.js`.
/*
const css_settings = `
#dht-cfg-overlay { #dht-cfg-overlay {
position: absolute; position: absolute;
left: 0; left: 0;
@ -26,8 +29,5 @@
#dht-cfg-note { #dht-cfg-note {
margin-top: 22px; margin-top: 22px;
} }
`
#dht-cfg sub { */
color: #666;
font-size: 13px;
}

View File

@ -84,7 +84,14 @@ var GUI = (function(){
// styles // styles
controller.styles = DOM.createStyle(`/*[CSS-CONTROLLER]*/`); controller.styles = DOM.createStyle(`
#app-mount div[class*="app-"] { margin-bottom: 48px !important; }
#dht-ctrl { position: absolute; bottom: 0; width: 100%; height: 48px; background-color: #FFF; z-index: 1000000; }
#dht-ctrl button { height: 32px; margin: 8px 0 8px 8px; font-size: 16px; padding: 0 12px; background-color: #7289DA; color: #FFF; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.75); }
#dht-ctrl button:disabled { background-color: #7A7A7A; cursor: default; }
#dht-ctrl-close { margin: 8px 8px 8px 0 !important; float: right; }
#dht-ctrl p { display: inline-block; margin: 14px 12px; }
#dht-ctrl input { display: none; }`);
// main // main
@ -97,7 +104,7 @@ ${btn("track", "")}
${btn("download", "Download")} ${btn("download", "Download")}
${btn("reset", "Reset")} ${btn("reset", "Reset")}
<p id='dht-ctrl-status'></p> <p id='dht-ctrl-status'></p>
<input id='dht-ctrl-upload-input' type='file' multiple style="display: none"> <input id='dht-ctrl-upload-input' type='file' multiple>
${btn("close", "X")}`); ${btn("close", "X")}`);
// elements // elements
@ -186,7 +193,11 @@ ${btn("close", "X")}`);
// styles // styles
settings.styles = DOM.createStyle(`/*[CSS-SETTINGS]*/`); settings.styles = DOM.createStyle(`
#dht-cfg-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.5; display: block; z-index: 1000001; }
#dht-cfg { position: absolute; left: 50%; top: 50%; width: 800px; height: 262px; margin-left: -400px; margin-top: -131px; padding: 8px; background-color: #fff; z-index: 1000002; }
#dht-cfg-note { margin-top: 22px; }
#dht-cfg sub { color: #666; font-size: 13px; }`);
// overlay // overlay
@ -263,7 +274,10 @@ It is recommended to disable link and image previews to avoid putting unnecessar
} }
}, },
setStatus: function(status) {} setStatus: function(state){
console.log("Status: " + state)
// TODO I guess.
}
}; };
return root; return root;

View File

@ -167,15 +167,15 @@ class SAVEFILE{
channelObj.name = channelName; channelObj.name = channelName;
if (extraInfo.position) { if (extraInfo.position){
channelObj.position = extraInfo.position; channelObj.position = extraInfo.position;
} }
if (extraInfo.topic) { if (extraInfo.topic){
channelObj.topic = extraInfo.topic; channelObj.topic = extraInfo.topic;
} }
if (extraInfo.nsfw) { if (extraInfo.nsfw){
channelObj.nsfw = extraInfo.nsfw; channelObj.nsfw = extraInfo.nsfw;
} }

View File

@ -110,12 +110,11 @@ const STATE = (function() {
* Registers a Discord server and channel. * Registers a Discord server and channel.
*/ */
addDiscordChannel(serverInfo, channelInfo){ addDiscordChannel(serverInfo, channelInfo){
var serverName = serverInfo.name; let serverName = serverInfo.name
var serverType = serverInfo.type; let serverType = serverInfo.type
var channelId = channelInfo.id; let channelId = channelInfo.id
var channelName = channelInfo.name; let channelName = channelInfo.name
var extraInfo = channelInfo.extra || {}; let extraInfo = channelInfo.extra
var serverIndex = this.getSavefile().findOrRegisterServer(serverName, serverType); var serverIndex = this.getSavefile().findOrRegisterServer(serverName, serverType);
if (this.getSavefile().tryRegisterChannel(serverIndex, channelId, channelName, extraInfo) === true){ if (this.getSavefile().tryRegisterChannel(serverIndex, channelId, channelName, extraInfo) === true){
@ -151,6 +150,13 @@ const STATE = (function() {
this._trackingStateChangedListeners.push(callback); this._trackingStateChangedListeners.push(callback);
callback(this._isTracking); callback(this._isTracking);
} }
/*
* Shim for code from the desktop app.
*/
setup(port, token) {
console.log("Placeholder port and token: " + port + " " + token);
}
} }
return new CLS(); return new CLS();

View File

@ -1,154 +1,161 @@
// noinspection JSAnnotator // NOTE: STOP! This file must be kept in sync with the upstream desktop app branch in order for future changes/fixes to the desktop script to be cleanly merged here.
// Changes for the browser version should be done in another file to maintain mergeablity.
const url = window.location.href; (function() {
const url = window.location.href;
if (!url.includes("discord.com/") && !url.includes("discordapp.com/") && !confirm("Could not detect Discord in the URL, do you want to run the script anyway?")) {
return;
}
if (window.DHT_LOADED) {
alert("Discord History Tracker is already loaded.");
return;
}
window.DHT_LOADED = true;
window.DHT_ON_UNLOAD = [];
/*[IMPORTS]*/
let delayedStopRequests = 0;
const stopTrackingDelayed = function(callback) {
delayedStopRequests++;
window.setTimeout(() => { if (!url.includes("discord.com/") && !url.includes("discordapp.com/") && !confirm("Could not detect Discord in the URL, do you want to run the script anyway?")) {
STATE.setIsTracking(false);
delayedStopRequests--;
if (callback) {
callback();
}
}, 200); // give the user visual feedback after clicking the button before switching off
};
let hasJustStarted = false;
let isSending = false;
const onError = function(e) {
console.log(e);
GUI.setStatus(e.status === "DISCONNECTED" ? "Disconnected" : "Error");
stopTrackingDelayed(() => isSending = false);
};
const isNoAction = function(action) {
return action === null || action === CONSTANTS.AUTOSCROLL_ACTION_NOTHING;
};
const onTrackingContinued = function(anyNewMessages) {
if (!STATE.isTracking()) {
return; return;
} }
GUI.setStatus("Tracking"); if (window.DHT_LOADED) {
alert("Discord History Tracker is already loaded.");
if (hasJustStarted) { return;
anyNewMessages = true;
hasJustStarted = false;
} }
isSending = false; window.DHT_LOADED = true;
window.DHT_ON_UNLOAD = [];
if (SETTINGS.autoscroll) { /*[IMPORTS]*/
let action = null;
const port = 0; /*[PORT]*/
const token = "/*[TOKEN]*/";
STATE.setup(port, token);
let delayedStopRequests = 0;
const stopTrackingDelayed = function(callback) {
delayedStopRequests++;
if (!DISCORD.hasMoreMessages()) { window.setTimeout(() => {
action = SETTINGS.afterFirstMsg;
}
if (isNoAction(action) && !anyNewMessages) {
action = SETTINGS.afterSavedMsg;
}
if (isNoAction(action)) {
DISCORD.loadOlderMessages();
}
else if (action === CONSTANTS.AUTOSCROLL_ACTION_PAUSE || (action === CONSTANTS.AUTOSCROLL_ACTION_SWITCH && !DISCORD.selectNextTextChannel())) {
GUI.setStatus("Reached End");
STATE.setIsTracking(false); STATE.setIsTracking(false);
} delayedStopRequests--;
}
}; if (callback) {
callback();
let waitUntilSendingFinishedTimer = null; }
}, 200); // give the user visual feedback after clicking the button before switching off
const onMessagesUpdated = async messages => { };
if (!STATE.isTracking() || delayedStopRequests > 0) {
return;
}
if (isSending) { let hasJustStarted = false;
window.clearTimeout(waitUntilSendingFinishedTimer); let isSending = false;
waitUntilSendingFinishedTimer = window.setTimeout(() => {
waitUntilSendingFinishedTimer = null;
onMessagesUpdated(messages);
}, 100);
return;
}
const info = DISCORD.getSelectedChannel(); const onError = function(e) {
console.log(e);
GUI.setStatus(e.status === "DISCONNECTED" ? "Disconnected" : "Error");
stopTrackingDelayed(() => isSending = false);
};
if (!info) { const isNoAction = function(action) {
GUI.setStatus("Error (Unknown Channel)"); return action === null || action === CONSTANTS.AUTOSCROLL_ACTION_NOTHING;
stopTrackingDelayed(); };
return;
}
isSending = true; const onTrackingContinued = function(anyNewMessages) {
if (!STATE.isTracking()) {
try {
await STATE.addDiscordChannel(info.server, info.channel);
} catch (e) {
onError(e);
return;
}
try {
if (!messages.length) {
isSending = false;
onTrackingContinued(false);
}
else {
const anyNewMessages = STATE.addDiscordMessages(messages);
onTrackingContinued(anyNewMessages);
}
} catch (e) {
onError(e);
}
};
DISCORD.setupMessageCallback(onMessagesUpdated);
STATE.onTrackingStateChanged(enabled => {
if (enabled) {
const messages = DISCORD.getMessages();
if (messages.length === 0) {
stopTrackingDelayed(() => alert("Cannot see any messages."));
return; return;
} }
GUI.setStatus("Starting"); GUI.setStatus("Tracking");
hasJustStarted = true;
// noinspection JSIgnoredPromiseFromCall if (hasJustStarted) {
onMessagesUpdated(messages); anyNewMessages = true;
} hasJustStarted = false;
else { }
isSending = false; isSending = false;
if (SETTINGS.autoscroll) {
let action = null;
if (!DISCORD.hasMoreMessages()) {
action = SETTINGS.afterFirstMsg;
}
if (isNoAction(action) && !anyNewMessages) {
action = SETTINGS.afterSavedMsg;
}
if (isNoAction(action)) {
DISCORD.loadOlderMessages();
}
else if (action === CONSTANTS.AUTOSCROLL_ACTION_PAUSE || (action === CONSTANTS.AUTOSCROLL_ACTION_SWITCH && !DISCORD.selectNextTextChannel())) {
GUI.setStatus("Reached End");
STATE.setIsTracking(false);
}
}
};
let waitUntilSendingFinishedTimer = null;
const onMessagesUpdated = async messages => {
if (!STATE.isTracking() || delayedStopRequests > 0) {
return;
}
if (isSending) {
window.clearTimeout(waitUntilSendingFinishedTimer);
waitUntilSendingFinishedTimer = window.setTimeout(() => {
waitUntilSendingFinishedTimer = null;
onMessagesUpdated(messages);
}, 100);
return;
}
const info = DISCORD.getSelectedChannel();
if (!info) {
GUI.setStatus("Error (Unknown Channel)");
stopTrackingDelayed();
return;
}
isSending = true;
try {
await STATE.addDiscordChannel(info.server, info.channel);
} catch (e) {
onError(e);
return;
}
try {
if (!messages.length) {
isSending = false;
onTrackingContinued(false);
}
else {
const anyNewMessages = await STATE.addDiscordMessages(messages);
onTrackingContinued(anyNewMessages);
}
} catch (e) {
onError(e);
}
};
DISCORD.setupMessageCallback(onMessagesUpdated);
STATE.onTrackingStateChanged(enabled => {
if (enabled) {
const messages = DISCORD.getMessages();
if (messages.length === 0) {
stopTrackingDelayed(() => alert("Cannot see any messages."));
return;
}
GUI.setStatus("Starting");
hasJustStarted = true;
// noinspection JSIgnoredPromiseFromCall
onMessagesUpdated(messages);
}
else {
isSending = false;
}
});
GUI.showController();
if (IS_FIRST_RUN) {
GUI.showSettings();
} }
}); })();
/*[DEBUGGER]*/
GUI.showController();
if (IS_FIRST_RUN) {
GUI.showSettings();
}