Last active
February 12, 2020 09:49
-
-
Save molenzwiebel/149ffc3bb214036f430820b613488f0b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//META{"name":"EmojiTyperIntegration"}*// | |
const EmojiTyperIntegration = class EmojiTyperIntegration { | |
getName() { return "EmojiTyper Integration"; } | |
getShortName() { return "emojityper-integration"; } | |
getDescription() { return "Integrates https://emojityper.com with your emoji picker. Simply search with @<term> to query EmojiPicker, or type :@<query> in chat to autocomplete with EmojiPicker."; } | |
getVersion() { return "1.0.0"; } | |
getAuthor(){ return "molenzwiebel"; } | |
load() {} | |
unload() {} | |
start() { | |
const modules = this.getWebpackModules(); | |
if (!modules) return this.failInitialization("Couldn't query Discord internals."); | |
const emojiSet = Object.keys(modules.c).map(x => modules.c[x]).find(x => x && x.exports && x.exports.__proto__ && x.exports.__proto__.getGuildEmoji); | |
if (!emojiSet || !emojiSet.exports) return this.failInitialization("Couldn't find Discord emoji storage."); | |
const allEmojis = emojiSet.exports.search({ getGuildId() { return -1; }, isPrivate() { return true; } }, "").filter(x => x.surrogates); | |
const pickerExport = Object.keys(modules.c).map(x => modules.c[x]).find(x => x.exports && x.exports.EmojiPicker); | |
if (!pickerExport || !pickerExport.exports || !pickerExport.exports.EmojiPicker) return this.failInitialization("Couldn't find EmojiPicker in Discord internals."); | |
this.emojiPicker = pickerExport.exports.EmojiPicker; | |
this.originalRender = this.emojiPicker.prototype.render; | |
// We override render() to inject our custom handleQueryChange. | |
const self = this; | |
this.emojiPicker.prototype.render = function() { | |
if (!this.handleQueryChange.injected) { | |
this.handleQueryChange = self.handleQueryChange.bind(this, this.handleQueryChange, allEmojis); | |
this.handleQueryChange.injected = true; | |
} | |
return self.originalRender.apply(this, arguments); | |
}; | |
const messageBarExport = Object.keys(modules.c).map(x => modules.c[x]).find(x => x && x.exports && x.exports.default && x.exports.default.prototype && x.exports.default.prototype.handleStartFileUpload); | |
if (!messageBarExport || !messageBarExport.exports || !messageBarExport.exports.default) return; // no message bar injection I guess | |
this.messageBar = messageBarExport.exports.default; | |
const originalRenderAutocomplete = this.originalRenderAutocomplete = this.messageBar.prototype.renderAutocomplete; | |
let emojiAutocompleter; | |
this.messageBar.prototype.renderAutocomplete = function() { | |
// Remove original autocompleter for emojis, we replace and add functionalities to it. | |
if (this.props.autocompleteOptions.EMOJI) { | |
emojiAutocompleter = this.props.autocompleteOptions.EMOJI; | |
delete this.props.autocompleteOptions.EMOJI; | |
} | |
const self = this; | |
this.props.autocompleteOptions.EMOJITYPER = { | |
matches(firstChar, rest) { | |
return firstChar === ":" && rest.length > 1; | |
}, | |
queryResults(text) { | |
if (!text.startsWith("@")) return emojiAutocompleter.queryResults(text); | |
if (self.state.emojiTyperQuery === text.substr(1)) return { emoji: self.state.emojiTyperResults }; | |
self.setState({ | |
emojiTyperQuery: text.substr(1), | |
emojiTyperResults: [] | |
}); | |
loadEmojiTyperResults(text.substr(1), allEmojis, results => { | |
if (self.state.emojiTyperQuery !== text.substr(1)) return; | |
self.setState({ | |
emojiTyperResults: results | |
}); | |
}); | |
return { emoji: [] }; | |
}, | |
renderResults(query, selectedIndex, handleHover, handleClick, results) { | |
if (!query.startsWith("@")) return emojiAutocompleter.renderResults.call(this, query, selectedIndex, handleHover, handleClick, results); | |
const emojiResults = self.state.emojiTyperResults; | |
if (emojiResults && emojiResults.length) { | |
results.emoji = emojiResults; | |
return emojiAutocompleter.renderResults.call(this, "EmojiTyper Results for '" + query.substr(1) + "'", selectedIndex, handleHover, handleClick, { emoji: emojiResults }); | |
} | |
return emojiAutocompleter.renderResults.call(this, "Loading EmojiTyper results...", selectedIndex, handleHover, handleClick, { emoji: allEmojis.filter(x => x.surrogates === "⏳") }); | |
}, | |
getText: emojiAutocompleter.getText | |
}; | |
return originalRenderAutocomplete.apply(this, arguments); | |
}; | |
} | |
stop() { | |
if (!this.enabled) return; | |
this.emojiPicker.prototype.render = this.originalRender; | |
this.messageBar.prototype.renderAutocomplete = this.originalRenderAutocomplete; | |
} | |
handleQueryChange(originalFn, allEmojis, query) { | |
// If we didn't search for @, just apply our original filtering. | |
if (query.indexOf("@") !== 0 || query === "@") return originalFn.call(this, query); | |
// Load results. | |
loadEmojiTyperResults(query.substr(1), allEmojis, result => { | |
this.setState({ | |
searchResults: result, | |
metaData: this.computeMetaData(result) | |
}); | |
}); | |
// Display an hourglass as "loading indicator". | |
const loadingEmoji = allEmojis.filter(x => x.allNamesString === ":hourglass_flowing_sand:"); | |
this.setState({ | |
query, | |
searchResults: loadingEmoji, | |
metaData: this.computeMetaData(loadingEmoji), | |
selectedRow: -1, | |
selectedColumn: -1, | |
currentSection: null | |
}); | |
} | |
failInitialization(message) { | |
alert("Failed to initialize " + this.getName() + ": " + message + " " + this.getName() + " will not function."); | |
this.enabled = false; | |
} | |
getWebpackModules() { | |
if (!webpackJsonp) return null; | |
const req = webpackJsonp([], { | |
[this.constructor.name]: (module, exports, req) => exports.default = req | |
}, [this.constructor.name]); | |
if (!req || !req.default) return null; | |
const modules = req.default; | |
if (modules.m) delete modules.m[this.constructor.name]; | |
if (modules.c) delete modules.c[this.constructor.name]; | |
return modules; | |
} | |
}; | |
const loadEmojiTyperResults = debounce(function loadEmojiTyperResults(query, allEmojis, callback) { | |
fetch(`https://emojityper.appspot.com/query?query=${encodeURIComponent(query)}&prefix=true`).then(x => x.json()).then(result => { | |
const results = result.results; | |
const emojis = [].concat(...results.map(x => x.slice(1))); | |
callback(allEmojis.filter(x => emojis.indexOf(x.surrogates) !== -1)); | |
}); | |
}, 300); | |
function debounce(func, wait) { | |
let timeout; | |
return function() { | |
let context = this, args = arguments; | |
const later = function() { | |
timeout = null; | |
func.apply(context, args); | |
}; | |
clearTimeout(timeout); | |
timeout = setTimeout(later, wait); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment