2018-05-20 03:51:19 +00:00
/ *
EndPwn "API"
Copyright 2018 EndPwn Project
Permission is hereby granted , free of charge , to any person obtaining a copy of this software and associated documentation files ( the "Software" ) , to deal in the Software without restriction , including without limitation the rights to use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies of the Software , and to permit persons to whom the Software is furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
DO NOT EDIT THIS FILE ! Your bootstrap may overwrite changes to it , and you will lose your work !
EndPwn3 users : You can prevent this by creating a file in the same directory named DONTUPDATE
https : //github.com/endpwn/
* /
function evaluate ( str , exportsR ) {
var exports = { } ;
var ret = eval ( str ) ;
if ( exportsR ) Object . assign ( exportsR , exports ) ;
return ret ;
}
( ( ) => {
var internal = {
2018-07-12 02:17:42 +00:00
print : function ( t ) {
console . log ( ` %c[EPAPI]%c ${ t } ` , "font-weight:bold;color:#0cc" , "" ) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
warn : function ( t , e ) {
if ( typeof e == "undefined" ) e = "" ;
else e = ":\n\n" + e ;
console . warn (
` %c[EPAPI]%c ${ t } ` ,
"font-weight:bold;color:#0cc" ,
"" ,
e
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
error : function ( e , t ) {
if ( typeof t == "undefined" ) t = "uncaught exception" ;
console . error (
` %c[EPAPI]%c ${ t } : \n \n ` ,
"font-weight:bold;color:#0cc" ,
"" ,
e
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
alert : function ( b , t ) {
if ( typeof t == "undefined" ) t = "EPAPI" ;
else t = "EPAPI: " + t ;
2018-05-20 03:51:19 +00:00
try {
2018-07-12 02:17:42 +00:00
wc
. findFunc ( "e.onConfirmSecondary" ) [ 1 ]
. exports . show ( { title : t , body : b } ) ;
2018-05-20 03:51:19 +00:00
} catch ( e2 ) {
2018-07-12 02:17:42 +00:00
internal . error (
e2 ,
"Error occurred while attempting to pop the standard dialog box, falling back to alert()"
) ;
2018-05-20 03:51:19 +00:00
alert ( b , t ) ;
}
} ,
crashed : 0 ,
2018-07-12 02:17:42 +00:00
crash : function ( e ) {
internal . error ( e , "Fatal error!" ) ;
2018-05-20 03:51:19 +00:00
if ( ! internal . crashed ) {
internal . crashed = 1 ;
if ( internal . brand ) {
2018-07-12 02:17:42 +00:00
internal . setSigmaColor ( "#f00" ) ;
2018-05-20 03:51:19 +00:00
}
2018-07-12 02:17:42 +00:00
internal . alert (
"A fatal error occurred in EPAPI.\n\nThis usually means there is a bug in EPAPI or your bootstrap. It can also mean that Discord updated, breaking something important.\n\nCheck the console for details.\n\nIf you don't know what this means, contact your bootstrap maintainer." ,
"Fatal error!"
) ;
2018-05-20 03:51:19 +00:00
}
} ,
2018-07-12 02:17:42 +00:00
setwordmark : function ( html ) {
2018-05-20 03:51:19 +00:00
try {
2018-07-12 02:17:42 +00:00
return ( document . querySelector (
'[class*="wordmark"]'
) . innerHTML = html ) ;
} catch ( e ) { }
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
setSigmaColor : function ( color ) {
setTimeout (
( ) =>
internal . setwordmark (
` <svg xmlns="http://www.w3.org/2000/svg"><g transform="matrix(0.01,0.000000,0.000000,-0.01,0,14)" id="lambda"><path fill=" ${ color } " d="M 967.00000,288.00000 L 1003.0000,288.00000 C 1003.0000,182.66667 986.00000,106.00000 952.00000,58.000000 C 918.00000,10.000000 875.00000,-14.000000 823.00000,-14.000000 C 781.00000,-14.000000 740.66667,1.6666667 702.00000,33.000000 C 663.33333,64.333333 628.66667,149.33333 598.00000,288.00000 L 512.00000,676.00000 L 214.00000,0.00000000 L 25.000000,0.00000000 L 453.00000,922.00000 C 430.33333,1041.3333 403.00000,1129.6667 371.00000,1187.0000 C 339.00000,1244.3333 299.33333,1273.0000 252.00000,1273.0000 C 214.00000,1273.0000 181.00000,1258.6667 153.00000,1230.0000 C 125.00000,1201.3333 109.00000,1156.3333 105.00000,1095.0000 L 69.000000,1095.0000 C 71.000000,1193.0000 90.666667,1271.3333 128.00000,1330.0000 C 165.33333,1388.6667 212.00000,1418.3333 268.00000,1419.0000 C 304.00000,1419.0000 338.00000,1404.3333 370.00000,1375.0000 C 402.00000,1345.6667 430.00000,1295.0000 454.00000,1223.0000 C 478.00000,1151.0000 515.00000,1002.3333 565.00000,777.00000 L 636.00000,460.00000 C 664.66667,328.66667 694.66667,241.00000 726.00000,197.00000 C 757.33333,153.00000 795.33333,130.66667 840.00000,130.00000 C 914.66667,130.00000 957.00000,182.66667 967.00000,288.00000 z"/></g></svg> `
) ,
2000
) ;
2018-05-20 03:51:19 +00:00
} ,
// document-level events (internal)
events : {
// dispatched whenever Discord's internal event system dispatches an event
2018-07-12 02:17:42 +00:00
discordNativeEvent : function ( e ) {
return new CustomEvent ( "ep-native" , { detail : e } ) ;
2018-05-20 03:51:19 +00:00
} ,
// dispatched whenever EPAPI is done initializing and loading plugins
2018-07-12 02:17:42 +00:00
onReady : function ( ) {
return new Event ( "ep-ready" ) ;
2018-05-20 03:51:19 +00:00
} ,
// dispatched during early init, to signal that at least the global namespace is ready
// intended for use by bootstraps
2018-07-12 02:17:42 +00:00
onPrepared : function ( ) {
return new Event ( "ep-prepared" ) ;
2018-05-20 03:51:19 +00:00
} ,
// dispatched whenever the user changes channel/guild in the ui
2018-07-12 02:17:42 +00:00
onChannelChange : function ( e ) {
return new CustomEvent ( "ep-onchannelchange" , {
detail : e . detail
} ) ;
2018-05-20 03:51:19 +00:00
} ,
// dispatched whenever any message is received by the client
2018-07-12 02:17:42 +00:00
onMessage : function ( e ) {
return new CustomEvent ( "ep-onmessage" , { detail : e . detail } ) ;
2018-05-20 03:51:19 +00:00
} ,
// dispatched whenever a message is received in the channel that the user is currently viewing
2018-07-12 02:17:42 +00:00
onChannelMessage : function ( e ) {
return new CustomEvent ( "ep-onchannelmessage" , {
detail : e . detail
} ) ;
2018-05-20 03:51:19 +00:00
}
} ,
// stuff asarpwn's i.js and main.js used to handle
2018-07-12 02:17:42 +00:00
prepare : async function ( ) {
internal . print ( "loading RapidDOM..." ) ;
2018-06-13 22:25:08 +00:00
require ( "./rapiddom.js" ) ;
2018-05-20 03:51:19 +00:00
// undefine config and settings if running in lite mode and dont deal with require()
if ( internal . lite ) {
exports . config = undefined ;
2018-07-12 02:17:42 +00:00
} else {
2018-06-13 22:25:08 +00:00
// only use the (now defunct) directory escape exploit if the bootstrap hasnt declared itself as native
// TODO: once the exploit fix gets pushed out to stable we should probably start assuming native by default
if ( ! internal . native ) {
// mutant hybrid require() for maximum compatibility, always defined now because some bootstraps have bad implementations
2018-07-12 02:17:42 +00:00
internal . print ( "defining require..." ) ;
var r = DiscordNative . nativeModules . requireModule (
"discord_/../electron"
) . remote . require ;
2018-06-13 22:25:08 +00:00
window . require = m => {
try {
2018-07-12 02:17:42 +00:00
return DiscordNative . nativeModules . requireModule (
"discord_/../" + m
) ;
} catch ( e ) {
2018-06-13 22:25:08 +00:00
return r ( m ) ;
}
} ;
}
2018-05-20 03:51:19 +00:00
// here we import and define some stuff that usually gets defined by the bootstrap, just in case
2018-07-12 02:17:42 +00:00
internal . print ( "requiring necessary modules..." ) ;
window . electron = require ( "electron" ) . remote ;
2018-05-20 03:51:19 +00:00
window . app = electron . app ;
window . fs = require ( "original-fs" ) ;
// kinclude executes a file directly in the context of the page
2018-07-12 02:17:42 +00:00
window . kinclude = function ( p ) {
return evaluate ( fs . readFileSync ( p , "utf8" ) . toString ( ) ) ;
} ;
2018-05-20 03:51:19 +00:00
// krequire is a reimplementation of require(), only intended for loading plugins
2018-07-12 02:17:42 +00:00
window . krequire = function ( p ) {
2018-06-13 22:25:08 +00:00
/ * v a r e x p o r t s = { } ;
2018-05-20 03:51:19 +00:00
evaluate ( fs . readFileSync ( $api . data + '/plugins/' + p + ( p . endsWith ( '.js' ) ? '' : '.js' ) , 'utf8' ) . toString ( ) , exports ) ;
2018-06-13 22:25:08 +00:00
return exports ; * /
2018-07-12 02:17:42 +00:00
return require ( $api . data +
"/plugins/" +
p +
( p . endsWith ( ".js" ) ? "" : ".js" ) ) ;
} ;
2018-05-20 03:51:19 +00:00
}
2018-07-12 02:17:42 +00:00
window . kparse = function ( p ) {
2018-05-20 03:51:19 +00:00
var exports = { } ;
evaluate ( p , exports ) ;
return exports ;
2018-07-12 02:17:42 +00:00
} ;
2018-05-20 03:51:19 +00:00
2018-05-20 15:12:24 +00:00
// this part sets up webcrack, which is a very important part of EPAPI
// created by bootsy, fixed for new webpack by dr1ft
2018-07-12 02:17:42 +00:00
internal . print ( "initializing webcrack..." ) ;
function webcrack ( n , b , d ) {
( mArr = d . m ) ,
( mCac = d . c ) ,
( mCar = [ ] ) ,
Object . keys ( mCac ) . forEach ( function ( n ) {
mCar [ n ] = mCac [ n ] ;
} ) ,
( findFunc = function ( n ) {
results = [ ] ;
if ( "string" == typeof n )
mArr . forEach ( function ( r , t ) {
- 1 !== r . toString ( ) . indexOf ( n ) &&
results . push ( mCac [ t ] ) ;
} ) ;
else {
if ( "function" != typeof n )
throw new TypeError (
"findFunc can only find via string and function, " +
typeof n +
" was passed"
) ;
mArr . forEach ( function ( r , e ) {
n ( r ) && results . push ( mCac [ e ] ) ;
} ) ;
}
return results ;
} ) ,
( findCache = function ( n ) {
if ( ( ( results = [ ] ) , "function" == typeof n ) )
mCar . forEach ( function ( r , t ) {
n ( r ) && results . push ( r ) ;
} ) ;
else {
if ( "string" != typeof n )
throw new TypeError (
"findCache can only find via function or string, " +
typeof n +
" was passed"
) ;
mCar . forEach ( function ( r , t ) {
if ( "object" == typeof r . exports )
for ( p in r . exports )
if (
( p == n && results . push ( r ) ,
"default" == p &&
"object" ==
typeof r . exports [ "default" ] )
)
for ( p in r . exports [ "default" ] )
p == n && results . push ( r ) ;
} ) ;
}
return results ;
} ) ,
( window . wc = {
get : d ,
modArr : mArr ,
modCache : mCac ,
modCArr : mCar ,
findFunc : findFunc ,
findCache : findCache
} ) ;
}
typeof webpackJsonp == "function"
? webpackJsonp ( [ 1e3 ] , { webcrack : webcrack } , [ "webcrack" ] )
: webpackJsonp . push ( [
[ 1e3 ] ,
{ webcrack : webcrack } ,
[ [ "webcrack" ] ]
] ) ;
2018-05-20 03:51:19 +00:00
2018-07-12 02:17:42 +00:00
internal . print ( "defining helper functions..." ) ;
2018-05-20 03:51:19 +00:00
// shorthand methods that are used internally and in many plugins, maintained for compatibility and convenience
window . $listen = ( e , c ) => {
var listener = {
name : e ,
2018-07-12 02:17:42 +00:00
callback : function ( ) {
2018-05-20 03:51:19 +00:00
try {
c . apply ( null , arguments ) ;
2018-07-12 02:17:42 +00:00
} catch ( e ) {
internal . error (
e ,
"An event listener threw an exception"
) ;
2018-05-20 03:51:19 +00:00
}
} ,
2018-07-12 02:17:42 +00:00
unregister : function ( ) {
2018-05-20 03:51:19 +00:00
document . removeEventListener ( this . name , this . callback ) ;
}
2018-07-12 02:17:42 +00:00
} ;
2018-05-20 03:51:19 +00:00
document . addEventListener ( listener . name , listener . callback ) ;
return listener ;
} ;
window . $dispatch = e => document . dispatchEvent ( e ) ;
window . $purge = e => {
2018-07-12 02:17:42 +00:00
internal . warn (
"$purge() is deprecated, use HTMLElement.purge() instead"
) ;
e . innerHTML = "" ;
2018-05-20 03:51:19 +00:00
} ;
2018-07-12 02:17:42 +00:00
window . $ _ = function ( e , c , t , i ) {
internal . warn (
"$_() is deprecated, use createElement() and RapidDOM instead"
) ;
2018-05-20 03:51:19 +00:00
var elm = document . createElement ( e ) ;
2018-07-12 02:17:42 +00:00
if ( typeof c != "undefined" ) {
2018-05-20 03:51:19 +00:00
elm . className = c ;
2018-07-12 02:17:42 +00:00
if ( typeof t != "undefined" ) {
2018-05-20 03:51:19 +00:00
elm . innerText = t ;
2018-07-12 02:17:42 +00:00
if ( typeof i != "undefined" ) {
2018-05-20 03:51:19 +00:00
elm . id = i ;
}
}
}
return elm ;
} ;
window . $chan = exports . ui . getCurrentChannel ;
window . $guild = exports . ui . getCurrentGuild ;
window . $me = exports . internal . getId ;
// expose EPAPI as $api, which is what most plugins expect it to be known as
window . $api = exports ;
// derive the date of creation from a discord snowflake id
2018-07-12 02:17:42 +00:00
Date . fromSnowflake = id => new Date ( id / 4194304 + 1420070400000 ) ;
2018-05-20 03:51:19 +00:00
} ,
// set everything up and load plugins
2018-07-12 02:17:42 +00:00
init : function ( ) {
2018-07-31 17:06:29 +00:00
if ( $ ( ".app" ) != null ? $ ( ".app" ) . children . length > 0 : 0 ) {
2018-05-20 03:51:19 +00:00
try {
2018-07-12 02:17:42 +00:00
if ( exports . localStorage . get ( "safemode" ) ) {
internal . print (
"running in safe mode, aborting late-init and informing the user"
) ;
exports . localStorage . remove ( "safemode" ) ;
internal . setSigmaColor ( "#ff0" ) ;
internal . alert (
"EPAPI is running in safe mode. No plugins have been loaded and internal Discord data structures have been left unmodified." ,
"Safe Mode"
) ;
2018-05-20 03:51:19 +00:00
return ;
}
2018-07-12 02:17:42 +00:00
exports . localStorage . remove ( "safemode" ) ;
2018-05-20 03:51:19 +00:00
// actually start initializing...
2018-07-12 02:17:42 +00:00
internal . print ( "Discord ready, initializing..." ) ;
2018-05-20 03:51:19 +00:00
// number of broken plugins
var warning = 0 ;
// use the ep-native event to dispatch other events
2018-07-12 02:17:42 +00:00
$listen ( "ep-native" , e => {
2018-05-20 03:51:19 +00:00
switch ( e . detail . type ) {
2018-07-12 02:17:42 +00:00
case "MESSAGE_CREATE" :
2018-05-20 03:51:19 +00:00
$dispatch ( internal . events . onMessage ( e ) ) ;
break ;
2018-07-12 02:17:42 +00:00
case "CHANNEL_SELECT" :
2018-05-20 03:51:19 +00:00
$dispatch ( internal . events . onChannelChange ( e ) ) ;
break ;
}
} ) ;
// ep-onchannelmessage is like ep-onmessage, except it only fires when the user is currently viewing the channel the message originates from
2018-07-12 02:17:42 +00:00
$listen ( "ep-onmessage" , e => {
2018-05-20 03:51:19 +00:00
if ( e . detail . channel _id == $chan ( ) ) {
$dispatch ( internal . events . onChannelMessage ( e ) ) ;
}
} ) ;
// register an event with discord's internal event system
2018-07-12 02:17:42 +00:00
internal . print ( "registering Discord event handler..." ) ;
2018-05-20 03:51:19 +00:00
window . _ _logAllInternalEvents = false ;
exports . internal . dispatcher . default . register ( e => {
if ( ! internal . crashed ) {
if ( window . _ _logAllInternalEvents ) {
2018-07-12 02:17:42 +00:00
console . log ( e . type + "\n" , e ) ;
2018-05-20 03:51:19 +00:00
}
try {
2018-07-12 02:17:42 +00:00
$dispatch (
internal . events . discordNativeEvent ( e )
) ;
} catch ( e ) {
2018-05-20 03:51:19 +00:00
internal . crash ( e ) ;
}
}
} ) ;
// hook the discord internal event system
// TODO: registering with the event system is no longer necessary, we should probably unify this with the above chunk of code
2018-07-12 02:17:42 +00:00
exports . internal . dispatcher . default . dispatch _original =
exports . internal . dispatcher . default . dispatch ;
exports . internal . dispatcher . default . dispatch = function ( e ) {
internal . hooks
. filter ( x => x . type == e . type )
. forEach ( x => x . callback ( e ) ) ;
exports . internal . dispatcher . default . dispatch _original (
e
) ;
} ;
2018-05-20 03:51:19 +00:00
// add our avatar -- credit to block for finding this method
2018-07-12 02:17:42 +00:00
wc . findFunc ( "clyde" ) [ 0 ] . exports . BOT _AVATARS . EndPwn =
"https://cdn.discordapp.com/avatars/350987786037493773/ae0a2f95898cfd867c843c1290e2b917.png" ;
2018-05-20 03:51:19 +00:00
// dont try loading plugins in lite mode
if ( internal . lite ) {
2018-07-12 02:17:42 +00:00
} else {
2018-05-20 03:51:19 +00:00
// load styles
2018-07-12 02:17:42 +00:00
if ( fs . existsSync ( exports . data + "/styles" ) ) {
fs
. readdirSync ( exports . data + "/styles" )
. forEach ( x => {
internal . print ( "loading /styles/" + x ) ;
if ( x . endsWith ( ".css" ) ) {
var style = document . createElement (
"style"
) ;
style . type = "text/css" ;
style . innerHTML = fs
. readFileSync (
exports . data + "/styles/" + x
)
. toString ( ) ;
document . head . appendChild ( style ) ;
}
} ) ;
2018-05-20 03:51:19 +00:00
}
// load plugins...
2018-07-12 02:17:42 +00:00
if ( fs . existsSync ( exports . data + "/plugins" ) ) {
fs
. readdirSync ( exports . data + "/plugins" )
. forEach ( x => {
if ( x . endsWith ( ".js" ) ) {
try {
var plugin = krequire ( x ) ;
if ( plugin . start !== undefined ) {
internal . print (
"loading /plugins/" + x
) ;
plugin . start ( ) ;
} else {
internal . print (
"/plugins/" +
x +
" does not export start(), ignoring..."
) ;
}
} catch ( e ) {
internal . error (
e ,
x +
" failed to initialize properly"
) ;
warning ++ ;
2018-05-20 03:51:19 +00:00
}
}
2018-07-12 02:17:42 +00:00
} ) ;
2018-06-13 22:25:08 +00:00
}
2018-05-20 03:51:19 +00:00
// execute autoruns...
2018-07-12 02:17:42 +00:00
if ( fs . existsSync ( exports . data + "/autorun" ) ) {
fs
. readdirSync ( exports . data + "/autorun" )
. forEach ( x => {
if ( x . endsWith ( ".js" ) ) {
try {
internal . print (
"executing /autorun/" + x
) ;
kinclude (
exports . data + "/autorun/" + x
) ;
} catch ( e ) {
internal . error (
e ,
x +
" failed to execute properly"
) ;
warning ++ ;
}
2018-05-20 03:51:19 +00:00
}
2018-07-12 02:17:42 +00:00
} ) ;
2018-05-20 03:51:19 +00:00
}
}
setTimeout ( ( ) => {
if ( window . jQuery )
2018-07-12 02:17:42 +00:00
internal . crash (
"EndPwn is not compatible with jQuery"
) ;
2018-05-20 03:51:19 +00:00
} , 2000 ) ;
// display a message if any plugins failed to load
if ( warning )
2018-07-12 02:17:42 +00:00
internal . alert (
` ${ warning } file ${
warning > 1 ? "s" : ""
} failed to load . Check the console for details . ` ,
"Plugin failure"
) ;
2018-05-20 03:51:19 +00:00
// print the about message to the console
2018-07-12 02:17:42 +00:00
if ( ! internal . silent ) exports . about ( ) ;
2018-05-20 03:51:19 +00:00
// dispatch the ep-ready event, we're all done here
$dispatch ( internal . events . onReady ( ) ) ;
2018-07-12 02:17:42 +00:00
} catch ( ex ) {
2018-05-20 03:51:19 +00:00
internal . crash ( ex ) ;
}
} else {
// discord isnt ready, wait a bit and try again
2018-07-12 02:17:42 +00:00
internal . print ( "Discord not ready, waiting 1 second..." ) ;
2018-05-20 03:51:19 +00:00
setTimeout ( arguments . callee , 1000 ) ;
}
} ,
hooks : [ ]
2018-07-12 02:17:42 +00:00
} ;
2018-05-20 03:51:19 +00:00
exports = {
// new version data
version : {
major : 5 ,
minor : 6 ,
2018-07-12 02:17:42 +00:00
revision : 46 , // TODO: find a better way of incrementing/calculating the revision; the current way is fucking ridiculous (manually editing)
2018-05-20 03:51:19 +00:00
2018-07-12 02:17:42 +00:00
toString : function ( ) {
2018-05-20 03:51:19 +00:00
return ` v ${ this . major } . ${ this . minor } . ${ this . revision } ` ;
}
} ,
// the first ever export added to epapi, originally as a test -- kept in mainline because i havent had the heart to remove it
2018-07-12 02:17:42 +00:00
xyzzy : "Nothing happened." ,
2018-05-20 03:51:19 +00:00
// display info
// ugly code, should probably be made prettier at some point
// oh no i made it even uglier
2018-07-12 02:17:42 +00:00
about : function ( ) {
console . log (
2018-07-12 02:49:21 +00:00
` %cLλmbda%c
2018-07-12 02:17:42 +00:00
$ { this . bootstrap . name ? this . bootstrap . name : "unknown" } $ {
this . bootstrap . version ? ` ${ this . bootstrap . version } ` : ""
} $ { this . bootstrap . method ? ` ( ${ this . bootstrap . method } ) ` : "" }
EPAPI $ { this . version } $ { window . crispr ? ` , CRISPR ${ window . crispr . version } ` : "" }
https : //xn--lmbda-2be.cf/
https : //discord.gg/6dKCZxn`,
2018-07-12 02:49:21 +00:00
"background:linear-gradient(to bottom right,#c0ff80,#c080ff);-webkit-background-clip:text;-webkit-text-fill-color:transparent;font-size:48px;font-family:sans-serif" ,
2018-07-12 02:17:42 +00:00
""
) ;
2018-05-20 03:51:19 +00:00
} ,
// get the lite status
get lite ( ) {
return internal . lite ;
} ,
/ *
2018-06-13 22:25:08 +00:00
2018-05-20 03:51:19 +00:00
entrypoint arguments :
2018-06-13 22:25:08 +00:00
2018-05-20 03:51:19 +00:00
bootstrap ( object ) : bootstrap properties
keys :
name ( string ) : name of bootstrap
version ( string or object ) : version of bootstrap
method ( string ) : name of stage 1 method in use
lite ( bool ) : disables node dependence ( for web browsers )
silent ( bool ) : dont display about ( ) after initialization
brand ( bool ) : enables the sigma wordmark replacement
secure ( bool ) : enables security features like permissions
2018-06-13 22:25:08 +00:00
native ( bool ) : informs epapi that we have access to native require ( )
2018-05-20 03:51:19 +00:00
all keys are optional , boolean values are assumed false if not provided
2018-06-13 22:25:08 +00:00
2018-05-20 03:51:19 +00:00
please do not call this method unless you are a bootstrap
2018-06-13 22:25:08 +00:00
2018-05-20 03:51:19 +00:00
* /
2018-07-12 02:17:42 +00:00
go : async function ( bootstrap , silent , brand , lite ) {
if (
location . hostname . indexOf ( "discordapp" ) == - 1 &&
location . hostname . indexOf ( "dr1ft.xyz" ) == - 1
)
return ;
2018-05-20 03:51:19 +00:00
try {
2018-07-12 02:17:42 +00:00
internal . print ( "starting up..." ) ;
2018-05-20 03:51:19 +00:00
// figure out which calling convention is being used
switch ( typeof bootstrap ) {
// new bootstrap using bootstrap properties object instead of separate arguments
2018-07-12 02:17:42 +00:00
case "object" :
2018-05-20 03:51:19 +00:00
exports . bootstrap = bootstrap ;
internal . lite = bootstrap . lite ? true : false ;
internal . silent = bootstrap . silent ? true : false ;
internal . brand = bootstrap . brand ? true : false ;
2018-06-13 22:25:08 +00:00
internal . native = bootstrap . native ? true : false ;
2018-05-20 03:51:19 +00:00
break ;
// older bootstrap
2018-07-12 02:17:42 +00:00
case "string" :
2018-05-20 03:51:19 +00:00
exports . bootstrap = {
name : bootstrap
} ;
internal . lite = lite ? true : false ; // dont use node integration
internal . silent = silent ? true : false ;
internal . brand = brand ? true : false ;
break ;
// really old bootstrap, or the bootstrap is doing something stupid we dont expect
default :
if ( window . _epmethod === undefined ) {
2018-07-12 02:17:42 +00:00
exports . method = "unknown" ;
2018-05-20 03:51:19 +00:00
} else {
exports . method = _epmethod ;
}
break ;
}
2018-07-12 02:17:42 +00:00
internal . print ( "detected calling convention" ) ;
2018-05-20 03:51:19 +00:00
// prepare the global namespace
2018-07-12 02:17:42 +00:00
internal . print ( "preparing the global namespace..." ) ;
2018-05-20 03:51:19 +00:00
await internal . prepare ( ) ;
// dispatch ep-prepared to let the bootstrap know that the global namespace is ready
$dispatch ( internal . events . onPrepared ( ) ) ;
if ( ! internal . lite ) {
// determine the root path where plugins and files will be found
2018-07-12 02:17:42 +00:00
exports . data =
app . getPath ( "userData" ) . replace ( /\\/g , "/" ) + "/" ;
internal . print ( "data path " + exports . data ) ;
2018-05-20 03:51:19 +00:00
// icon by toxoid49b, tweaked by me
if ( internal . brand ) {
2018-07-12 02:17:42 +00:00
internal . setSigmaColor ( "#c080ff" ) ;
2018-05-20 03:51:19 +00:00
}
}
// start trying to init
2018-07-12 02:17:42 +00:00
internal . print ( "starting init loop..." ) ;
2018-05-20 03:51:19 +00:00
setTimeout ( internal . init , 0 ) ;
// undefine the entrypoint to avoid getting double-called
delete exports . go ;
2018-07-12 02:17:42 +00:00
} catch ( ex ) {
2018-05-20 03:51:19 +00:00
// something bad happened, undefine $api and display a message
internal . crash ( ex ) ;
}
} ,
// methods for committing data to settings.json
settings : {
// get a value in the settings.json object
2018-07-12 02:17:42 +00:00
get : function ( k ) {
2018-05-20 03:51:19 +00:00
if ( internal . lite )
2018-07-12 02:17:42 +00:00
internal . warn (
"Something tried retrieving data from settings.json, but we are running in lite mode! Returning undefined..."
) ;
2018-05-20 03:51:19 +00:00
else
2018-07-12 02:17:42 +00:00
return JSON . parse (
fs . readFileSync ( exports . data + "/settings.json" , "utf8" )
) [ k ] ;
2018-05-20 03:51:19 +00:00
} ,
// set a value in the settings.json object
2018-07-12 02:17:42 +00:00
set : function ( k , v ) {
2018-05-20 03:51:19 +00:00
if ( internal . lite ) {
2018-07-12 02:17:42 +00:00
internal . warn (
"Something tried putting data into settings.json, but we are running in lite mode! Doing nothing..."
) ;
} else {
var o = JSON . parse (
fs . readFileSync ( exports . data + "/settings.json" , "utf8" )
) ;
2018-05-20 03:51:19 +00:00
o [ k ] = v ;
2018-07-12 02:17:42 +00:00
fs . writeFileSync (
exports . data + "/settings.json" ,
JSON . stringify ( o , null , 2 )
) ;
2018-05-20 03:51:19 +00:00
return v ;
}
}
} ,
// localStorage stuff
localStorage : {
// get a value from localStorage
2018-07-12 02:17:42 +00:00
get : function ( k ) {
2018-05-20 03:51:19 +00:00
return exports . internal . objectStorage . impl . get ( k ) ;
} ,
// set a value in localStorage
2018-07-12 02:17:42 +00:00
set : function ( k , v ) {
2018-05-20 03:51:19 +00:00
return exports . internal . objectStorage . impl . set ( k , v ) ;
} ,
// remove a value from localStorage
2018-07-12 02:17:42 +00:00
remove : function ( k ) {
2018-05-20 03:51:19 +00:00
return exports . internal . objectStorage . impl . remove ( k ) ;
}
} ,
// utility functions
util : {
// BUG: wrap and its sister function both fuck things up that use `this`
// i know exactly why this happens, but not the slightest clue how to fix it
// manual wrapping is necessary in some cases because of this
//
// trying to use these on any function that uses `this` will fuck that function
// dont do it
// intercept a function's arguments
2018-07-12 02:17:42 +00:00
wrap : function ( target1 , callback ) {
2018-05-20 03:51:19 +00:00
// for security; we're evaluating an untrusted expression in the local scope here
2018-06-13 22:25:08 +00:00
//var internal = {};
2018-05-20 03:51:19 +00:00
// get the original function
var orig = evaluate ( target1 ) ;
// the stub we will overwrite the function with
function stub ( ) {
// what we will pass to the original function
var args ;
try {
// call the wrapper and get our args
args = callback . apply ( null , arguments ) ;
2018-07-12 02:17:42 +00:00
} catch ( e ) {
internal . error (
e ,
"A function wrapper threw an exception"
) ;
2018-05-20 03:51:19 +00:00
// dont completely break the function if there's a flaw in the wrapper
args = arguments ;
}
// returning undefined results in the function call being suppressed
2018-07-12 02:17:42 +00:00
if ( typeof args != "undefined" ) {
2018-05-20 03:51:19 +00:00
// call the original function
2018-07-12 02:17:42 +00:00
return orig . apply ( null , args ) ;
2018-05-20 03:51:19 +00:00
}
}
stub . original = orig ;
stub . callback = callback ;
callback = callback . bind ( stub ) ;
// do the overwriting thing
eval ( ` ${ target1 } =stub ` ) ;
} ,
// intercept a function's return value
2018-07-12 02:17:42 +00:00
wrapAfter : function ( target1 , callback ) {
2018-06-13 22:25:08 +00:00
//var internal = {};
2018-05-20 03:51:19 +00:00
// get the original function
var orig = evaluate ( target1 ) ;
// the stub we will overwrite the function with
function stub ( ) {
// call the original argument
var r = orig . apply ( null , arguments ) ;
try {
// call the wrapper and return its return value
return callback ( r ) ;
2018-07-12 02:17:42 +00:00
} catch ( e ) {
internal . error (
e ,
"A function wrapper threw an exception"
) ;
2018-05-20 03:51:19 +00:00
// again, dont fuck stuff up if there's a flaw in the wrapper
return r ;
}
}
stub . original = orig ;
stub . callback = callback ;
callback = callback . bind ( stub ) ;
// overwrite that shit
eval ( ` ${ target1 } =stub ` ) ;
} ,
// extended findFunc that automatically narrows down results
2018-07-12 02:17:42 +00:00
findFuncExports : function ( s , e ) {
if ( s === undefined )
throw Error ( "must provide a search string" ) ;
2018-05-20 03:51:19 +00:00
if ( e === undefined ) e = s ;
2018-07-12 02:17:42 +00:00
var results = wc
. findFunc ( s )
. filter (
x =>
x !== undefined &&
x . exports !== undefined &&
x . exports [ e ] !== undefined
) ;
2018-05-20 03:51:19 +00:00
if ( results . length == 0 )
2018-07-12 02:17:42 +00:00
throw Error ( "findFuncExports() found no matches" ) ;
2018-05-20 03:51:19 +00:00
if ( results . length > 1 )
2018-07-12 02:17:42 +00:00
internal . warn (
"findFuncExports() found more than one match"
) ;
2018-05-20 03:51:19 +00:00
return results [ 0 ] . exports ;
} ,
2018-07-12 02:17:42 +00:00
findConstructor : function ( s , e ) {
if ( s === undefined )
throw Error ( "must provide a search string" ) ;
var results =
e !== undefined
? wc
. findFunc ( s )
. filter (
x =>
x !== undefined &&
x . exports !== undefined &&
x . exports [ e ] !== undefined
)
: wc . findFunc ( s ) ;
2018-05-20 03:51:19 +00:00
if ( results . length == 0 )
2018-07-12 02:17:42 +00:00
throw Error ( "findConstructor() found no matches" ) ;
2018-05-20 03:51:19 +00:00
if ( results . length > 1 )
2018-07-12 02:17:42 +00:00
internal . warn (
"findConstructor() found more than one match"
) ;
2018-05-20 03:51:19 +00:00
return mArr [ results [ 0 ] . i ] ;
}
} ,
// discord internal modules exposed with webcrack, commented out lines' purposes have been forgotten
internal : {
2018-07-12 02:17:42 +00:00
get constants ( ) {
return wc . findCache ( "API_HOST" ) [ 0 ] . exports ;
} ,
get dispatcher ( ) {
return wc
. findCache ( "Dispatcher" )
. filter (
x =>
x . exports !== undefined &&
x . exports . Store === undefined &&
x . exports . default !== undefined
) [ 0 ] . exports ;
} ,
2018-05-20 03:51:19 +00:00
//get evnt() { wc.findFunc('MESSAGE_CREATE')[1].exports },
2018-07-12 02:17:42 +00:00
get messageUI ( ) {
return exports . util . findFuncExports ( "receiveMessage" ) ;
} ,
get messageCreation ( ) {
return exports . util . findFuncExports ( "createMessage" ) ;
} ,
get notification ( ) {
return exports . util . findFuncExports (
"showNotification" ,
"setTTSType"
) ;
} ,
2018-05-20 03:51:19 +00:00
//get hguild() { wc.findFunc('leaveGuild')[0].exports },
//get lguild() { wc.findFunc('markGuildAsRead')[0].exports },
2018-07-12 02:17:42 +00:00
get objectStorage ( ) {
return wc . findCache ( "ObjectStorage" ) [ 0 ] . exports ;
} ,
get user ( ) {
return wc . findCache ( "getUser" ) [ 0 ] . exports ;
} ,
2018-05-20 03:51:19 +00:00
2018-07-12 02:17:42 +00:00
getId : ( ) => wc . findCache ( "getId" ) [ 0 ] . exports . getId ( )
2018-05-20 03:51:19 +00:00
} ,
// discord internal events stuff
// $api.event.* is has no use outside of epapi internal things
// as such, it has been moved to an internal object
events : {
// $listen('ep-native') without all the fuss
2018-07-12 02:17:42 +00:00
listen : function ( type , callback ) {
if ( type === undefined )
throw Error ( "must provide an event type" ) ;
if ( callback === undefined )
throw Error ( "must provide a callback" ) ;
2018-05-20 03:51:19 +00:00
2018-07-12 02:17:42 +00:00
return $listen ( "ep-native" , e => {
2018-05-20 03:51:19 +00:00
if ( e . detail . type == type ) {
callback ( e . detail ) ;
}
} ) ;
} ,
// forge an event and dispatch it
2018-07-12 02:17:42 +00:00
dispatch : function ( event ) {
if ( event . type === undefined )
throw Error ( "must provide an event type" ) ;
2018-05-20 03:51:19 +00:00
return exports . internal . dispatcher . default . dirtyDispatch ( event ) ;
} ,
// intercept and modify an event before it can go anywhere
2018-07-12 02:17:42 +00:00
hook : function ( type , callback ) {
if ( type === undefined )
throw Error ( "must provide an event type" ) ;
if ( callback === undefined )
throw Error ( "must provide a callback" ) ;
2018-05-20 03:51:19 +00:00
var newHook = {
type : type ,
callback : callback ,
unregister : ( ) => {
var i = internal . hooks . indexOf ( newHook ) ;
2018-07-12 02:17:42 +00:00
if ( i > - 1 ) internal . hooks . splice ( i , 1 ) ;
2018-05-20 03:51:19 +00:00
}
} ;
internal . hooks . push ( newHook ) ;
return newHook ;
}
} ,
// discord api stuff
discord : {
// take a wild guess
2018-07-12 02:17:42 +00:00
rest : async function ( method , endpoint , body , c ) {
2018-05-20 03:51:19 +00:00
// the url we will be making our request to
var url = "https://discordapp.com/api/v6" + endpoint ;
// fetch() options
var options = {
headers : {
// get the token from localStorage as our auth header
2018-07-12 02:17:42 +00:00
Authorization : exports . internal . objectStorage . impl . get (
"token"
) ,
"Content-type" : "application/json"
2018-05-20 03:51:19 +00:00
} ,
method : method
} ;
// if the body isnt already a string go ahead and stringify it
2018-07-12 02:17:42 +00:00
if ( typeof body !== "string" ) body = JSON . stringify ( body ) ;
2018-05-20 03:51:19 +00:00
// probably not the best way to handle this
2018-07-12 02:17:42 +00:00
if ( method !== "GET" ) options . body = body ;
2018-05-20 03:51:19 +00:00
// fetch
var r = await fetch ( url , options ) ;
2018-07-12 02:17:42 +00:00
if ( ! r . ok ) throw Error ( await r . text ( ) ) ;
2018-05-20 03:51:19 +00:00
// urgh, theyre using callbacks
if ( c !== undefined ) {
// warn the dev
2018-07-12 02:17:42 +00:00
internal . warn (
"Using callbacks in REST calls is deprecated and may be removed in a future release."
) ;
2018-05-20 03:51:19 +00:00
// operate like EPAPI =<5.0
c ( await r . text ( ) ) ;
2018-07-12 02:17:42 +00:00
} else {
2018-05-20 03:51:19 +00:00
return await r . json ( ) ;
}
} ,
// mark a message read
2018-07-12 02:17:42 +00:00
ack : function ( channel , id ) {
return this . rest (
"POST" ,
` /channels/ ${ channel } /messages/ ${ id } /ack `
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
sendMessage : function ( channel , text ) {
return this . rest ( "POST" , ` /channels/ ${ channel } /messages ` , {
content : text
} ) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
sendEmbed : function ( channel , ebd ) {
return this . rest ( "POST" , ` /channels/ ${ channel } /messages ` , {
embed : ebd
} ) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
getGuild : function ( id , c ) {
return this . rest (
"GET" ,
` /guilds/ ${ id } ` ,
"" ,
c ? e => c ( JSON . parse ( e ) ) : undefined
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
getChannel : function ( id , c ) {
return this . rest (
"GET" ,
"/channels/" + id ,
"" ,
c ? e => c ( JSON . parse ( e ) ) : undefined
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
getUser : function ( id ) {
2018-05-20 03:51:19 +00:00
return exports . internal . user . getUser ( id ) ;
} ,
2018-07-12 02:17:42 +00:00
getCurrentUser : function ( ) {
2018-05-20 03:51:19 +00:00
return exports . internal . user . getCurrentUser ( ) ;
} ,
2018-07-12 02:17:42 +00:00
getGuildRoles : function ( id , c ) {
return this . rest (
"GET" ,
` /guilds/ ${ id } /roles ` ,
"" ,
c ? e => c ( JSON . parse ( e ) ) : undefined
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
getGuildChannels : function ( id , c ) {
return this . rest (
"GET" ,
` /guilds/ ${ id } /channels ` ,
"" ,
c ? e => c ( JSON . parse ( e ) ) : undefined
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
getGuildUser : function ( guildid , userid , c ) {
return this . rest (
"GET" ,
` /guilds/ ${ guildid } /members/ ${ userid } ` ,
"" ,
c ? e => c ( JSON . parse ( e ) ) : undefined
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
getGuildUsers : function ( id , c ) {
return this . rest (
"GET" ,
` /guilds/ ${ id } /members?limit=1000 ` ,
"" ,
c ? e => c ( JSON . parse ( e ) ) : undefined
) ;
2018-05-20 03:51:19 +00:00
}
} ,
// ui stuff, including pulling data from the path for some reason
ui : {
// navigate
2018-07-12 02:17:42 +00:00
transitionTo : function ( path ) {
wc . findCache ( "transitionTo" ) [ 0 ] . exports . transitionTo ( path ) ;
2018-05-20 03:51:19 +00:00
} ,
// focus discord
2018-07-12 02:17:42 +00:00
focus : function ( ) {
2018-05-20 03:51:19 +00:00
electron . getCurrentWindow ( ) . focus ( ) ;
} ,
// pull the channel id from the url
2018-07-12 02:17:42 +00:00
getCurrentChannel : function ( ) {
var p = window . location . pathname . split ( "/" ) ;
2018-05-20 03:51:19 +00:00
return p [ p . length - 1 ] ;
} ,
// pull the guild id from the url
2018-07-12 02:17:42 +00:00
getCurrentGuild : function ( ) {
var p = window . location . pathname . split ( "/" ) ;
2018-05-20 03:51:19 +00:00
return p [ p . length - 2 ] ;
} ,
// creates a fake message in the current channel (like clyde)
2018-07-12 02:17:42 +00:00
fakeMsg : function ( t , f ) {
var msg = exports . internal . messageCreation . createMessage (
this . getCurrentChannel ( ) ,
t
) ;
msg . author . avatar = "EndPwn" ;
2018-05-20 03:51:19 +00:00
msg . author . bot = true ;
2018-07-12 02:17:42 +00:00
msg . author . discriminator = "0000" ;
msg . author . id = "1" ;
msg . author . username = "EndPwn" ;
msg . state = "SENT" ;
if ( typeof f != "undefined" ) {
2018-05-20 03:51:19 +00:00
f ( msg ) ;
}
2018-07-12 02:17:42 +00:00
exports . internal . messageUI . receiveMessage (
this . getCurrentChannel ( ) ,
msg
) ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
hideChannels : function ( ) {
$ ( '[class^="channels"]' ) . style . display = "none" ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
showChannels : function ( ) {
$ ( '[class^="channels"]' ) . style . display = "" ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
hideServers : function ( ) {
$ ( ".guilds-wrapper" ) . style . display = "none" ;
2018-05-20 03:51:19 +00:00
} ,
2018-07-12 02:17:42 +00:00
showServers : function ( ) {
$ ( ".guilds-wrapper" ) . style . display = "" ;
2018-05-20 03:51:19 +00:00
} ,
/ * h i d e T o o l b a r : f u n c t i o n ( ) {
$ ( '.topic' ) . style . display = 'none' ;
$ ( '.header-toolbar' ) . style . display = 'none' ;
} ,
2018-06-13 22:25:08 +00:00
2018-05-20 03:51:19 +00:00
showToolbar : function ( ) {
$ ( '.topic' ) . style . display = '' ;
$ ( '.header-toolbar' ) . style . display = '' ;
} , * /
2018-07-12 02:17:42 +00:00
toggleUsers : function ( ) {
wc . findFunc ( "toggleSection" ) [ 1 ] . exports . TOGGLE _USERS . action ( ) ;
2018-05-20 03:51:19 +00:00
} ,
// display a dialog box
2018-07-12 02:17:42 +00:00
showDialog : function ( x ) {
// for example, $api.ui.showDialog({title: 'Pwnt!', body: 'It works!'})
exports . util
. findFuncExports ( "e.onConfirmSecondary" , "show" )
. show ( x ) ;
2018-05-20 03:51:19 +00:00
} ,
// display a banner at the top of the app
2018-07-12 02:17:42 +00:00
showNotice : function ( text , button ) {
exports . util
. findFuncExports ( "ActionTypes.NOTICE_SHOW" , "show" )
. show ( "GENERIC" , text , button , ( ) => { } , 0 ) ;
2018-05-20 03:51:19 +00:00
} ,
// get profile notes for a user
2018-07-12 02:17:42 +00:00
getNote : function ( id ) {
return exports . util
. findFuncExports ( "getNote" , "_actionHandlers" )
. getNote ( id ) ;
2018-05-20 03:51:19 +00:00
} ,
get focused ( ) {
return electron . getCurrentWindow ( ) . isFocused ( ) ;
}
}
2018-07-12 02:17:42 +00:00
} ;
2018-05-20 03:51:19 +00:00
2018-07-12 02:17:42 +00:00
if ( typeof module != "undefined" ) module . exports = exports ;
} ) ( ) ;