emote updates

This commit is contained in:
Zack Rauen 2019-06-28 01:36:05 -04:00
parent 2d1d48e0fd
commit 720744a297
9 changed files with 156 additions and 203 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,28 @@
import Builtin from "../structs/builtin";
import {Config, EmoteInfo, EmoteConfig} from "data";
import {Config, EmoteConfig} from "data";
import {Utilities, WebpackModules, DataStore, DiscordModules, Events, Settings, Strings} from "modules";
import BDEmote from "../ui/emote";
import Toasts from "../ui/toasts";
// import EmoteMenu from "./emotemenu";
import FormattableString from "../structs/string";
const request = require("request");
// const fs = require("fs");
const EmoteURLs = {
TwitchGlobal: new FormattableString(`https://static-cdn.jtvnw.net/emoticons/v1/{{id}}/1.0`),
TwitchSubscriber: new FormattableString(`https://static-cdn.jtvnw.net/emoticons/v1/{{id}}/1.0`),
FrankerFaceZ: new FormattableString(`https://cdn.frankerfacez.com/emoticon/{{id}}/1`),
BTTV: new FormattableString(`https://cdn.betterttv.net/emote/{{id}}/1x`),
BTTV2: new FormattableString(`https://cdn.betterttv.net/emote/{{id}}/1x`)
};
const EmoteMetaInfo = {
TwitchGlobal: {},
TwitchSubscriber: {},
BTTV: {},
FrankerFaceZ: {},
BTTV2: {}
};
const Emotes = {
TwitchGlobal: {},
@ -32,7 +49,8 @@ export default new class EmoteModule extends Builtin {
get collection() {return "settings";}
get category() {return "general";}
get id() {return "emotes";}
get categories() { return Object.keys(bdEmoteSettingIDs).filter(k => this.isCategoryEnabled(bdEmoteSettingIDs[k])); }
get categories() {return Object.keys(bdEmoteSettingIDs).filter(k => this.isCategoryEnabled(bdEmoteSettingIDs[k]));}
get shouldDownload() {return Settings.get("emotes", this.category, "download");}
isCategoryEnabled(id) {return super.get("emotes", "categories", id);}
@ -49,9 +67,8 @@ export default new class EmoteModule extends Builtin {
get blacklist() {return blacklist;}
get favorites() {return this.favoriteEmotes;}
getCategory(category) {
return Emotes[category];
}
getCategory(category) {return Emotes[category];}
getRemoteFile(category) {return new FormattableString(Utilities.repoUrl(`data/emotes/${category.toLowerCase()}.json`));}
initialize() {
super.initialize();
@ -66,10 +83,10 @@ export default new class EmoteModule extends Builtin {
}
async enabled() {
Settings.registerCollection("emotes", "Emotes", EmoteConfig, {title: Strings.Emotes.clearEmotes, onClick: () => { this.clearEmoteData(); this.loadEmoteData(EmoteInfo); }});
Settings.registerCollection("emotes", "Emotes", EmoteConfig, {title: Strings.Emotes.clearEmotes, onClick: () => {this.clearEmoteData(); this.loadEmoteData();}});
// Disable emote module for now because it's annoying and slow
await this.getBlacklist();
await this.loadEmoteData(EmoteInfo);
await this.loadEmoteData();
while (!this.MessageContentComponent) await new Promise(resolve => setTimeout(resolve, 100));
this.patchMessageContent();
@ -178,103 +195,6 @@ export default new class EmoteModule extends Builtin {
});
}
async loadEmoteData(emoteInfo) {
this.emotesLoaded = false;
const _fs = require("fs");
const emoteFile = "emote_data.json";
const file = Config.dataPath + emoteFile;
const exists = _fs.existsSync(file);
if (exists && this.isCacheValid()) {
Toasts.show("Loading emotes from cache.", {type: "info"});
this.log("Loading emotes from local cache.");
const data = await new Promise(resolve => {
_fs.readFile(file, "utf8", (err, content) => {
this.log("Emotes loaded from cache.");
if (err) content = {};
resolve(content);
});
});
const parsed = Utilities.testJSON(data);
let isValid = !!parsed;
if (isValid) Object.assign(Emotes, parsed);
for (const e in emoteInfo) {
isValid = Object.keys(Emotes[emoteInfo[e].variable]).length > 0;
}
if (isValid) {
Toasts.show("Emotes successfully loaded.", {type: "success"});
this.emotesLoaded = true;
Events.dispatch("emotes-loaded");
return;
}
this.log("Cache was corrupt, downloading...");
_fs.unlinkSync(file);
}
if (!Settings.get(this.category, "general", "download")) return;
Toasts.show(Strings.Emotes.downloading, {type: "info"});
for (const e in emoteInfo) {
await new Promise(r => setTimeout(r, 1000));
const data = await this.downloadEmotes(emoteInfo[e]);
Emotes[emoteInfo[e].variable] = data;
}
Toasts.show(Strings.Emotes.downloaded, {type: "success"});
try { _fs.writeFileSync(file, JSON.stringify(Emotes), "utf8"); }
catch (err) { this.stacktrace("Could not save emote data.", err); }
this.emotesLoaded = true;
Events.dispatch("emotes-loaded");
}
downloadEmotes(emoteMeta) {
const repoFile = Utilities.repoUrl(`data/emotes/${emoteMeta.variable.toLowerCase()}.json`);
if (emoteMeta.url && !emoteMeta.backup) emoteMeta.backup = repoFile;
if (!emoteMeta.url) emoteMeta.url = repoFile;
const options = {
url: emoteMeta.url,
timeout: emoteMeta.timeout ? emoteMeta.timeout : 5000,
json: true
};
this.log(`Downloading: ${emoteMeta.variable} (${emoteMeta.url})`);
return new Promise((resolve, reject) => {
request.get(options, (error, response, parsedData) => {
if (error) {
this.stacktrace("Could not download " + emoteMeta.variable, error);
if (emoteMeta.backup || emoteMeta.url) {
emoteMeta.url = emoteMeta.backup;
emoteMeta.backup = null;
if (emoteMeta.backupParser) emoteMeta.parser = emoteMeta.backupParser;
return resolve(this.downloadEmotes(emoteMeta));
}
return reject({});
}
if (typeof(emoteMeta.parser) === "function") parsedData = emoteMeta.parser(parsedData);
for (const emote in parsedData) {
if (emote.length < 4 || blacklist.includes(emote)) {
delete parsedData[emote];
continue;
}
parsedData[emote] = emoteMeta.getEmoteURL(parsedData[emote]);
}
resolve(parsedData);
this.log("Downloaded: " + emoteMeta.variable);
});
});
}
getBlacklist() {
return new Promise(resolve => {
request.get({url: Utilities.repoUrl(`data/emotes/blacklist.json`), json: true}, (err, resp, data) => {
@ -284,16 +204,64 @@ export default new class EmoteModule extends Builtin {
});
}
isCacheValid() {
const cacheLength = DataStore.getBDData("emoteCacheDays") || DataStore.setBDData("emoteCacheDays", 7) || 7;
const cacheDate = new Date(DataStore.getBDData("emoteCacheDate") || null);
const currentDate = new Date();
const daysBetween = Math.round(Math.abs((currentDate.getTime() - cacheDate.getTime()) / (24 * 60 * 60 * 1000)));
if (daysBetween > cacheLength) {
DataStore.setBDData("emoteCacheDate", currentDate.toJSON());
return false;
isCacheValid(category) {
return new Promise(resolve => {
const etag = DataStore.getCacheHash("emotes", category);
if (!etag) return resolve(false);
request.head({url: this.getRemoteFile(category), headers: {"If-None-Match": etag}}, (err, resp) => {
resolve(resp.statusCode == 304);
});
});
}
async loadEmoteData() {
this.emotesLoaded = false;
for (const category in Emotes) {
const exists = DataStore.emotesExist(category);
const valid = await this.isCacheValid(category);
const useCache = (valid) || (!valid && exists && !this.shouldDownload);
let data = null;
if (useCache) {
this.log(`Loading ${category} emotes from local cache.`);
const cachedData = DataStore.getEmoteData(category);
const hasData = Object.keys(data).length > 0;
if (hasData) data = cachedData;
}
if (!data) data = await this.downloadEmotes(category);
Object.assign(Emotes[category], data);
}
return true;
// Toasts.show(Strings.Emotes.downloading, {type: "info"});
// Toasts.show(Strings.Emotes.downloaded, {type: "success"});
this.emotesLoaded = true;
Events.dispatch("emotes-loaded");
}
downloadEmotes(category) {
const url = this.getRemoteFile(category);
this.log(`Downloading ${category} from ${url}`);
const options = {url: url, timeout: 5000, json: true};
return new Promise(resolve => {
request.get(options, (error, response, parsedData) => {
if (error || response.statusCode != 200) {
this.stacktrace(`Could not download ${category} emotes.`, error);
return resolve({});
}
for (const emote in parsedData) {
if (emote.length < 4 || blacklist.includes(emote) || !parsedData[emote]) {
delete parsedData[emote];
continue;
}
parsedData[emote] = EmoteURLs[category].format({id: parsedData[emote]});
}
DataStore.saveEmoteData(category, parsedData);
resolve(parsedData);
this.log(`Downloaded ${category}`);
});
});
}
clearEmoteData() {
@ -305,4 +273,24 @@ export default new class EmoteModule extends Builtin {
DataStore.setBDData("emoteCacheDate", (new Date()).toJSON());
for (const category in Emotes) Object.assign(Emotes, {[category]: {}});
}
};
};
// (async () => {
// const emoteData = await new Promise(resolve => {
// const req = require("request");
// req.get({url: "https://twitchemotes.com/api_cache/v3/global.json", json: true}, (err, resp, parsedData) => {
// for (const emote in parsedData) {
// if (emote.length < 4 || window.bemotes.includes(emote)) {
// delete parsedData[emote];
// continue;
// }
// parsedData[emote] = parsedData[emote].id;
// }
// resolve(parsedData);
// });
// });
// const fs = require("fs");
// fs.writeFileSync("Z:\\Programming\\BetterDiscordStuff\\BetterDiscordApp\\data\\emotes\\global.json", JSON.stringify(emoteData));
// return emoteData;
// })();

View File

@ -1,6 +1,4 @@
import Config from "./config";
import EmoteInfo from "./emotes/info";
import EmoteConfig from "./emotes/config";
import SettingsConfig from "./settings/config";
export {Config, EmoteInfo, EmoteConfig, SettingsConfig};
export {default as Config} from "./config";
export {default as EmoteConfig} from "./settings/emoteconfig";
export {default as SettingsConfig} from "./settings/config";
export {default as Strings} from "./strings";

View File

@ -1,38 +0,0 @@
export default {
TwitchGlobal: {
url: "https://twitchemotes.com/api_cache/v3/global.json",
variable: "TwitchGlobal",
getEmoteURL: (e) => `https://static-cdn.jtvnw.net/emoticons/v1/${e.id}/1.0`,
getOldData: (url, name) => {return {id: url.match(/\/([0-9]+)\//)[1], code: name, emoticon_set: 0, description: null};}
},
TwitchSubscriber: {
variable: "TwitchSubscriber",
getEmoteURL: (e) => `https://static-cdn.jtvnw.net/emoticons/v1/${e}/1.0`,
getOldData: (url) => url.match(/\/([0-9]+)\//)[1]
},
FrankerFaceZ: {
variable: "FrankerFaceZ",
getEmoteURL: (e) => `https://cdn.frankerfacez.com/emoticon/${e}/1`,
getOldData: (url) => url.match(/\/([0-9]+)\//)[1]
},
BTTV: {
url: "https://api.betterttv.net/emotes",
variable: "BTTV",
parser: (data) => {
const emotes = {};
for (let e = 0, len = data.emotes.length; e < len; e++) {
const emote = data.emotes[e];
emotes[emote.regex] = emote.url;
}
return emotes;
},
getEmoteURL: (e) => `${e}`,
getOldData: (url) => url
},
BTTV2: {
variable: "BTTV2",
oldVariable: "emotesBTTV2",
getEmoteURL: (e) => `https://cdn.betterttv.net/emote/${e}/1x`,
getOldData: (url) => url.match(/emote\/(.+)\//)[1]
}
};

View File

@ -17,28 +17,30 @@ export default new class DataStore {
constructor() {
this.data = {misc: {}};
this.pluginData = {};
this.localeHashes = {};
this.cacheData = {};
}
initialize() {
if (!fs.existsSync(this.baseFolder)) fs.mkdirSync(this.baseFolder);
if (!fs.existsSync(this.dataFolder)) fs.mkdirSync(this.dataFolder);
if (!fs.existsSync(this.localeFolder)) fs.mkdirSync(this.localeFolder);
if (!fs.existsSync(this.localeCache)) fs.writeFileSync(this.localeCache, JSON.stringify({}));
if (!fs.existsSync(this.emoteFolder)) fs.mkdirSync(this.emoteFolder);
if (!fs.existsSync(this.cacheFile)) fs.writeFileSync(this.cacheFile, JSON.stringify({}));
if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data.misc, null, 4));
if (!fs.existsSync(this.customCSS)) fs.writeFileSync(this.customCSS, "");
const dataFiles = fs.readdirSync(this.dataFolder).filter(f => !fs.statSync(path.resolve(this.dataFolder, f)).isDirectory() && f.endsWith(".json"));
for (const file of dataFiles) {
this.data[file.split(".")[0]] = __non_webpack_require__(path.resolve(this.dataFolder, file));
}
this.localeHashes = JSON.parse(fs.readFileSync(this.localeCache).toString());
this.cacheData = Utilities.testJSON(fs.readFileSync(this.cacheFile).toString()) || {};
}
get customCSS() {return this._customCSS || (this._customCSS = path.resolve(this.dataFolder, "custom.css"));}
get baseFolder() {return this._baseFolder || (this._baseFolder = path.resolve(Config.dataPath, "data"));}
get dataFolder() {return this._dataFolder || (this._dataFolder = path.resolve(this.baseFolder, `${releaseChannel}`));}
get localeFolder() {return this._localeFolder || (this._localeFolder = path.resolve(this.baseFolder, `locales`));}
get localeCache() {return this._localeCache || (this._localeCache = path.resolve(this.localeFolder, `.cache`));}
get emoteFolder() {return this._emoteFolder || (this._emoteFolder = path.resolve(this.baseFolder, `emotes`));}
get cacheFile() {return this._cacheFile || (this._cacheFile = path.resolve(this.baseFolder, `.cache`));}
get BDFile() {return this._BDFile || (this._BDFile = path.resolve(Config.dataPath, "data", `${releaseChannel}.json`));}
getPluginFile(pluginName) {return path.resolve(Config.dataPath, "plugins", pluginName + ".config.json");}
@ -67,13 +69,30 @@ export default new class DataStore {
fs.writeFileSync(path.resolve(this.localeFolder, `${locale}.json`), JSON.stringify(strings, null, 4));
}
getLocaleHash(locale) {
return this.localeHashes[locale] || "";
getCacheHash(category, key) {
if (!this.cacheData[category]) return "";
if (!fs.existsSync(path.resolve(this.baseFolder, category, `${key}.json`))) return "";
return this.cacheData[category][key] || "";
}
saveLocaleHash(locale, hash) {
this.localeHashes[locale] = hash;
fs.writeFileSync(this.localeCache, JSON.stringify(this.localeHashes, null, 4));
setCacheHash(category, key, hash) {
if (!this.cacheData[category]) this.cacheData[category] = {};
this.cacheData[category][key] = hash;
fs.writeFileSync(this.cacheFile, JSON.stringify(this.cacheData, null, 4));
}
emotesExist(category) {
return fs.existsSync(path.resolve(this.emoteFolder, `${category}.json`));
}
getEmoteData(category) {
const file = path.resolve(this.emoteFolder, `${category}.json`);
if (!fs.existsSync(file)) return null;
return Utilities.testJSON(fs.readFileSync(file).toString());
}
saveEmoteData(category, data) {
fs.writeFileSync(path.resolve(this.emoteFolder, `${category}.json`), JSON.stringify(data));
}
getData(key) {

View File

@ -39,7 +39,7 @@ export default new class LocaleManager {
}
async getLocaleStrings(locale) {
const hash = DataStore.getLocaleHash(locale);
const hash = DataStore.getCacheHash("locales", locale);
if (!hash) return await this.downloadLocale(locale);
const invalid = await this.downloadLocale(locale, hash);
if (!invalid) return DataStore.getLocale(locale);
@ -54,12 +54,10 @@ export default new class LocaleManager {
json: true
};
if (hash) options.headers = {"If-None-Match": hash};
console.log(options.headers);
request.get(options, (err, resp, newStrings) => {
if (err || resp.statusCode !== 200) return resolve(null);
console.log(resp);
DataStore.saveLocale(locale, newStrings);
DataStore.saveLocaleHash(locale, resp.headers.etag);
DataStore.setCacheHash("locales", locale, resp.headers.etag);
resolve(newStrings);
});
});