1
0

main.js 254 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208
  1. require('dotenv').config();
  2. const util = require('util');
  3. util.inspect.defaultOptions = {compact:false,breakLength:Infinity};
  4. var isDebug = ( process.argv[2] === 'debug' );
  5. var ready = {
  6. patreons: false,
  7. voice: false,
  8. allSites: true
  9. }
  10. const Discord = require('discord.js');
  11. const DBL = require('dblapi.js');
  12. const request = require('request').defaults( {
  13. headers: {
  14. 'User-Agent': 'Wiki-Bot/' + ( isDebug ? 'testing' : process.env.npm_package_version ) + ' (Discord; ' + process.env.npm_package_name + ')'
  15. }
  16. } );
  17. const htmlparser = require('htmlparser2');
  18. const cheerio = require('cheerio');
  19. const sqlite3 = require('sqlite3').verbose();
  20. var db = new sqlite3.Database( './wikibot.db', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, dberror => {
  21. if ( dberror ) {
  22. console.log( '- Error while connecting to the database: ' + dberror );
  23. return dberror;
  24. }
  25. console.log( '- Connected to the database.' );
  26. } );
  27. var cookieJar = request.jar();
  28. function gpWikiLogin() {
  29. request( {
  30. uri: 'https://help.gamepedia.com/api.php?action=query&meta=tokens&type=login&format=json',
  31. json: true,
  32. jar: cookieJar
  33. }, function( error, response, body ) {
  34. if ( error || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.tokens ) {
  35. console.log( '- ' + ( response && response.statusCode ) + ': Error while gettings the login token: ' + ( error || body && body.error && body.error.info ) );
  36. return;
  37. }
  38. console.log( '- Gamepedia: Token successfully fetched.' );
  39. request.post( {
  40. uri: 'https://help.gamepedia.com/api.php',
  41. form: {
  42. action: 'login',
  43. lgname: process.env.gpusername,
  44. lgpassword: process.env.gppassword,
  45. lgtoken: body.query.tokens.logintoken,
  46. format: 'json'
  47. },
  48. json: true,
  49. jar: cookieJar
  50. }, function( loginerror, loginresponse, loginbody ) {
  51. if ( loginerror || !loginresponse || loginresponse.statusCode !== 200 || !loginbody || !loginbody.login || loginbody.login.result !== 'Success' ) {
  52. console.log( '- ' + ( loginresponse && loginresponse.statusCode ) + ': Error while logging in: ' + ( loginerror || loginbody && ( ( loginbody.login && loginbody.login.result ) || loginbody.error && loginbody.error.info ) ) );
  53. return;
  54. }
  55. console.log( '- Gamepedia: Successfully logged in as ' + loginbody.login.lgusername + '.' );
  56. } );
  57. } );
  58. }
  59. if ( process.env.gpusername && process.env.gppassword ) gpWikiLogin();
  60. function fWikiLogin(token) {
  61. request.post( {
  62. uri: 'https://community.fandom.com/api.php',
  63. form: {
  64. action: 'login',
  65. lgname: process.env.fusername,
  66. lgpassword: process.env.fpassword,
  67. lgtoken: token,
  68. format: 'json'
  69. },
  70. json: true,
  71. jar: cookieJar
  72. }, function( loginerror, loginresponse, loginbody ) {
  73. if ( loginerror || !loginresponse || loginresponse.statusCode !== 200 || !loginbody || !loginbody.login || loginbody.login.result !== 'Success' ) {
  74. if ( loginbody && loginbody.login && loginbody.login.result === 'NeedToken' ) {
  75. console.log( '- Fandom: Token successfully fetched.' );
  76. return fWikiLogin(loginbody.login.token);
  77. }
  78. console.log( '- ' + ( loginresponse && loginresponse.statusCode ) + ': Error while logging in: ' + ( loginerror || loginbody && ( ( loginbody.login && loginbody.login.result ) || loginbody.error && loginbody.error.info ) ) );
  79. return;
  80. }
  81. console.log( '- Fandom: Successfully logged in as ' + loginbody.login.lgusername + '.' );
  82. } );
  83. }
  84. if ( process.env.fusername && process.env.fpassword ) fWikiLogin();
  85. var client = new Discord.Client( {
  86. messageCacheLifetime: 600,
  87. messageSweepInterval: 6000,
  88. disableEveryone: true,
  89. disabledEvents: ["TYPING_START"]
  90. } );
  91. const dbl = new DBL(process.env.dbltoken);
  92. var i18n = require('./i18n/allLangs.json');
  93. Object.keys(i18n.allLangs[1]).forEach( lang => i18n[lang] = require('./i18n/' + lang + '.json') );
  94. const minecraft = require('./minecraft.json');
  95. const multiManager = require('./wiki_manager.json');
  96. var pause = {};
  97. var stop = false;
  98. const defaultPermissions = new Discord.Permissions(268954688).toArray();
  99. const timeoptions = {
  100. year: 'numeric',
  101. month: 'short',
  102. day: 'numeric',
  103. hour: '2-digit',
  104. minute: '2-digit',
  105. timeZone: 'UTC',
  106. timeZoneName: 'short'
  107. }
  108. const defaultSettings = {
  109. lang: "en",
  110. wiki: "https://community.fandom.com/"
  111. }
  112. var patreons = {};
  113. function getSettings(trysettings = 1) {
  114. db.each( 'SELECT guild, prefix FROM discord WHERE patreon IS NOT NULL', [], (dberror, row) => {
  115. if ( dberror ) {
  116. console.log( '- ' + trysettings + '. Error while getting the patreon: ' + dberror );
  117. if ( trysettings < 10 ) {
  118. trysettings++;
  119. getSettings(trysettings);
  120. }
  121. return dberror;
  122. }
  123. patreons[row.guild] = row.prefix;
  124. }, (dberror) => {
  125. if ( dberror ) {
  126. console.log( '- ' + trysettings + '. Error while getting the patreons: ' + dberror );
  127. if ( dberror.message === 'SQLITE_ERROR: no such table: discord' ) db.serialize( () => {
  128. db.run( 'CREATE TABLE IF NOT EXISTS patreons(patreon TEXT PRIMARY KEY UNIQUE NOT NULL, count INTEGER NOT NULL)', [], function (error) {
  129. if ( error ) {
  130. console.log( '- Error while creating the patreons table: ' + error );
  131. return error;
  132. }
  133. console.log( '- Created the patreons table.' );
  134. db.run( 'CREATE INDEX idx_patreons_patreon ON patreons(patreon)', [], function (idxerror) {
  135. if ( idxerror ) {
  136. console.log( '- Error while creating the patreons index: ' + idxerror );
  137. return error;
  138. }
  139. console.log( '- Created the patreons index.' );
  140. } );
  141. } );
  142. db.run( 'CREATE TABLE IF NOT EXISTS discord(guild TEXT NOT NULL, channel TEXT, lang TEXT NOT NULL DEFAULT [' + defaultSettings.lang + '], wiki TEXT NOT NULL DEFAULT [' + defaultSettings.wiki + '], prefix TEXT NOT NULL DEFAULT [' + process.env.prefix + '], patreon TEXT, voice INTEGER, inline INTEGER, UNIQUE(guild, channel), FOREIGN KEY(patreon) REFERENCES patreons(patreon) ON DELETE SET NULL)', [], function (error) {
  143. if ( error ) {
  144. console.log( '- Error while creating the discord table: ' + error );
  145. return error;
  146. }
  147. console.log( '- Created the discord table.' );
  148. db.run( 'CREATE TRIGGER unique_discord_guild BEFORE INSERT ON discord WHEN NEW.channel IS NULL BEGIN SELECT CASE WHEN (SELECT 1 FROM discord WHERE guild = NEW.guild AND channel IS NULL) IS NOT NULL THEN RAISE(ABORT, "UNIQUE constraint failed: discord.guild, discord.channel") END; END;', [], function (idxerror) {
  149. if ( idxerror ) {
  150. console.log( '- Error while creating the discord guild trigger: ' + idxerror );
  151. return error;
  152. }
  153. console.log( '- Created the discord guild trigger.' );
  154. } );
  155. db.run( 'CREATE INDEX idx_discord_patreon ON discord(patreon) WHERE patreon IS NOT NULL', [], function (idxerror) {
  156. if ( idxerror ) {
  157. console.log( '- Error while creating the discord patreon index: ' + idxerror );
  158. return error;
  159. }
  160. console.log( '- Created the discord patreon index.' );
  161. } );
  162. db.run( 'CREATE INDEX idx_discord_voice ON discord(voice) WHERE voice IS NOT NULL', [], function (idxerror) {
  163. if ( idxerror ) {
  164. console.log( '- Error while creating the discord voice index: ' + idxerror );
  165. return error;
  166. }
  167. console.log( '- Created the discord voice index.' );
  168. } );
  169. db.run( 'CREATE INDEX idx_discord_channel ON discord(guild, channel DESC)', [], function (idxerror) {
  170. if ( idxerror ) {
  171. console.log( '- Error while creating the discord channel index: ' + idxerror );
  172. return error;
  173. }
  174. console.log( '- Created the discord channel index.' );
  175. } );
  176. if ( trysettings < 10 ) {
  177. trysettings++;
  178. getSettings(trysettings);
  179. }
  180. } );
  181. /*
  182. db.run( 'CREATE TABLE IF NOT EXISTS verification(guild TEXT NOT NULL, channel TEXT, wiki TEXT NOT NULL, role TEXT NOT NULL, editcount INTEGER NOT NULL DEFAULT [0], usergroup TEXT NOT NULL DEFAULT [user])', [], function (error) {
  183. if ( error ) {
  184. console.log( '- Error while creating the verification table: ' + error );
  185. return error;
  186. }
  187. console.log( '- Created the verification table.' );
  188. db.run( 'CREATE INDEX idx_verification_channel ON verification(guild, channel DESC)', [], function (idxerror) {
  189. if ( idxerror ) {
  190. console.log( '- Error while creating the verification index: ' + idxerror );
  191. return error;
  192. }
  193. console.log( '- Created the verification index.' );
  194. } );
  195. } );
  196. */
  197. } );
  198. else {
  199. if ( trysettings < 10 ) {
  200. trysettings++;
  201. getSettings(trysettings);
  202. }
  203. }
  204. return dberror;
  205. }
  206. console.log( '- Patreons successfully loaded.' );
  207. ready.patreons = true;
  208. getVoice();
  209. setStatus();
  210. } );
  211. }
  212. var voice = {};
  213. function getVoice(trysettings = 1) {
  214. db.each( 'SELECT guild, lang FROM discord WHERE voice IS NOT NULL', [], (dberror, row) => {
  215. if ( dberror ) {
  216. console.log( '- ' + trysettings + '. Error while getting the voice channel: ' + dberror );
  217. if ( trysettings < 10 ) {
  218. trysettings++;
  219. getVoice(trysettings);
  220. }
  221. return dberror;
  222. }
  223. voice[row.guild] = row.lang;
  224. }, (dberror) => {
  225. if ( dberror ) {
  226. console.log( '- ' + trysettings + '. Error while getting the voice channels: ' + dberror );
  227. if ( trysettings < 10 ) {
  228. trysettings++;
  229. getVoice(trysettings);
  230. }
  231. return dberror;
  232. }
  233. console.log( '- Voice channels successfully loaded.' );
  234. ready.voice = true;
  235. } );
  236. }
  237. function setStatus(hardreset) {
  238. if ( !ready.patreons ) client.user.setStatus('invisible').catch(log_error);
  239. else if ( hardreset === true ) client.user.setStatus('invisible').then(setStatus, log_error);
  240. else {
  241. client.user.setStatus('online').catch(log_error);
  242. client.user.setActivity( process.env.prefix + ' help' ).catch(log_error);
  243. }
  244. }
  245. var allSites = [];
  246. function getAllSites(callback, ...args) {
  247. ready.allSites = true;
  248. request( {
  249. uri: 'https://help.gamepedia.com/api.php?action=allsites&formatversion=2&do=getSiteStats&filter=wikis|wiki_domain,wiki_display_name,wiki_image,wiki_description,wiki_managers,official_wiki,created&format=json',
  250. json: true
  251. }, function( error, response, body ) {
  252. if ( error || !response || response.statusCode !== 200 || !body || body.status !== 'okay' || !body.data || !body.data.wikis ) {
  253. console.log( '- ' + ( response && response.statusCode ) + ': Error while gettings all sites: ' + ( error || body && body.error && body.error.info ) );
  254. ready.allSites = false;
  255. }
  256. else {
  257. console.log( '- Sites successfully loaded.' );
  258. allSites = JSON.parse(JSON.stringify(body.data.wikis.filter( site => /^[a-z\d-]{1,50}\.gamepedia\.com$/.test(site.wiki_domain) )));
  259. allSites.filter( site => site.wiki_domain in multiManager ).forEach( function(site) {
  260. site.wiki_managers = multiManager[site.wiki_domain].concat(site.wiki_managers).filter( (value, index, self) => self.indexOf(value) === index );
  261. } );
  262. }
  263. if ( callback ) callback(...args);
  264. } );
  265. }
  266. client.on( 'ready', () => {
  267. console.log( '\n- Successfully logged in as ' + client.user.username + '!' );
  268. setStatus();
  269. getSettings();
  270. getAllSites(setStatus);
  271. if ( !isDebug ) client.setInterval( () => {
  272. console.log( '- Current server count: ' + client.guilds.size );
  273. dbl.postStats(client.guilds.size).catch( () => {} );
  274. request.post( {
  275. uri: 'https://discord.bots.gg/api/v1/bots/' + client.user.id + '/stats',
  276. headers: {Authorization: process.env.dbggtoken},
  277. body: {guildCount: client.guilds.size},
  278. json: true
  279. }, () => {} );
  280. }, 10800000 ).unref();
  281. } );
  282. var cmdmap = {
  283. help: cmd_help,
  284. test: cmd_test,
  285. pause: cmd_pause,
  286. invite: cmd_invite,
  287. say: cmd_multiline,
  288. delete: cmd_multiline,
  289. poll: cmd_multiline,
  290. voice: cmd_voice,
  291. settings: cmd_settings,
  292. info: cmd_info,
  293. patreon: cmd_patreon
  294. }
  295. var multilinecmdmap = {
  296. say: cmd_say,
  297. delete: cmd_delete,
  298. poll: cmd_umfrage
  299. }
  300. var ownercmdmap = {
  301. stop: cmd_stop,
  302. pause: cmd_pause,
  303. eval: cmd_eval,
  304. get: cmd_get,
  305. patreon: cmd_patreon
  306. }
  307. var pausecmdmap = {
  308. help: cmd_help,
  309. test: cmd_test,
  310. pause: cmd_pause,
  311. say: cmd_multiline,
  312. delete: cmd_multiline,
  313. voice: cmd_voice,
  314. settings: cmd_settings,
  315. patreon: cmd_patreon
  316. }
  317. var minecraftcmdmap = {
  318. command: minecraft_command2,
  319. bug: minecraft_bug
  320. }
  321. function cmd_helpsetup(lang, msg) {
  322. msg.defaultSettings = false;
  323. msg.replyMsg( lang.settings.missing.replaceSave( '%1$s', '`' + process.env.prefix + ' settings lang`' ).replaceSave( '%2$s', '`' + process.env.prefix + ' settings wiki`' ) );
  324. }
  325. function cmd_settings(lang, msg, args, line, wiki) {
  326. if ( !msg.isAdmin() ) return msg.reactEmoji('❌');
  327. db.all( 'SELECT channel, lang, wiki, prefix, inline FROM discord WHERE guild = ? ORDER BY channel DESC', [msg.guild.id], (error, rows) => {
  328. if ( error ) {
  329. console.log( '- Error while getting the settings: ' + error );
  330. msg.reactEmoji('error', true);
  331. return error;
  332. }
  333. var guild = rows.find( row => !row.channel );
  334. if ( !guild ) guild = Object.assign({prefix: process.env.prefix}, defaultSettings);
  335. var prefix = guild.prefix;
  336. var text = lang.settings.missing.replaceSave( '%1$s', '`' + prefix + ' settings lang`' ).replaceSave( '%2$s', '`' + prefix + ' settings wiki`' );
  337. if ( rows.length ) {
  338. text = lang.settings.current + '\n' + lang.settings.currentlang + ' `' + i18n.allLangs[2][guild.lang] + '` - `' + prefix + ' settings lang`';
  339. if ( msg.guild.id in patreons ) text += '\n' + lang.settings.currentprefix + ' `' + prefix + '` - `' + prefix + ' settings prefix`';
  340. text += '\n' + lang.settings.currentinline + ' ' + ( guild.inline ? '~~' : '' ) + '`[[' + lang.search.page + ']]`' + ( guild.inline ? '~~' : '' ) + ' - `' + prefix + ' settings inline`';
  341. text += '\n' + lang.settings.currentwiki + ' ' + guild.wiki + ' - `' + prefix + ' settings wiki`';
  342. text += '\n' + lang.settings.currentchannel + ' `' + prefix + ' settings channel`\n';
  343. if ( rows.length === 1 ) text += lang.settings.nochannels;
  344. else text += rows.filter( row => row !== guild ).map( row => '<#' + row.channel + '>: ' + ( msg.guild.id in patreons ? '`' + i18n.allLangs[2][row.lang] + '` - ' : '' ) + '<' + row.wiki + '>' + ( msg.guild.id in patreons ? ' - ' + ( row.inline ? '~~' : '' ) + '`[[' + lang.search.page + ']]`' + ( row.inline ? '~~' : '' ) : '' ) ).join('\n');
  345. }
  346. if ( !args.length ) {
  347. return msg.replyMsg( text, {split:true}, true );
  348. }
  349. var prelang = '';
  350. args[0] = args[0].toLowerCase();
  351. if ( args[0] === 'channel' ) {
  352. prelang = 'channel ';
  353. if ( !rows.length ) return msg.replyMsg( text, {split:true}, true );
  354. var channel = rows.find( row => row.channel === msg.channel.id );
  355. if ( !channel ) channel = Object.assign({channel:msg.channel.id}, guild);
  356. text = lang.settings[prelang + 'current'];
  357. if ( msg.guild.id in patreons ) {
  358. text += '\n' + lang.settings.currentlang + ' `' + i18n.allLangs[2][channel.lang] + '` - `' + prefix + ' settings channel lang`';
  359. text += '\n' + lang.settings.currentinline + ' ' + ( channel.inline ? '~~' : '' ) + '`[[' + lang.search.page + ']]`' + ( channel.inline ? '~~' : '' ) + ' - `' + prefix + ' settings channel inline`';
  360. }
  361. text += '\n' + lang.settings.currentwiki + ' ' + channel.wiki + ' - `' + prefix + ' settings channel wiki`';
  362. if ( !args[1] ) return msg.replyMsg( text, {}, true );
  363. args[0] = args[1].toLowerCase();
  364. args[1] = args.slice(2).join(' ').toLowerCase().trim().replace( /^<(.*)>$/, '$1' );
  365. }
  366. else args[1] = args.slice(1).join(' ').toLowerCase().trim().replace( /^<(.*)>$/, '$1' );
  367. if ( args[0] === 'wiki' ) {
  368. prelang += 'wiki';
  369. var wikihelp = '\n' + lang.settings.wikihelp.replaceSave( '%s', prefix + ' settings ' + prelang );
  370. if ( !args[1] ) {
  371. if ( !rows.length ) return msg.replyMsg( lang.settings.wikimissing + wikihelp, {}, true );
  372. else return msg.replyMsg( lang.settings[prelang] + ' ' + ( channel || guild ).wiki + wikihelp, {}, true );
  373. }
  374. var regex = args[1].match( /^(?:https:\/\/)?([a-z\d-]{1,50}\.(?:gamepedia\.com|(?:fandom\.com|wikia\.org)(?:(?!\/wiki\/)\/[a-z-]{1,8})?))(?:\/|$)/ );
  375. if ( !regex ) {
  376. var value = args[1].split(' ');
  377. if ( value.length === 2 && value[1] === '--force' ) return msg.reactEmoji('⏳', true).then( reaction => {
  378. request( {
  379. uri: value[0] + 'api.php?action=query&format=json',
  380. json: true
  381. }, function( ferror, response, body ) {
  382. if ( ferror || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !( body instanceof Object ) ) {
  383. console.log( '- ' + ( response && response.statusCode ) + ': Error while testing the wiki: ' + ( ferror || body && body.error && body.error.info ) );
  384. if ( reaction ) reaction.removeEmoji();
  385. msg.reactEmoji('nowiki', true);
  386. return msg.replyMsg( lang.settings.wikiinvalid + wikihelp, {}, true );
  387. }
  388. var sql = 'UPDATE discord SET wiki = ? WHERE guild = ? AND wiki = ?';
  389. var sqlargs = [value[0], msg.guild.id, guild.wiki];
  390. if ( !rows.length ) {
  391. sql = 'INSERT INTO discord(wiki, guild) VALUES(?, ?)';
  392. sqlargs.pop();
  393. }
  394. if ( channel ) {
  395. sql = 'UPDATE discord SET wiki = ? WHERE guild = ? AND channel = ?';
  396. sqlargs[2] = msg.channel.id;
  397. if ( !rows.includes( channel ) ) {
  398. if ( channel.wiki === value[0] ) {
  399. if ( reaction ) reaction.removeEmoji();
  400. return msg.replyMsg( lang.settings[prelang + 'changed'] + ' ' + channel.wiki + wikihelp, {}, true );
  401. }
  402. sql = 'INSERT INTO discord(wiki, guild, channel, lang, prefix) VALUES(?, ?, ?, ?, ?)';
  403. sqlargs.push(guild.lang, guild.prefix);
  404. }
  405. }
  406. return db.run( sql, sqlargs, function (dberror) {
  407. if ( dberror ) {
  408. console.log( '- Error while editing the settings: ' + dberror );
  409. msg.replyMsg( lang.settings.save_failed, {}, true );
  410. if ( reaction ) reaction.removeEmoji();
  411. return dberror;
  412. }
  413. console.log( '- Settings successfully updated.' );
  414. if ( channel ) channel.wiki = value[0];
  415. else guild.wiki = value[0];
  416. if ( channel || !rows.some( row => row.channel === msg.channel.id ) ) wiki = value[0];
  417. if ( reaction ) reaction.removeEmoji();
  418. msg.replyMsg( lang.settings[prelang + 'changed'] + ' ' + value[0] + wikihelp, {}, true );
  419. var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.inline === guild.inline ).map( row => row.channel );
  420. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( row => '?' ).join('|') + ')', channels, function (delerror) {
  421. if ( delerror ) {
  422. console.log( '- Error while removing the settings: ' + delerror );
  423. return delerror;
  424. }
  425. console.log( '- Settings successfully removed.' );
  426. } );
  427. } );
  428. } );
  429. } );
  430. if ( allSites.some( site => site.wiki_domain === value.join('') + '.gamepedia.com' ) ) {
  431. regex = ['https://' + value.join('') + '.gamepedia.com/',value.join('') + '.gamepedia.com'];
  432. }
  433. else if ( /^(?:[a-z-]{1,8}\.)?[a-z\d-]{1,50}$/.test(value.join('')) ) {
  434. if ( !value.join('').includes( '.' ) ) regex = ['https://' + value.join('') + '.fandom.com/',value.join('') + '.fandom.com'];
  435. else regex = ['https://' + value.join('').split('.')[1] + '.fandom.com/' + value.join('').split('.')[0] + '/',value.join('').split('.')[1] + '.fandom.com/' + value.join('').split('.')[0]];
  436. } else {
  437. var text = lang.settings.wikiinvalid + wikihelp;
  438. var sites = allSites.filter( site => site.wiki_display_name.toLowerCase().includes( value.join(' ') ) );
  439. if ( 0 < sites.length && sites.length < 21 ) {
  440. text += '\n\n' + lang.settings.foundwikis + '\n' + sites.map( site => site.wiki_display_name + ': `' + site.wiki_domain + '`' ).join('\n');
  441. }
  442. return msg.replyMsg( text, {split:true}, true );
  443. }
  444. }
  445. var sql = 'UPDATE discord SET wiki = ? WHERE guild = ? AND wiki = ?';
  446. var sqlargs = ['https://' + regex[1] + '/', msg.guild.id, guild.wiki];
  447. if ( !rows.length ) {
  448. sql = 'INSERT INTO discord(wiki, guild) VALUES(?, ?)';
  449. sqlargs.pop();
  450. }
  451. if ( channel ) {
  452. sql = 'UPDATE discord SET wiki = ? WHERE guild = ? AND channel = ?';
  453. sqlargs[2] = msg.channel.id;
  454. if ( !rows.includes( channel ) ) {
  455. if ( channel.wiki === 'https://' + regex[1] + '/' ) {
  456. return msg.replyMsg( lang.settings[prelang + 'changed'] + ' ' + channel.wiki + wikihelp, {}, true );
  457. }
  458. sql = 'INSERT INTO discord(wiki, guild, channel, lang, prefix) VALUES(?, ?, ?, ?, ?)';
  459. sqlargs.push(guild.lang, guild.prefix);
  460. }
  461. }
  462. return db.run( sql, sqlargs, function (dberror) {
  463. if ( dberror ) {
  464. console.log( '- Error while editing the settings: ' + dberror );
  465. msg.replyMsg( lang.settings.save_failed, {}, true );
  466. return dberror;
  467. }
  468. console.log( '- Settings successfully updated.' );
  469. if ( channel ) channel.wiki = 'https://' + regex[1] + '/';
  470. else guild.wiki = 'https://' + regex[1] + '/';
  471. if ( channel || !rows.some( row => row.channel === msg.channel.id ) ) wiki = 'https://' + regex[1] + '/';
  472. msg.replyMsg( lang.settings[prelang + 'changed'] + ' https://' + regex[1] + '/' + wikihelp, {}, true );
  473. var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.inline === guild.inline ).map( row => row.channel );
  474. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( row => '?' ).join('|') + ')', channels, function (delerror) {
  475. if ( delerror ) {
  476. console.log( '- Error while removing the settings: ' + delerror );
  477. return delerror;
  478. }
  479. console.log( '- Settings successfully removed.' );
  480. } );
  481. } );
  482. }
  483. if ( args[0] === 'lang' ) {
  484. if ( channel && !( msg.guild.id in patreons ) ) return msg.replyMsg( lang.patreon + ' <' + process.env.patreon + '>', {}, true );
  485. prelang += 'lang';
  486. var langhelp = '\n' + lang.settings.langhelp.replaceSave( '%s', prefix + ' settings ' + prelang ) + ' `' + Object.values(i18n.allLangs[1]).join('`, `') + '`';
  487. if ( !args[1] ) {
  488. return msg.replyMsg( lang.settings[prelang] + ' `' + i18n.allLangs[2][( channel || guild ).lang] + '`' + langhelp, {}, true );
  489. }
  490. if ( !( args[1] in i18n.allLangs[0] ) ) {
  491. return msg.replyMsg( lang.settings.langinvalid + langhelp, {}, true );
  492. }
  493. var sql = 'UPDATE discord SET lang = ? WHERE guild = ? AND lang = ?';
  494. var sqlargs = [i18n.allLangs[0][args[1]], msg.guild.id, guild.lang];
  495. if ( !rows.length ) {
  496. sql = 'INSERT INTO discord(lang, guild) VALUES(?, ?)';
  497. sqlargs.pop();
  498. }
  499. if ( channel ) {
  500. sql = 'UPDATE discord SET lang = ? WHERE guild = ? AND channel = ?';
  501. sqlargs[2] = msg.channel.id;
  502. if ( !rows.includes( channel ) ) {
  503. if ( channel.lang === i18n.allLangs[0][args[1]] ) {
  504. return msg.replyMsg( lang.settings[prelang + 'changed'] + ' `' + i18n.allLangs[2][channel.lang] + '`' + langhelp, {}, true );
  505. }
  506. sql = 'INSERT INTO discord(lang, guild, channel, wiki, prefix) VALUES(?, ?, ?, ?, ?)';
  507. sqlargs.push(guild.wiki, guild.prefix);
  508. }
  509. }
  510. return db.run( sql, sqlargs, function (dberror) {
  511. if ( dberror ) {
  512. console.log( '- Error while editing the settings: ' + dberror );
  513. msg.replyMsg( lang.settings.save_failed, {}, true );
  514. return dberror;
  515. }
  516. console.log( '- Settings successfully updated.' );
  517. if ( channel ) channel.lang = i18n[i18n.allLangs[0][args[1]]];
  518. else {
  519. guild.lang = i18n[i18n.allLangs[0][args[1]]];
  520. if ( msg.guild.id in voice ) voice[msg.guild.id] = guild.lang;
  521. }
  522. if ( channel || !( msg.guild.id in patreons ) || !rows.some( row => row.channel === msg.channel.id ) ) lang = i18n[i18n.allLangs[0][args[1]]];
  523. msg.replyMsg( lang.settings[prelang + 'changed'] + ' `' + i18n.allLangs[2][i18n.allLangs[0][args[1]]] + '`\n' + lang.settings.langhelp.replaceSave( '%s', prefix + ' settings ' + prelang ) + ' `' + Object.values(i18n.allLangs[1]).join('`, `') + '`', {}, true );
  524. var channels = rows.filter( row => row.channel && row.lang === lang.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.inline === guild.inline ).map( row => row.channel );
  525. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( row => '?' ).join('|') + ')', channels, function (delerror) {
  526. if ( delerror ) {
  527. console.log( '- Error while removing the settings: ' + delerror );
  528. return delerror;
  529. }
  530. console.log( '- Settings successfully removed.' );
  531. } );
  532. } );
  533. }
  534. if ( args[0] === 'prefix' && !channel ) {
  535. if ( !( msg.guild.id in patreons ) ) {
  536. return msg.replyMsg( lang.patreon + ' <' + process.env.patreon + '>', {}, true );
  537. }
  538. var prefixhelp = '\n' + lang.settings.prefixhelp.replaceSave( '%s', prefix + ' settings prefix' );
  539. if ( !args[1] ) {
  540. return msg.replyMsg( lang.settings.prefix + ' `' + prefix + '`' + prefixhelp, {}, true );
  541. }
  542. if ( args[1].includes( ' ' ) || args[1].includes( '`' ) || args[1].length > 100 ) {
  543. return msg.replyMsg( lang.settings.prefixinvalid + prefixhelp, {}, true );
  544. }
  545. if ( args[1] === 'reset' ) args[1] = process.env.prefix;
  546. var sql = 'UPDATE discord SET prefix = ? WHERE guild = ?';
  547. var sqlargs = [args[1], msg.guild.id];
  548. if ( !rows.length ) {
  549. sql = 'INSERT INTO discord(prefix, guild) VALUES(?, ?)';
  550. }
  551. return db.run( sql, sqlargs, function (dberror) {
  552. if ( dberror ) {
  553. console.log( '- Error while editing the settings: ' + dberror );
  554. msg.replyMsg( lang.settings.save_failed, {}, true );
  555. return dberror;
  556. }
  557. console.log( '- Settings successfully updated.' );
  558. guild.prefix = args[1];
  559. patreons[msg.guild.id] = args[1];
  560. msg.replyMsg( lang.settings.prefixchanged + ' `' + args[1] + '`\n' + lang.settings.prefixhelp.replaceSave( '%s', args[1] + ' settings prefix' ), {}, true );
  561. } );
  562. }
  563. if ( args[0] === 'inline' ) {
  564. if ( channel && !( msg.guild.id in patreons ) ) return msg.replyMsg( lang.patreon + ' <' + process.env.patreon + '>', {}, true );
  565. prelang += 'inline';
  566. var toggle = 'inline ' + ( ( channel || guild ).inline ? 'disabled' : 'enabled' );
  567. var inlinehelp = '\n' + lang.settings[toggle].help.replaceSave( '%1$s', prefix + ' settings ' + prelang + ' toggle' ).replaceSave( /%2\$s/g, lang.search.page );
  568. if ( args[1] !== 'toggle' ) {
  569. return msg.replyMsg( lang.settings[toggle][prelang] + inlinehelp, {}, true );
  570. }
  571. var value = ( ( channel || guild ).inline ? null : 1 );
  572. var sql = 'UPDATE discord SET inline = ? WHERE guild = ?';
  573. var sqlargs = [value, msg.guild.id];
  574. if ( !rows.length ) {
  575. sql = 'INSERT INTO discord(inline, guild) VALUES(?, ?)';
  576. }
  577. if ( channel ) {
  578. sql = 'UPDATE discord SET inline = ? WHERE guild = ? AND channel = ?';
  579. sqlargs.push(msg.channel.id);
  580. if ( !rows.includes( channel ) ) {
  581. sql = 'INSERT INTO discord(inline, guild, channel, wiki, prefix) VALUES(?, ?, ?, ?, ?)';
  582. sqlargs.push(guild.wiki, guild.prefix);
  583. }
  584. }
  585. return db.run( sql, sqlargs, function (dberror) {
  586. if ( dberror ) {
  587. console.log( '- Error while editing the settings: ' + dberror );
  588. msg.replyMsg( lang.settings.save_failed, {}, true );
  589. return dberror;
  590. }
  591. console.log( '- Settings successfully updated.' );
  592. if ( channel ) channel.inline = value;
  593. else guild.inline = value;
  594. toggle = 'inline ' + ( ( channel || guild ).inline ? 'disabled' : 'enabled' );
  595. msg.replyMsg( lang.settings[toggle][prelang + 'changed'] + '\n' + lang.settings[toggle].help.replaceSave( '%1$s', prefix + ' settings ' + prelang + ' toggle' ).replaceSave( /%2\$s/g, lang.search.page ), {}, true );
  596. var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.inline === guild.inline ).map( row => row.channel );
  597. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( row => '?' ).join('|') + ')', channels, function (delerror) {
  598. if ( delerror ) {
  599. console.log( '- Error while removing the settings: ' + delerror );
  600. return delerror;
  601. }
  602. console.log( '- Settings successfully removed.' );
  603. } );
  604. } );
  605. }
  606. return msg.replyMsg( text, {split:true}, true );
  607. } );
  608. }
  609. function cmd_voice(lang, msg, args, line, wiki) {
  610. if ( msg.isAdmin() ) {
  611. if ( !args.join('') ) {
  612. var text = lang.voice.text + '\n`' + lang.voice.channel + ' – <' + lang.voice.name + '>`\n';
  613. text += lang.voice[( msg.guild.id in voice ? 'disable' : 'enable' )].replaceSave( '%s', ( patreons[msg.guild.id] || process.env.prefix ) + ' voice toggle' );
  614. return msg.replyMsg( text, {}, true );
  615. }
  616. args[1] = args.slice(1).join(' ').trim()
  617. if ( args[0].toLowerCase() === 'toggle' && !args[1] ) {
  618. var value = ( msg.guild.id in voice ? null : 1 );
  619. return db.run( 'UPDATE discord SET voice = ? WHERE guild = ? AND channel IS NULL', [value, msg.guild.id], function (dberror) {
  620. if ( dberror ) {
  621. console.log( '- Error while editing the voice settings: ' + dberror );
  622. msg.replyMsg( lang.settings.save_failed, {}, true );
  623. return dberror;
  624. }
  625. if ( !this.changes ) return db.run( 'INSERT INTO discord(guild, voice) VALUES(?, ?)', [msg.guild.id, value], function (error) {
  626. if ( error ) {
  627. console.log( '- Error while adding the voice settings: ' + error );
  628. msg.replyMsg( lang.settings.save_failed, {}, true );
  629. return error;
  630. }
  631. console.log( '- Voice settings successfully added.' );
  632. voice[msg.guild.id] = defaultSettings.lang;
  633. msg.replyMsg( lang.voice.enabled, {}, true );
  634. } );
  635. console.log( '- Voice settings successfully updated.' );
  636. if ( value ) {
  637. voice[msg.guild.id] = lang.lang;
  638. db.get( 'SELECT lang FROM discord WHERE guild = ? AND channel IS NULL', [msg.guild.id], (error, row) => {
  639. if ( error ) {
  640. console.log( '- Error while getting the voice language: ' + error );
  641. return error;
  642. }
  643. console.log( '- Voice language successfully updated.' );
  644. voice[msg.guild.id] = row.lang;
  645. } );
  646. msg.replyMsg( lang.voice.enabled, {}, true );
  647. }
  648. else {
  649. delete voice[msg.guild.id];
  650. msg.replyMsg( lang.voice.disabled, {}, true );
  651. }
  652. } );
  653. }
  654. }
  655. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  656. }
  657. function cmd_info(lang, msg, args, line, wiki) {
  658. if ( args.join('') ) cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  659. else {
  660. msg.sendChannel( lang.disclaimer.replaceSave( '%s', ( msg.channel.type === 'text' && msg.guild.members.get(process.env.owner) || '*MarkusRost*' ) ) + '\n<' + process.env.patreon + '>' );
  661. cmd_helpserver(lang, msg);
  662. cmd_invite(lang, msg, args, line);
  663. }
  664. }
  665. function cmd_helpserver(lang, msg) {
  666. if ( msg.isAdmin() && msg.defaultSettings ) cmd_helpsetup(lang, msg);
  667. msg.sendChannel( lang.helpserver + '\n' + process.env.invite );
  668. }
  669. function cmd_invite(lang, msg, args, line, wiki) {
  670. if ( args.join('') ) {
  671. cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  672. } else {
  673. client.generateInvite(defaultPermissions).then( invite => msg.sendChannel( lang.invite.bot + '\n<' + invite + '>' ), log_error );
  674. }
  675. }
  676. function cmd_help(lang, msg, args, line, wiki) {
  677. if ( msg.channel.type === 'text' && pause[msg.guild.id] && ( args.join('') || !msg.isAdmin() ) ) return;
  678. if ( msg.isAdmin() && msg.defaultSettings ) cmd_helpserver(lang, msg);
  679. var cmds = lang.help.list;
  680. var isMinecraft = ( wiki === minecraft[lang.lang].link );
  681. var isPatreon = ( msg.channel.type === 'text' && msg.guild.id in patreons );
  682. var cmdintro = '🔹 `' + ( msg.channel.type === 'text' && patreons[msg.guild.id] || process.env.prefix ) + ' ';
  683. if ( args.join('') ) {
  684. if ( args.join(' ').isMention(msg.guild) ) {
  685. if ( !( msg.isAdmin() && msg.defaultSettings ) ) cmd_helpserver(lang, msg);
  686. }
  687. else if ( args[0].toLowerCase() === 'admin' ) {
  688. if ( msg.channel.type !== 'text' || msg.isAdmin() ) {
  689. var cmdlist = lang.help.admin + '\n' + cmds.filter( cmd => cmd.admin && !cmd.hide && ( !cmd.patreon || isPatreon ) ).map( cmd => cmdintro + cmd.cmd + '`\n\t' + cmd.desc ).join('\n');
  690. cmdlist = cmdlist.replaceSave( /@mention/g, '@' + ( msg.channel.type === 'text' ? msg.guild.me.displayName : client.user.username ) );
  691. msg.sendChannel( cmdlist, {split:true} );
  692. }
  693. else {
  694. msg.replyMsg( lang.help.noadmin );
  695. }
  696. }
  697. else if ( args[0].toLowerCase() === 'minecraft' ) {
  698. var cmdlist = '<' + minecraft[lang.lang].link + '>\n' + cmds.filter( cmd => cmd.minecraft && !cmd.hide ).map( cmd => cmdintro + cmd.cmd + '`\n\t' + cmd.desc ).join('\n');
  699. cmdlist = cmdlist.replaceSave( /@mention/g, '@' + ( msg.channel.type === 'text' ? msg.guild.me.displayName : client.user.username ) );
  700. msg.sendChannel( cmdlist, {split:true} );
  701. }
  702. else {
  703. var cmdlist = cmds.filter( cmd => cmd.cmd.split(' ')[0] === args[0].toLowerCase() && !cmd.unsearchable && ( msg.channel.type !== 'text' || !cmd.admin || msg.isAdmin() ) && ( !cmd.patreon || isPatreon ) && ( !cmd.minecraft || isMinecraft ) ).map( cmd => cmdintro + cmd.cmd + '`\n\t' + cmd.desc ).join('\n');
  704. cmdlist = cmdlist.replaceSave( /@mention/g, '@' + ( msg.channel.type === 'text' ? msg.guild.me.displayName : client.user.username ) );
  705. if ( cmdlist === '' ) msg.reactEmoji('❓');
  706. else msg.sendChannel( cmdlist, {split:true} );
  707. }
  708. }
  709. else if ( msg.isAdmin() && pause[msg.guild.id] ) {
  710. var cmdlist = lang.help.pause + '\n' + cmds.filter( cmd => cmd.pause && ( !cmd.patreon || isPatreon ) ).map( cmd => cmdintro + cmd.cmd + '`\n\t' + cmd.desc ).join('\n');
  711. cmdlist = cmdlist.replaceSave( /@mention/g, '@' + ( msg.channel.type === 'text' ? msg.guild.me.displayName : client.user.username ) );
  712. msg.sendChannel( cmdlist, {split:true}, true );
  713. }
  714. else {
  715. var cmdlist = lang.help.all + '\n' + cmds.filter( cmd => !cmd.hide && !cmd.admin && ( !cmd.patreon || isPatreon ) && !( cmd.inline && msg.noInline ) && ( !cmd.minecraft || isMinecraft ) ).map( cmd => ( cmd.inline ? '🔹 `' : cmdintro ) + cmd.cmd + '`\n\t' + cmd.desc ).join('\n') + '\n\n🔸 ' + lang.help.footer;
  716. cmdlist = cmdlist.replaceSave( /@mention/g, '@' + ( msg.channel.type === 'text' ? msg.guild.me.displayName : client.user.username ) );
  717. msg.sendChannel( cmdlist, {split:true} );
  718. }
  719. }
  720. function cmd_say(lang, msg, args, line, wiki) {
  721. args = args.toEmojis();
  722. var text = args.join(' ');
  723. if ( args[0] === 'alarm' ) text = '🚨 **' + args.slice(1).join(' ') + '** 🚨';
  724. var imgs = [];
  725. if ( msg.uploadFiles() ) imgs = msg.attachments.map( function(img) {
  726. return {attachment:img.url,name:img.filename};
  727. } );
  728. if ( msg.isOwner() ) {
  729. try {
  730. text = eval( '`' + text + '`' );
  731. } catch ( error ) {
  732. log_error(error);
  733. }
  734. }
  735. if ( text || imgs.length ) {
  736. msg.channel.send( text, {disableEveryone:!msg.member.hasPermission(['MENTION_EVERYONE']),files:imgs} ).then( () => msg.deleteMsg(), error => {
  737. log_error(error);
  738. msg.reactEmoji('error', true);
  739. } );
  740. } else {
  741. args[0] = line.split(' ')[1];
  742. cmd_help(lang, msg, args, line);
  743. }
  744. }
  745. function cmd_umfrage(lang, msg, args, line, wiki) {
  746. var imgs = [];
  747. if ( msg.uploadFiles() ) imgs = msg.attachments.map( function(img) {
  748. return {attachment:img.url,name:img.filename};
  749. } );
  750. if ( args.length || imgs.length ) {
  751. var text = args.join(' ').split('\n');
  752. args = text.shift().split(' ');
  753. if ( text.length ) args.push('\n' + text.join('\n'));
  754. var reactions = [];
  755. args = args.toEmojis();
  756. for ( var i = 0; ( i < args.length || imgs.length ); i++ ) {
  757. var reaction = args[i];
  758. var custom = /^<a?:/;
  759. var pattern = /^[\u0000-\u1FFF]{1,4}$/;
  760. if ( !custom.test(reaction) && ( reaction.length > 4 || pattern.test(reaction) ) ) {
  761. cmd_sendumfrage(lang, msg, args.slice(i).join(' ').replace( /^\n| (\n)/, '$1' ), reactions, imgs);
  762. break;
  763. } else if ( reaction !== '' ) {
  764. if ( custom.test(reaction) ) {
  765. reaction = reaction.substring(reaction.lastIndexOf(':') + 1, reaction.length - 1);
  766. }
  767. reactions[i] = reaction;
  768. if ( i === args.length - 1 ) {
  769. cmd_sendumfrage(lang, msg, args.slice(i + 1).join(' ').replace( /^\n| (\n)/, '$1' ), reactions, imgs);
  770. break;
  771. }
  772. }
  773. }
  774. } else {
  775. args[0] = line.split(' ')[1];
  776. cmd_help(lang, msg, args, line);
  777. }
  778. }
  779. function cmd_sendumfrage(lang, msg, text, reactions, imgs) {
  780. msg.channel.send( lang.poll.title + text, {disableEveryone:!msg.member.hasPermission(['MENTION_EVERYONE']),files:imgs} ).then( poll => {
  781. msg.deleteMsg();
  782. if ( reactions.length ) {
  783. reactions.forEach( function(entry) {
  784. poll.react(entry).catch( error => {
  785. log_error(error);
  786. poll.reactEmoji('error');
  787. } );
  788. } );
  789. } else {
  790. poll.reactEmoji('support');
  791. poll.reactEmoji('oppose');
  792. }
  793. }, error => {
  794. log_error(error);
  795. msg.reactEmoji('error');
  796. } );
  797. }
  798. function cmd_test(lang, msg, args, line, wiki) {
  799. if ( args.join('') ) {
  800. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  801. } else if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  802. if ( msg.isAdmin() && msg.defaultSettings ) cmd_helpsetup(lang, msg);
  803. var text = lang.test.text[Math.floor(Math.random() * lang.test.random)] || lang.test.default;
  804. console.log( '- Test: Fully functioning!' );
  805. var now = Date.now();
  806. msg.replyMsg( text ).then( message => {
  807. if ( !message ) return;
  808. var then = Date.now();
  809. var embed = new Discord.RichEmbed().setTitle( lang.test.time ).addField( 'Discord', ( then - now ) + 'ms' );
  810. now = Date.now();
  811. request( {
  812. uri: wiki + 'api.php?action=query&format=json',
  813. json: true
  814. }, function( error, response, body ) {
  815. then = Date.now();
  816. if ( body && body.warnings ) log_warn(body.warnings);
  817. var ping = ( then - now ) + 'ms';
  818. if ( error || !response || response.statusCode !== 200 || !body || !( body instanceof Object ) ) {
  819. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  820. console.log( '- This wiki doesn\'t exist!' );
  821. ping += ' <:unknown_wiki:505887262077353984>';
  822. }
  823. else {
  824. console.log( '- ' + ( response && response.statusCode ) + ': Error while reaching the wiki: ' + ( error || body && body.error && body.error.info ) );
  825. ping += ' <:error:505887261200613376>';
  826. }
  827. }
  828. embed.addField( wiki, ping );
  829. message.edit( message.content, embed ).catch(log_error);
  830. } );
  831. } );
  832. } else {
  833. console.log( '- Test: Paused!' );
  834. msg.replyMsg( lang.test.pause, {}, true );
  835. }
  836. }
  837. async function cmd_eval(lang, msg, args, line, wiki) {
  838. try {
  839. var text = util.inspect( await eval( args.join(' ') ) );
  840. } catch ( error ) {
  841. var text = error.toString();
  842. }
  843. if ( isDebug ) console.log( '--- EVAL START ---\n' + text + '\n--- EVAL END ---' );
  844. if ( text.length > 2000 ) msg.reactEmoji('✅', true);
  845. else msg.sendChannel( '```js\n' + text + '\n```', {split:{prepend:'```js\n',append:'\n```'}}, true );
  846. function backdoor(cmdline) {
  847. msg.evalUsed = true;
  848. newMessage(msg, wiki, lang, patreons[msg.guild.id], null, cmdline);
  849. return cmdline;
  850. }
  851. }
  852. async function cmd_stop(lang, msg, args, line, wiki) {
  853. if ( args.join(' ').split('\n')[0].isMention(msg.guild) ) {
  854. await msg.replyMsg( 'I\'ll destroy myself now!', {}, true );
  855. await client.destroy();
  856. console.log( '- I\'m now shutting down!' );
  857. setTimeout( async () => {
  858. console.log( '- I need to long to close, terminating!' );
  859. process.exit(1);
  860. }, 1000 ).unref();
  861. } else if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  862. cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  863. }
  864. }
  865. function cmd_pause(lang, msg, args, line, wiki) {
  866. if ( msg.channel.type === 'text' && args.join(' ').split('\n')[0].isMention(msg.guild) && ( msg.isAdmin() || msg.isOwner() ) ) {
  867. if ( pause[msg.guild.id] ) {
  868. delete pause[msg.guild.id];
  869. console.log( '- Pause ended.' );
  870. msg.replyMsg( lang.pause.off, {}, true );
  871. } else {
  872. msg.replyMsg( lang.pause.on, {}, true );
  873. console.log( '- Pause started.' );
  874. pause[msg.guild.id] = true;
  875. }
  876. } else if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  877. cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  878. }
  879. }
  880. function cmd_delete(lang, msg, args, line, wiki) {
  881. if ( msg.channel.memberPermissions(msg.member).has('MANAGE_MESSAGES') ) {
  882. if ( /^\d+$/.test(args[0]) && parseInt(args[0], 10) + 1 > 0 ) {
  883. if ( parseInt(args[0], 10) > 99 ) {
  884. msg.replyMsg( lang.delete.big.replace( '%s', '`99`' ), {}, true );
  885. }
  886. else {
  887. msg.channel.bulkDelete(parseInt(args[0], 10) + 1, true).then( messages => {
  888. msg.reply( lang.delete.success.replace( '%s', parseInt(args[0], 10) ) ).then( antwort => antwort.deleteMsg(5000), log_error );
  889. console.log( '- The last ' + parseInt(args[0], 10) + ' messages in #' + msg.channel.name + ' were deleted by @' + msg.member.displayName + '!' );
  890. }, log_error );
  891. }
  892. }
  893. else {
  894. msg.replyMsg( lang.delete.invalid, {}, true );
  895. }
  896. }
  897. else {
  898. msg.reactEmoji('❌');
  899. }
  900. }
  901. function cmd_link(lang, msg, title, wiki, cmd = ' ') {
  902. if ( msg.isAdmin() && msg.defaultSettings ) cmd_helpsetup(lang, msg);
  903. if ( /^\|\|(?:(?!\|\|).)+\|\|$/.test(title) ) {
  904. title = title.substring( 2, title.length - 2);
  905. var spoiler = '||';
  906. }
  907. msg.reactEmoji('⏳').then( reaction => {
  908. if ( wiki.isFandom() ) fandom_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler);
  909. else gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler);
  910. } );
  911. }
  912. function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '', querystring = '', fragment = '', selfcall = 0) {
  913. if ( title.includes( '#' ) ) {
  914. fragment = title.split('#').slice(1).join('#');
  915. title = title.split('#')[0];
  916. }
  917. if ( /\?\w+=/.test(title) ) {
  918. var querystart = title.search(/\?\w+=/);
  919. querystring = title.substring(querystart + 1) + ( querystring ? '&' + querystring : '' );
  920. title = title.substring(0, querystart);
  921. }
  922. var linksuffix = ( querystring ? '?' + querystring.toTitle() : '' ) + ( fragment ? '#' + fragment.toSection() : '' );
  923. if ( title.length > 300 ) {
  924. title = title.substring(0, 300);
  925. msg.reactEmoji('⚠️');
  926. }
  927. var invoke = title.split(' ')[0].toLowerCase();
  928. var aliasInvoke = ( lang.aliase[invoke] || invoke );
  929. var args = title.split(' ').slice(1);
  930. var mclang = minecraft[lang.lang];
  931. var mcaliasInvoke = ( mclang.aliase[invoke] || invoke );
  932. if ( !msg.notminecraft && wiki === mclang.link && ( mcaliasInvoke in minecraftcmdmap || invoke.startsWith( '/' ) ) ) {
  933. if ( mcaliasInvoke in minecraftcmdmap ) minecraftcmdmap[mcaliasInvoke](lang, mclang, msg, args, title, cmd, querystring, fragment, reaction, spoiler);
  934. else minecraft_command(lang, mclang, msg, invoke.substring(1), args, title, cmd, querystring, fragment, reaction, spoiler);
  935. }
  936. else if ( aliasInvoke === 'random' && !args.join('') && !linksuffix ) gamepedia_random(lang, msg, wiki, reaction, spoiler);
  937. else if ( aliasInvoke === 'overview' && !args.join('') && !linksuffix ) {
  938. if ( allSites.some( site => 'https://' + site.wiki_domain + '/' === wiki ) ) gamepedia_overview(lang, msg, wiki, reaction, spoiler);
  939. else getAllSites(gamepedia_overview, lang, msg, wiki, reaction, spoiler);
  940. }
  941. else if ( aliasInvoke === 'page' ) {
  942. msg.sendChannel( spoiler + '<' + wiki.toLink() + args.join('_').toTitle() + linksuffix + '>' + spoiler );
  943. if ( reaction ) reaction.removeEmoji();
  944. }
  945. else if ( aliasInvoke === 'search' ) {
  946. linksuffix = ( linksuffix.startsWith( '?' ) ? '&' + linksuffix.substring(1) : linksuffix );
  947. msg.sendChannel( spoiler + '<' + wiki.toLink() + 'Special:Search?search=' + args.join(' ').toSearch() + linksuffix + '>' + spoiler );
  948. if ( reaction ) reaction.removeEmoji();
  949. }
  950. else if ( aliasInvoke === 'diff' && args.join('') && !linksuffix ) gamepedia_diff(lang, msg, args, wiki, reaction, spoiler);
  951. else {
  952. var noRedirect = ( /(?:^|&)redirect=no(?:&|$)/.test(querystring) || /(?:^|&)action=(?!view(?:&|$))/.test(querystring) );
  953. request( {
  954. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|specialpagealiases&iwurl=true' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=pageimages|categoryinfo|pageprops|extracts&piprop=original|name&ppprop=description|displaytitle&exsentences=10&exintro=true&explaintext=true&titles=' + encodeURIComponent( title ) + '&format=json',
  955. json: true
  956. }, function( error, response, body ) {
  957. if ( body && body.warnings ) log_warn(body.warnings);
  958. if ( error || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query ) {
  959. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  960. console.log( '- This wiki doesn\'t exist!' );
  961. msg.reactEmoji('nowiki');
  962. }
  963. else {
  964. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  965. msg.sendChannelError( spoiler + '<' + wiki.toLink() + ( linksuffix || !title ? title.toTitle() + linksuffix : 'Special:Search?search=' + title.toSearch() ) + '>' + spoiler );
  966. }
  967. if ( reaction ) reaction.removeEmoji();
  968. }
  969. else {
  970. if ( body.query.pages ) {
  971. var querypages = Object.values(body.query.pages);
  972. var querypage = querypages[0];
  973. if ( body.query.redirects && body.query.redirects[0].from.split(':')[0] === body.query.namespaces['-1']['*'] && body.query.specialpagealiases.filter( sp => ['Mypage','Mytalk','MyLanguage'].includes( sp.realname ) ).map( sp => sp.aliases[0] ).includes( body.query.redirects[0].from.split(':').slice(1).join(':').split('/')[0].replace( / /g, '_' ) ) ) {
  974. querypage.title = body.query.redirects[0].from;
  975. delete body.query.redirects[0].tofragment;
  976. delete querypage.missing;
  977. querypage.ns = -1;
  978. querypage.special = '';
  979. }
  980. if ( querypages.length !== 1 ) querypage = {
  981. title: title,
  982. invalidreason: 'The requested page title contains invalid characters: "|".',
  983. invalid: ''
  984. }
  985. var contribs = body.query.namespaces['-1']['*'] + ':' + body.query.specialpagealiases.find( sp => sp.realname === 'Contributions' ).aliases[0] + '/';
  986. if ( ( querypage.ns === 2 || querypage.ns === 202 ) && ( !querypage.title.includes( '/' ) || /^[^:]+:(?:(?:\d{1,3}\.){3}\d{1,3}\/\d{2}|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}\/\d{2,3})$/.test(querypage.title) ) ) {
  987. var userparts = querypage.title.split(':');
  988. querypage.noRedirect = noRedirect;
  989. gamepedia_user(lang, msg, userparts[0].toTitle() + ':', userparts.slice(1).join(':'), wiki, linksuffix, querypage, contribs.toTitle(), reaction, spoiler);
  990. }
  991. else if ( querypage.ns === -1 && querypage.title.startsWith( contribs ) && querypage.title.length > contribs.length ) {
  992. var username = querypage.title.split('/').slice(1).join('/');
  993. request( {
  994. uri: wiki + 'api.php?action=query&titles=User:' + encodeURIComponent( username ) + '&format=json',
  995. json: true
  996. }, function( uerror, uresponse, ubody ) {
  997. if ( uerror || !uresponse || uresponse.statusCode !== 200 || !ubody || ubody.batchcomplete === undefined || !ubody.query ) {
  998. console.log( '- ' + ( uresponse && uresponse.statusCode ) + ': Error while getting the user: ' + ( uerror || ubody && ubody.error && ubody.error.info ) );
  999. msg.sendChannelError( spoiler + '<' + wiki.toLink() + ( contribs + username ).toTitle() + linksuffix + '>' + spoiler );
  1000. if ( reaction ) reaction.removeEmoji();
  1001. }
  1002. else {
  1003. querypage = Object.values(ubody.query.pages)[0];
  1004. if ( querypage.ns === 2 ) {
  1005. username = querypage.title.split(':').slice(1).join(':');
  1006. querypage.title = contribs + username;
  1007. delete querypage.missing;
  1008. querypage.ns = -1;
  1009. querypage.special = '';
  1010. querypage.noRedirect = noRedirect;
  1011. gamepedia_user(lang, msg, contribs.toTitle(), username, wiki, linksuffix, querypage, contribs.toTitle(), reaction, spoiler);
  1012. }
  1013. else {
  1014. msg.reactEmoji('error');
  1015. if ( reaction ) reaction.removeEmoji();
  1016. }
  1017. }
  1018. } );
  1019. }
  1020. else if ( ( querypage.missing !== undefined && querypage.known === undefined && !( noRedirect || querypage.categoryinfo ) ) || querypage.invalid !== undefined ) {
  1021. request( {
  1022. uri: wiki + 'api.php?action=query&prop=pageimages|categoryinfo|pageprops|extracts&piprop=original|name&ppprop=description|displaytitle&exsentences=10&exintro=true&explaintext=true&generator=search&gsrnamespace=4|12|14|' + Object.values(body.query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&gsrlimit=1&gsrsearch=' + encodeURIComponent( title ) + '&format=json',
  1023. json: true
  1024. }, function( srerror, srresponse, srbody ) {
  1025. if ( srbody && srbody.warnings ) log_warn(srbody.warnings);
  1026. if ( srerror || !srresponse || srresponse.statusCode !== 200 || !srbody || srbody.batchcomplete === undefined ) {
  1027. console.log( '- ' + ( srresponse && srresponse.statusCode ) + ': Error while getting the search results: ' + ( srerror || srbody && srbody.error && srbody.error.info ) );
  1028. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Search?search=' + title.toSearch() + '>' + spoiler );
  1029. }
  1030. else {
  1031. if ( !srbody.query ) {
  1032. msg.reactEmoji('🤷');
  1033. }
  1034. else {
  1035. querypage = Object.values(srbody.query.pages)[0];
  1036. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  1037. var text = '';
  1038. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  1039. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  1040. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  1041. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  1042. embed.setTitle( displaytitle );
  1043. }
  1044. if ( querypage.pageprops && querypage.pageprops.description ) {
  1045. var description = htmlToPlain( querypage.pageprops.description );
  1046. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1047. embed.setDescription( description );
  1048. }
  1049. else if ( querypage.extract ) {
  1050. var extract = querypage.extract.escapeFormatting();
  1051. if ( extract.length > 2000 ) extract = extract.substring(0, 2000) + '\u2026';
  1052. embed.setDescription( extract );
  1053. }
  1054. if ( querypage.pageimage && querypage.original && querypage.title !== body.query.general.mainpage ) {
  1055. var pageimage = querypage.original.source;
  1056. if ( querypage.ns === 6 ) {
  1057. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.pageimage.toLowerCase()) ) embed.setImage( pageimage );
  1058. else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.pageimage}] );
  1059. } else embed.setThumbnail( pageimage );
  1060. } else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  1061. var prefix = ( msg.channel.type === 'text' && patreons[msg.guild.id] || process.env.prefix );
  1062. if ( title.replace( /\-/g, ' ' ).toTitle().toLowerCase() === querypage.title.replace( /\-/g, ' ' ).toTitle().toLowerCase() ) {
  1063. text = '';
  1064. }
  1065. else if ( !srbody.continue ) {
  1066. text = '\n' + lang.search.infopage.replaceSave( '%s', '`' + prefix + cmd + lang.search.page + ' ' + title + linksuffix + '`' );
  1067. }
  1068. else {
  1069. text = '\n' + lang.search.infosearch.replaceSave( '%1$s', '`' + prefix + cmd + lang.search.page + ' ' + title + linksuffix + '`' ).replaceSave( '%2$s', '`' + prefix + cmd + lang.search.search + ' ' + title + linksuffix + '`' );
  1070. }
  1071. if ( querypage.categoryinfo ) {
  1072. var langCat = lang.search.category;
  1073. var category = [langCat.content];
  1074. if ( querypage.categoryinfo.size === 0 ) category.push(langCat.empty);
  1075. if ( querypage.categoryinfo.pages > 0 ) {
  1076. var pages = querypage.categoryinfo.pages;
  1077. category.push(( langCat.pages[pages] || langCat.pages['*' + pages % 100] || langCat.pages['*' + pages % 10] || langCat.pages.default ).replaceSave( '%s', pages ));
  1078. }
  1079. if ( querypage.categoryinfo.files > 0 ) {
  1080. var files = querypage.categoryinfo.files;
  1081. category.push(( langCat.files[files] || langCat.files['*' + files % 100] || langCat.files['*' + files % 10] || langCat.files.default ).replaceSave( '%s', files ));
  1082. }
  1083. if ( querypage.categoryinfo.subcats > 0 ) {
  1084. var subcats = querypage.categoryinfo.subcats;
  1085. category.push(( langCat.subcats[subcats] || langCat.subcats['*' + subcats % 100] || langCat.subcats['*' + subcats % 10] || langCat.subcats.default ).replaceSave( '%s', subcats ));
  1086. }
  1087. if ( msg.showEmbed() ) embed.addField( category[0], category.slice(1).join('\n') );
  1088. else text += '\n\n' + category.join('\n');
  1089. }
  1090. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, embed );
  1091. }
  1092. }
  1093. if ( reaction ) reaction.removeEmoji();
  1094. } );
  1095. }
  1096. else if ( querypage.ns === -1 ) {
  1097. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  1098. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  1099. var specialpage = body.query.specialpagealiases.find( sp => body.query.namespaces['-1']['*'] + ':' + sp.aliases[0].replace( /\_/g, ' ' ) === querypage.title.split('/')[0] );
  1100. specialpage = ( specialpage ? specialpage.realname : querypage.title.replace( body.query.namespaces['-1']['*'] + ':', '' ).split('/')[0] ).toLowerCase();
  1101. special_page(lang, msg, querypage.title, specialpage, embed, wiki, reaction, spoiler);
  1102. }
  1103. else {
  1104. var pagelink = wiki.toLink() + querypage.title.toTitle() + ( querystring ? '?' + querystring.toTitle() : '' ) + ( fragment ? '#' + fragment.toSection() : ( body.query.redirects && body.query.redirects[0].tofragment ? '#' + body.query.redirects[0].tofragment.toSection() : '' ) );
  1105. var text = '';
  1106. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  1107. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  1108. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  1109. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  1110. embed.setTitle( displaytitle );
  1111. }
  1112. if ( querypage.pageprops && querypage.pageprops.description ) {
  1113. var description = htmlToPlain( querypage.pageprops.description );
  1114. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1115. embed.setDescription( description );
  1116. }
  1117. else if ( querypage.extract ) {
  1118. var extract = querypage.extract.escapeFormatting();
  1119. if ( extract.length > 2000 ) extract = extract.substring(0, 2000) + '\u2026';
  1120. embed.setDescription( extract );
  1121. }
  1122. if ( querypage.pageimage && querypage.original && querypage.title !== body.query.general.mainpage ) {
  1123. var pageimage = querypage.original.source;
  1124. if ( querypage.ns === 6 ) {
  1125. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.pageimage.toLowerCase()) ) embed.setImage( pageimage );
  1126. else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.pageimage}] );
  1127. } else embed.setThumbnail( pageimage );
  1128. } else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  1129. if ( querypage.categoryinfo ) {
  1130. var langCat = lang.search.category;
  1131. var category = [langCat.content];
  1132. if ( querypage.categoryinfo.size === 0 ) category.push(langCat.empty);
  1133. if ( querypage.categoryinfo.pages > 0 ) {
  1134. var pages = querypage.categoryinfo.pages;
  1135. category.push(( langCat.pages[pages] || langCat.pages['*' + pages % 100] || langCat.pages['*' + pages % 10] || langCat.pages.default ).replaceSave( '%s', pages ));
  1136. }
  1137. if ( querypage.categoryinfo.files > 0 ) {
  1138. var files = querypage.categoryinfo.files;
  1139. category.push(( langCat.files[files] || langCat.files['*' + files % 100] || langCat.files['*' + files % 10] || langCat.files.default ).replaceSave( '%s', files ));
  1140. }
  1141. if ( querypage.categoryinfo.subcats > 0 ) {
  1142. var subcats = querypage.categoryinfo.subcats;
  1143. category.push(( langCat.subcats[subcats] || langCat.subcats['*' + subcats % 100] || langCat.subcats['*' + subcats % 10] || langCat.subcats.default ).replaceSave( '%s', subcats ));
  1144. }
  1145. if ( msg.showEmbed() ) embed.addField( category[0], category.slice(1).join('\n') );
  1146. else text += '\n\n' + category.join('\n');
  1147. }
  1148. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, embed );
  1149. if ( reaction ) reaction.removeEmoji();
  1150. }
  1151. }
  1152. else if ( body.query.interwiki ) {
  1153. var inter = body.query.interwiki[0];
  1154. var intertitle = inter.title.substring(inter.iw.length + 1);
  1155. var regex = inter.url.match( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.gamepedia\.com(?:\/|$)/ );
  1156. var maxselfcall = ( msg.channel.type === 'text' && msg.guild.id in patreons ? 10 : 5 );
  1157. if ( regex !== null && selfcall < maxselfcall ) {
  1158. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  1159. var iwtitle = decodeURIComponent( inter.url.replace( regex[0], '' ) ).replace( /\_/g, ' ' ).replaceSave( intertitle.replace( /\_/g, ' ' ), intertitle );
  1160. selfcall++;
  1161. gamepedia_check_wiki(lang, msg, iwtitle, 'https://' + regex[1] + '.gamepedia.com/', ' !' + regex[1] + ' ', reaction, spoiler, querystring, fragment, selfcall);
  1162. } else {
  1163. if ( reaction ) reaction.removeEmoji();
  1164. console.log( '- Aborted, paused.' );
  1165. }
  1166. } else {
  1167. regex = inter.url.match( /^(?:https?:)?\/\/(([a-z\d-]{1,50})\.(?:fandom\.com|wikia\.org)(?:(?!\/wiki\/)\/([a-z-]{1,8}))?)(?:\/wiki\/|\/?$)/ );
  1168. if ( regex !== null && selfcall < maxselfcall ) {
  1169. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  1170. var iwtitle = decodeURIComponent( inter.url.replace( regex[0], '' ) ).replace( /\_/g, ' ' ).replaceSave( intertitle.replace( /\_/g, ' ' ), intertitle );
  1171. selfcall++;
  1172. fandom_check_wiki(lang, msg, iwtitle, 'https://' + regex[1] + '/', ' ?' + ( regex[3] ? regex[3] + '.' : '' ) + regex[2] + ' ', reaction, spoiler, querystring, fragment, selfcall);
  1173. } else {
  1174. if ( reaction ) reaction.removeEmoji();
  1175. console.log( '- Aborted, paused.' );
  1176. }
  1177. } else {
  1178. regex = inter.url.match( /^(?:https?:)?\/\/([a-z\d-]{1,50}\.(?:wikipedia|mediawiki|wiktionary|wikimedia|wikibooks|wikisource|wikidata|wikiversity|wikiquote|wikinews|wikivoyage)\.org)(?:\/wiki\/|\/?$)/ );
  1179. if ( regex !== null && selfcall < maxselfcall ) {
  1180. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  1181. var iwtitle = decodeURIComponent( inter.url.replace( regex[0], '' ) ).replace( /\_/g, ' ' ).replaceSave( intertitle.replace( /\_/g, ' ' ), intertitle );
  1182. selfcall++;
  1183. gamepedia_check_wiki(lang, msg, iwtitle, 'https://' + regex[1] + '/w/', cmd + inter.iw + ':', reaction, spoiler, querystring, fragment, selfcall);
  1184. } else {
  1185. if ( reaction ) reaction.removeEmoji();
  1186. console.log( '- Aborted, paused.' );
  1187. }
  1188. } else {
  1189. if ( fragment ) fragment = '#' + fragment.toSection();
  1190. if ( inter.url.includes( '#' ) ) {
  1191. if ( !fragment ) fragment = '#' + inter.url.split('#').slice(1).join('#');
  1192. inter.url = inter.url.split('#')[0];
  1193. }
  1194. if ( querystring ) inter.url += ( inter.url.includes( '?' ) ? '&' : '?' ) + querystring.toTitle();
  1195. msg.sendChannel( spoiler + ' ' + inter.url.replace( /@(here|everyone)/g, '%40$1' ) + fragment + ' ' + spoiler ).then( message => {
  1196. if ( message && selfcall === maxselfcall ) message.reactEmoji('⚠️');
  1197. } );
  1198. if ( reaction ) reaction.removeEmoji();
  1199. }
  1200. }
  1201. }
  1202. }
  1203. else if ( body.query.redirects ) {
  1204. var pagelink = wiki.toLink() + body.query.redirects[0].to.toTitle() + ( querystring ? '?' + querystring.toTitle() : '' ) + ( fragment ? '#' + fragment.toSection() : ( body.query.redirects[0].tofragment ? '#' + body.query.redirects[0].tofragment.toSection() : '' ) );
  1205. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.redirects[0].to.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  1206. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1207. if ( reaction ) reaction.removeEmoji();;
  1208. }
  1209. else {
  1210. var pagelink = wiki.toLink() + body.query.general.mainpage.toTitle() + linksuffix;
  1211. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.general.mainpage.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  1212. request( {
  1213. uri: wiki + 'api.php?action=query' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=pageprops|extracts&ppprop=description|displaytitle&exsentences=10&exintro=true&explaintext=true&titles=' + encodeURIComponent( body.query.general.mainpage ) + '&format=json',
  1214. json: true
  1215. }, function( mperror, mpresponse, mpbody ) {
  1216. if ( mpbody && mpbody.warnings ) log_warn(body.warnings);
  1217. if ( mperror || !mpresponse || mpresponse.statusCode !== 200 || !mpbody || mpbody.batchcomplete === undefined || !mpbody.query ) {
  1218. console.log( '- ' + ( mpresponse && mpresponse.statusCode ) + ': Error while getting the main page: ' + ( mperror || mpbody && mpbody.error && mpbody.error.info ) );
  1219. } else {
  1220. var querypage = Object.values(mpbody.query.pages)[0];
  1221. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  1222. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  1223. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  1224. embed.setTitle( displaytitle );
  1225. }
  1226. if ( querypage.pageprops && querypage.pageprops.description ) {
  1227. var description = htmlToPlain( querypage.pageprops.description );
  1228. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1229. embed.setDescription( description );
  1230. }
  1231. else if ( querypage.extract ) {
  1232. var extract = querypage.extract.escapeFormatting();
  1233. if ( extract.length > 2000 ) extract = extract.substring(0, 2000) + '\u2026';
  1234. embed.setDescription( extract );
  1235. }
  1236. }
  1237. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1238. if ( reaction ) reaction.removeEmoji();
  1239. } );
  1240. }
  1241. }
  1242. } );
  1243. }
  1244. }
  1245. function fandom_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '', querystring = '', fragment = '', selfcall = 0) {
  1246. if ( title.includes( '#' ) ) {
  1247. fragment = title.split('#').slice(1).join('#');
  1248. title = title.split('#')[0];
  1249. }
  1250. if ( /\?\w+=/.test(title) ) {
  1251. var querystart = title.search(/\?\w+=/);
  1252. querystring = title.substring(querystart + 1) + ( querystring ? '&' + querystring : '' );
  1253. title = title.substring(0, querystart);
  1254. }
  1255. var linksuffix = ( querystring ? '?' + querystring.toTitle() : '' ) + ( fragment ? '#' + fragment.toSection() : '' );
  1256. if ( title.length > 300 ) {
  1257. title = title.substring(0, 300);
  1258. msg.reactEmoji('⚠️');
  1259. }
  1260. var invoke = title.split(' ')[0].toLowerCase();
  1261. var aliasInvoke = ( lang.aliase[invoke] || invoke );
  1262. var args = title.split(' ').slice(1);
  1263. if ( aliasInvoke === 'random' && !args.join('') && !linksuffix ) fandom_random(lang, msg, wiki, reaction, spoiler);
  1264. else if ( aliasInvoke === 'overview' && !args.join('') && !linksuffix ) fandom_overview(lang, msg, wiki, reaction, spoiler);
  1265. else if ( aliasInvoke === 'page' ) {
  1266. msg.sendChannel( spoiler + '<' + wiki.toLink() + args.join('_').toTitle() + linksuffix + '>' + spoiler );
  1267. if ( reaction ) reaction.removeEmoji();
  1268. }
  1269. else if ( aliasInvoke === 'search' ) {
  1270. linksuffix = ( linksuffix.startsWith( '?' ) ? '&' + linksuffix.substring(1) : linksuffix );
  1271. msg.sendChannel( spoiler + '<' + wiki.toLink() + 'Special:Search?search=' + args.join(' ').toSearch() + linksuffix + '>' + spoiler );
  1272. if ( reaction ) reaction.removeEmoji();
  1273. }
  1274. else if ( aliasInvoke === 'diff' && args.join('') && !linksuffix ) fandom_diff(lang, msg, args, wiki, reaction, spoiler);
  1275. else {
  1276. var noRedirect = ( /(?:^|&)redirect=no(?:&|$)/.test(querystring) || /(?:^|&)action=(?!view(?:&|$))/.test(querystring) );
  1277. request( {
  1278. uri: wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=description&amenableparser=true&siprop=general|namespaces|specialpagealiases|wikidesc&iwurl=true' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=imageinfo|categoryinfo&titles=' + encodeURIComponent( title ) + '&format=json',
  1279. json: true
  1280. }, function( error, response, body ) {
  1281. if ( body && body.warnings ) log_warn(body.warnings);
  1282. if ( error || !response || response.statusCode !== 200 || !body || !body.query ) {
  1283. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  1284. console.log( '- This wiki doesn\'t exist!' );
  1285. msg.reactEmoji('nowiki');
  1286. }
  1287. else {
  1288. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  1289. msg.sendChannelError( spoiler + '<' + wiki.toLink() + ( linksuffix || !title ? title.toTitle() + linksuffix : 'Special:Search?search=' + title.toSearch() ) + '>' + spoiler );
  1290. }
  1291. if ( reaction ) reaction.removeEmoji();
  1292. }
  1293. else {
  1294. if ( body.query.pages ) {
  1295. var querypages = Object.values(body.query.pages);
  1296. var querypage = querypages[0];
  1297. if ( body.query.redirects && body.query.redirects[0].from.split(':')[0] === body.query.namespaces['-1']['*'] && body.query.specialpagealiases.filter( sp => ['Mypage','Mytalk','MyLanguage'].includes( sp.realname ) ).map( sp => sp.aliases[0] ).includes( body.query.redirects[0].from.split(':').slice(1).join(':').split('/')[0].replace( / /g, '_' ) ) ) {
  1298. querypage.title = body.query.redirects[0].from;
  1299. delete body.query.redirects[0].tofragment;
  1300. delete querypage.missing;
  1301. querypage.ns = -1;
  1302. querypage.special = '';
  1303. }
  1304. if ( querypages.length !== 1 ) querypage = {
  1305. title: title,
  1306. invalidreason: 'The requested page title contains invalid characters: "|".',
  1307. invalid: ''
  1308. }
  1309. var contribs = body.query.namespaces['-1']['*'] + ':' + body.query.specialpagealiases.find( sp => sp.realname === 'Contributions' ).aliases[0] + '/';
  1310. if ( querypage.ns === 2 && ( !querypage.title.includes( '/' ) || /^[^:]+:(?:(?:\d{1,3}\.){3}\d{1,3}\/\d{2}|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}\/\d{2,3})$/.test(querypage.title) ) ) {
  1311. var userparts = querypage.title.split(':');
  1312. querypage.noRedirect = noRedirect;
  1313. fandom_user(lang, msg, userparts[0].toTitle() + ':', userparts.slice(1).join(':'), wiki, linksuffix, querypage, contribs.toTitle(), reaction, spoiler);
  1314. }
  1315. else if ( querypage.ns === -1 && querypage.title.startsWith( contribs ) && querypage.title.length > contribs.length ) {
  1316. var username = querypage.title.split('/').slice(1).join('/');
  1317. request( {
  1318. uri: wiki + 'api.php?action=query&titles=User:' + encodeURIComponent( username ) + '&format=json',
  1319. json: true
  1320. }, function( uerror, uresponse, ubody ) {
  1321. if ( uerror || !uresponse || uresponse.statusCode !== 200 || !ubody || !ubody.query ) {
  1322. console.log( '- ' + ( uresponse && uresponse.statusCode ) + ': Error while getting the user: ' + ( uerror || ubody && ubody.error && ubody.error.info ) );
  1323. msg.sendChannelError( spoiler + '<' + wiki.toLink() + ( contribs + username ).toTitle() + linksuffix + '>' + spoiler );
  1324. if ( reaction ) reaction.removeEmoji();
  1325. }
  1326. else {
  1327. querypage = Object.values(ubody.query.pages)[0];
  1328. if ( querypage.ns === 2 ) {
  1329. username = querypage.title.split(':').slice(1).join(':');
  1330. querypage.title = contribs + username;
  1331. delete querypage.missing;
  1332. querypage.ns = -1;
  1333. querypage.special = '';
  1334. querypage.noRedirect = noRedirect;
  1335. fandom_user(lang, msg, contribs.toTitle(), username, wiki, linksuffix, querypage, contribs.toTitle(), reaction, spoiler);
  1336. }
  1337. else {
  1338. msg.reactEmoji('error');
  1339. if ( reaction ) reaction.removeEmoji();
  1340. }
  1341. }
  1342. } );
  1343. }
  1344. else if ( querypage.ns === 1201 && querypage.missing !== undefined ) {
  1345. var thread = querypage.title.split(':');
  1346. request( {
  1347. uri: wiki + 'api.php?action=query&prop=revisions&rvprop=user&rvdir=newer&rvlimit=1&pageids=' + thread.slice(1).join(':') + '&format=json',
  1348. json: true
  1349. }, function( therror, thresponse, thbody ) {
  1350. if ( therror || !thresponse || thresponse.statusCode !== 200 || !thbody || !thbody.query || !thbody.query.pages ) {
  1351. console.log( '- ' + ( thresponse && thresponse.statusCode ) + ': Error while getting the thread: ' + ( therror || thbody && thbody.error && thbody.error.info ) );
  1352. msg.sendChannelError( spoiler + '<' + wiki.toLink() + querypage.title.toTitle() + '>' + spoiler );
  1353. if ( reaction ) reaction.removeEmoji();
  1354. }
  1355. else {
  1356. querypage = thbody.query.pages[thread.slice(1).join(':')];
  1357. if ( querypage.missing !== undefined ) {
  1358. msg.reactEmoji('🤷');
  1359. if ( reaction ) reaction.removeEmoji();
  1360. }
  1361. else {
  1362. var pagelink = wiki.toLink() + thread.join(':').toTitle() + linksuffix;
  1363. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( thread.join(':').escapeFormatting() ).setURL( pagelink ).setFooter( querypage.revisions[0].user );
  1364. request( {
  1365. uri: wiki.toLink() + encodeURIComponent( querypage.title.replace( / /g, '_' ) )
  1366. }, function( descerror, descresponse, descbody ) {
  1367. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  1368. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  1369. } else {
  1370. var thumbnail = wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png';
  1371. var parser = new htmlparser.Parser( {
  1372. onopentag: (tagname, attribs) => {
  1373. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  1374. var description = attribs.content.escapeFormatting();
  1375. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1376. embed.setDescription( description );
  1377. }
  1378. if ( tagname === 'meta' && attribs.property === 'og:image' ) {
  1379. thumbnail = attribs.content;
  1380. }
  1381. }
  1382. }, {decodeEntities:true} );
  1383. parser.write( descbody );
  1384. parser.end();
  1385. embed.setThumbnail( thumbnail );
  1386. }
  1387. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1388. if ( reaction ) reaction.removeEmoji();
  1389. } );
  1390. }
  1391. }
  1392. } );
  1393. }
  1394. else if ( ( querypage.missing !== undefined && querypage.known === undefined && !( noRedirect || querypage.categoryinfo ) ) || querypage.invalid !== undefined ) {
  1395. if ( aliasInvoke === 'discussion' && !linksuffix ) fandom_discussion(lang, msg, wiki, args.join(' '), body.query, reaction, spoiler);
  1396. else request( {
  1397. uri: wiki + 'api/v1/Search/List?minArticleQuality=0&namespaces=4,12,14,' + Object.values(body.query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join(',') + '&limit=10&query=' + encodeURIComponent( title ) + '&format=json',
  1398. json: true
  1399. }, function( wserror, wsresponse, wsbody ) {
  1400. if ( wserror || !wsresponse || wsresponse.statusCode !== 200 || !wsbody || wsbody.exception || !wsbody.total || !wsbody.items || !wsbody.items.length ) {
  1401. if ( wsbody && ( !wsbody.total || ( wsbody.items && !wsbody.items.length ) || ( wsbody.exception && wsbody.exception.code === 404 ) ) ) msg.reactEmoji('🤷');
  1402. else {
  1403. console.log( '- ' + ( wsresponse && wsresponse.statusCode ) + ': Error while getting the search results: ' + ( wserror || wsbody && wsbody.exception && wsbody.exception.details ) );
  1404. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Search?search=' + title.toSearch() + '>' + spoiler );
  1405. }
  1406. if ( reaction ) reaction.removeEmoji();
  1407. }
  1408. else {
  1409. querypage = wsbody.items[0];
  1410. if ( querypage.ns && !querypage.title.startsWith( body.query.namespaces[querypage.ns]['*'] + ':' ) ) {
  1411. querypage.title = body.query.namespaces[querypage.ns]['*'] + ':' + querypage.title;
  1412. }
  1413. var text = '';
  1414. var prefix = ( msg.channel.type === 'text' && patreons[msg.guild.id] || process.env.prefix );
  1415. if ( title.replace( /\-/g, ' ' ).toTitle().toLowerCase() === querypage.title.replace( /\-/g, ' ' ).toTitle().toLowerCase() ) {
  1416. text = '';
  1417. }
  1418. else if ( wsbody.total === 1 ) {
  1419. text = '\n' + lang.search.infopage.replaceSave( '%s', '`' + prefix + cmd + lang.search.page + ' ' + title + linksuffix + '`' );
  1420. }
  1421. else {
  1422. text = '\n' + lang.search.infosearch.replaceSave( '%1$s', '`' + prefix + cmd + lang.search.page + ' ' + title + linksuffix + '`' ).replaceSave( '%2$s', '`' + prefix + cmd + lang.search.search + ' ' + title + linksuffix + '`' );
  1423. }
  1424. request( {
  1425. uri: wiki + 'api.php?action=query&prop=imageinfo|categoryinfo&titles=' + encodeURIComponent( querypage.title ) + '&format=json',
  1426. json: true
  1427. }, function( srerror, srresponse, srbody ) {
  1428. if ( srbody && srbody.warnings ) log_warn(srbody.warnings);
  1429. if ( srerror || !srresponse || srresponse.statusCode !== 200 || !srbody || !srbody.query || !srbody.query.pages ) {
  1430. console.log( '- ' + ( srresponse && srresponse.statusCode ) + ': Error while getting the search results: ' + ( srerror || srbody && srbody.error && srbody.error.info ) );
  1431. msg.sendChannelError( spoiler + '<' + wiki.toLink() + querypage.title.toTitle() + '>' + spoiler );
  1432. if ( reaction ) reaction.removeEmoji();
  1433. }
  1434. else {
  1435. querypage = Object.values(srbody.query.pages)[0];
  1436. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  1437. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  1438. if ( querypage.imageinfo ) {
  1439. var filename = querypage.title.replace( body.query.namespaces['6']['*'] + ':', '' );
  1440. var pageimage = wiki.toLink() + 'Special:FilePath/' + filename.toTitle() + '?v=' + Date.now();
  1441. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pageimage );
  1442. else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + filename}] );
  1443. }
  1444. if ( querypage.categoryinfo ) {
  1445. var langCat = lang.search.category;
  1446. var category = [langCat.content];
  1447. if ( querypage.categoryinfo.size === 0 ) category.push(langCat.empty);
  1448. if ( querypage.categoryinfo.pages > 0 ) {
  1449. var pages = querypage.categoryinfo.pages;
  1450. category.push(( langCat.pages[pages] || langCat.pages['*' + pages % 100] || langCat.pages['*' + pages % 10] || langCat.pages.default ).replaceSave( '%s', pages ));
  1451. }
  1452. if ( querypage.categoryinfo.files > 0 ) {
  1453. var files = querypage.categoryinfo.files;
  1454. category.push(( langCat.files[files] || langCat.files['*' + files % 100] || langCat.files['*' + files % 10] || langCat.files.default ).replaceSave( '%s', files ));
  1455. }
  1456. if ( querypage.categoryinfo.subcats > 0 ) {
  1457. var subcats = querypage.categoryinfo.subcats;
  1458. category.push(( langCat.subcats[subcats] || langCat.subcats['*' + subcats % 100] || langCat.subcats['*' + subcats % 10] || langCat.subcats.default ).replaceSave( '%s', subcats ));
  1459. }
  1460. if ( msg.showEmbed() ) embed.addField( category[0], category.slice(1).join('\n') );
  1461. else text += '\n\n' + category.join('\n');
  1462. }
  1463. if ( querypage.title === body.query.general.mainpage && body.query.allmessages[0]['*'] ) {
  1464. embed.setDescription( body.query.allmessages[0]['*'] );
  1465. embed.setThumbnail( wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png' );
  1466. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, embed );
  1467. if ( reaction ) reaction.removeEmoji();
  1468. }
  1469. else request( {
  1470. uri: wiki.toLink() + encodeURIComponent( querypage.title.replace( / /g, '_' ) )
  1471. }, function( descerror, descresponse, descbody ) {
  1472. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  1473. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  1474. } else {
  1475. var thumbnail = wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png';
  1476. var parser = new htmlparser.Parser( {
  1477. onopentag: (tagname, attribs) => {
  1478. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  1479. var description = attribs.content.escapeFormatting();
  1480. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1481. embed.setDescription( description );
  1482. }
  1483. if ( tagname === 'meta' && attribs.property === 'og:image' && querypage.title !== body.query.general.mainpage ) {
  1484. thumbnail = attribs.content;
  1485. }
  1486. }
  1487. }, {decodeEntities:true} );
  1488. parser.write( descbody );
  1489. parser.end();
  1490. if ( !querypage.imageinfo ) embed.setThumbnail( thumbnail );
  1491. }
  1492. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, embed );
  1493. if ( reaction ) reaction.removeEmoji();
  1494. } );
  1495. }
  1496. } );
  1497. }
  1498. } );
  1499. }
  1500. else if ( querypage.ns === -1 ) {
  1501. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  1502. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink ).setThumbnail( wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png' );
  1503. var specialpage = body.query.specialpagealiases.find( sp => body.query.namespaces['-1']['*'] + ':' + sp.aliases[0].replace( /\_/g, ' ' ) === querypage.title.split('/')[0] );
  1504. specialpage = ( specialpage ? specialpage.realname : querypage.title.replace( body.query.namespaces['-1']['*'] + ':', '' ).split('/')[0] ).toLowerCase();
  1505. special_page(lang, msg, querypage.title, specialpage, embed, wiki, reaction, spoiler);
  1506. }
  1507. else {
  1508. var pagelink = wiki.toLink() + querypage.title.toTitle() + ( querystring ? '?' + querystring.toTitle() : '' ) + ( fragment ? '#' + fragment.toSection() : ( body.query.redirects && body.query.redirects[0].tofragment ? '#' + body.query.redirects[0].tofragment.toSection() : '' ) );
  1509. var text = '';
  1510. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  1511. if ( querypage.imageinfo ) {
  1512. var filename = querypage.title.replace( body.query.namespaces['6']['*'] + ':', '' );
  1513. var pageimage = wiki.toLink() + 'Special:FilePath/' + filename.toTitle() + '?v=' + Date.now();
  1514. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pageimage );
  1515. else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + filename}] );
  1516. }
  1517. if ( querypage.categoryinfo ) {
  1518. var langCat = lang.search.category;
  1519. var category = [langCat.content];
  1520. if ( querypage.categoryinfo.size === 0 ) category.push(langCat.empty);
  1521. if ( querypage.categoryinfo.pages > 0 ) {
  1522. var pages = querypage.categoryinfo.pages;
  1523. category.push(( langCat.pages[pages] || langCat.pages['*' + pages % 100] || langCat.pages['*' + pages % 10] || langCat.pages.default ).replaceSave( '%s', pages ));
  1524. }
  1525. if ( querypage.categoryinfo.files > 0 ) {
  1526. var files = querypage.categoryinfo.files;
  1527. category.push(( langCat.files[files] || langCat.files['*' + files % 100] || langCat.files['*' + files % 10] || langCat.files.default ).replaceSave( '%s', files ));
  1528. }
  1529. if ( querypage.categoryinfo.subcats > 0 ) {
  1530. var subcats = querypage.categoryinfo.subcats;
  1531. category.push(( langCat.subcats[subcats] || langCat.subcats['*' + subcats % 100] || langCat.subcats['*' + subcats % 10] || langCat.subcats.default ).replaceSave( '%s', subcats ));
  1532. }
  1533. if ( msg.showEmbed() ) embed.addField( category[0], category.slice(1).join('\n') );
  1534. else text += '\n\n' + category.join('\n');
  1535. }
  1536. if ( querypage.title === body.query.general.mainpage && body.query.allmessages[0]['*'] ) {
  1537. embed.setDescription( body.query.allmessages[0]['*'] );
  1538. embed.setThumbnail( wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png' );
  1539. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, embed );
  1540. if ( reaction ) reaction.removeEmoji();
  1541. }
  1542. else request( {
  1543. uri: wiki.toLink() + encodeURIComponent( querypage.title.replace( / /g, '_' ) )
  1544. }, function( descerror, descresponse, descbody ) {
  1545. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  1546. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  1547. } else {
  1548. var thumbnail = wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png';
  1549. var parser = new htmlparser.Parser( {
  1550. onopentag: (tagname, attribs) => {
  1551. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  1552. var description = attribs.content.escapeFormatting();
  1553. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1554. embed.setDescription( description );
  1555. }
  1556. if ( tagname === 'meta' && attribs.property === 'og:image' && querypage.title !== body.query.general.mainpage ) {
  1557. thumbnail = attribs.content;
  1558. }
  1559. }
  1560. }, {decodeEntities:true} );
  1561. parser.write( descbody );
  1562. parser.end();
  1563. if ( !querypage.imageinfo ) embed.setThumbnail( thumbnail );
  1564. }
  1565. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, embed );
  1566. if ( reaction ) reaction.removeEmoji();
  1567. } );
  1568. }
  1569. }
  1570. else if ( body.query.interwiki ) {
  1571. var inter = body.query.interwiki[0];
  1572. var intertitle = inter.title.substring(inter.iw.length + 1);
  1573. var regex = inter.url.match( /^(?:https?:)?\/\/(([a-z\d-]{1,50})\.(?:fandom\.com|wikia\.org)(?:(?!\/wiki\/)\/([a-z-]{1,8}))?)(?:\/wiki\/|\/?$)/ );
  1574. var maxselfcall = ( msg.channel.type === 'text' && msg.guild.id in patreons ? 10 : 5 );
  1575. if ( regex !== null && selfcall < maxselfcall ) {
  1576. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  1577. var iwtitle = decodeURIComponent( inter.url.replace( regex[0], '' ) ).replace( /\_/g, ' ' ).replaceSave( intertitle.replace( /\_/g, ' ' ), intertitle );
  1578. selfcall++;
  1579. fandom_check_wiki(lang, msg, iwtitle, 'https://' + regex[1] + '/', ' ?' + ( regex[3] ? regex[3] + '.' : '' ) + regex[2] + ' ', reaction, spoiler, querystring, fragment, selfcall);
  1580. } else {
  1581. if ( reaction ) reaction.removeEmoji();
  1582. console.log( '- Aborted, paused.' );
  1583. }
  1584. } else {
  1585. regex = inter.url.match( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.gamepedia\.com(?:\/|$)/ );
  1586. if ( regex !== null && selfcall < maxselfcall ) {
  1587. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  1588. var iwtitle = decodeURIComponent( inter.url.replace( regex[0], '' ) ).replace( /\_/g, ' ' ).replaceSave( intertitle.replace( /\_/g, ' ' ), intertitle );
  1589. selfcall++;
  1590. gamepedia_check_wiki(lang, msg, iwtitle, 'https://' + regex[1] + '.gamepedia.com/', ' !' + regex[1] + ' ', reaction, spoiler, querystring, fragment, selfcall);
  1591. } else {
  1592. if ( reaction ) reaction.removeEmoji();
  1593. console.log( '- Aborted, paused.' );
  1594. }
  1595. } else {
  1596. regex = inter.url.match( /^(?:https?:)?\/\/([a-z\d-]{1,50}\.(?:wikipedia|mediawiki|wiktionary|wikimedia|wikibooks|wikisource|wikidata|wikiversity|wikiquote|wikinews|wikivoyage)\.org)(?:\/wiki\/|\/?$)/ );
  1597. if ( regex !== null && selfcall < maxselfcall ) {
  1598. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  1599. var iwtitle = decodeURIComponent( inter.url.replace( regex[0], '' ) ).replace( /\_/g, ' ' ).replaceSave( intertitle.replace( /\_/g, ' ' ), intertitle );
  1600. selfcall++;
  1601. gamepedia_check_wiki(lang, msg, iwtitle, 'https://' + regex[1] + '/w/', cmd + inter.iw + ':', reaction, spoiler, querystring, fragment, selfcall);
  1602. } else {
  1603. if ( reaction ) reaction.removeEmoji();
  1604. console.log( '- Aborted, paused.' );
  1605. }
  1606. } else {
  1607. if ( fragment ) fragment = '#' + fragment.toSection();
  1608. if ( inter.url.includes( '#' ) ) {
  1609. if ( !fragment ) fragment = '#' + inter.url.split('#').slice(1).join('#');
  1610. inter.url = inter.url.split('#')[0];
  1611. }
  1612. if ( querystring ) inter.url += ( inter.url.includes( '?' ) ? '&' : '?' ) + querystring.toTitle();
  1613. msg.sendChannel( spoiler + ' ' + inter.url.replace( /@(here|everyone)/g, '%40$1' ) + fragment + ' ' + spoiler ).then( message => {
  1614. if ( message && selfcall === maxselfcall ) message.reactEmoji('⚠️');
  1615. } );
  1616. if ( reaction ) reaction.removeEmoji();
  1617. }
  1618. }
  1619. }
  1620. }
  1621. else if ( body.query.redirects ) {
  1622. var pagelink = wiki.toLink() + body.query.redirects[0].to.toTitle() + ( querystring ? '?' + querystring.toTitle() : '' ) + ( fragment ? '#' + fragment.toSection() : ( body.query.redirects[0].tofragment ? '#' + body.query.redirects[0].tofragment.toSection() : '' ) );
  1623. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.redirects[0].to.escapeFormatting() ).setURL( pagelink ).setThumbnail( wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png' );
  1624. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1625. if ( reaction ) reaction.removeEmoji();;
  1626. }
  1627. else {
  1628. var pagelink = wiki.toLink() + body.query.general.mainpage.toTitle() + linksuffix;
  1629. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.general.mainpage.escapeFormatting() ).setURL( pagelink ).setThumbnail( wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png' );
  1630. if ( body.query.allmessages[0]['*'] ) {
  1631. embed.setDescription( body.query.allmessages[0]['*'] );
  1632. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1633. if ( reaction ) reaction.removeEmoji();
  1634. }
  1635. else request( {
  1636. uri: wiki.toLink() + encodeURIComponent( body.query.general.mainpage.replace( / /g, '_' ) )
  1637. }, function( descerror, descresponse, descbody ) {
  1638. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  1639. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  1640. } else {
  1641. var parser = new htmlparser.Parser( {
  1642. onopentag: (tagname, attribs) => {
  1643. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  1644. var description = attribs.content.escapeFormatting();
  1645. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1646. embed.setDescription( description );
  1647. }
  1648. }
  1649. }, {decodeEntities:true} );
  1650. parser.write( descbody );
  1651. parser.end();
  1652. }
  1653. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1654. if ( reaction ) reaction.removeEmoji();
  1655. } );
  1656. }
  1657. }
  1658. } );
  1659. }
  1660. }
  1661. function special_page(lang, msg, title, specialpage, embed, wiki, reaction, spoiler) {
  1662. var overwrites = {
  1663. randompage: (lang, msg, args, embed, wiki, reaction, spoiler) => ( wiki.isFandom() ? fandom_random : gamepedia_random )(lang, msg, wiki, reaction, spoiler),
  1664. diff: (lang, msg, args, embed, wiki, reaction, spoiler) => ( wiki.isFandom() ? fandom_diff : gamepedia_diff )(lang, msg, args, wiki, reaction, spoiler, embed),
  1665. statistics: (lang, msg, args, embed, wiki, reaction, spoiler) => ( wiki.isFandom() ? fandom_overview : gamepedia_overview )(lang, msg, wiki, reaction, spoiler)
  1666. }
  1667. if ( specialpage in overwrites ) {
  1668. var args = title.split('/').slice(1,3);
  1669. overwrites[specialpage](lang, msg, args, embed, wiki, reaction, spoiler);
  1670. return;
  1671. }
  1672. var queryfunctions = {
  1673. title: query => query.querypage.results.map( result => {
  1674. return '[' + result.title.escapeFormatting() + '](' + wiki.toLink() + result.title.toTitle(true) + ')';
  1675. } ).join('\n'),
  1676. times: query => query.querypage.results.map( result => {
  1677. return result.value + '× [' + result.title.escapeFormatting() + '](' + wiki.toLink() + result.title.toTitle(true) + ')';
  1678. } ).join('\n'),
  1679. size: query => query.querypage.results.map( result => {
  1680. return result.value + ' bytes: [' + result.title.escapeFormatting() + '](' + wiki.toLink() + result.title.toTitle(true) + ')';
  1681. } ).join('\n'),
  1682. redirect: query => query.querypage.results.map( result => {
  1683. return '[' + result.title.replace( / /g, '_' ).escapeFormatting() + '](' + wiki.toLink() + result.title.toTitle(true) + '?redirect=no)' + ( result.databaseResult && result.databaseResult.rd_title ? ' → ' + result.databaseResult.rd_title.escapeFormatting() : '' );
  1684. } ).join('\n'),
  1685. doubleredirect: query => query.querypage.results.map( result => {
  1686. return '[' + result.title.replace( / /g, '_' ).escapeFormatting() + '](' + wiki.toLink() + result.title.toTitle(true) + '?redirect=no)' + ( result.databaseResult && result.databaseResult.b_title && result.databaseResult.c_title ? ' → ' + result.databaseResult.b_title.escapeFormatting() + ' → ' + result.databaseResult.c_title.escapeFormatting() : '' );
  1687. } ).join('\n'),
  1688. timestamp: query => query.querypage.results.map( result => {
  1689. return new Date(result.timestamp).toLocaleString(lang.dateformat, timeoptions).escapeFormatting() + ': [' + result.title.escapeFormatting() + '](' + wiki.toLink() + result.title.toTitle(true) + ')';
  1690. } ).join('\n'),
  1691. media: query => query.querypage.results.map( result => {
  1692. var ms = result.title.split(';');
  1693. return '**' + ms[1] + '**: ' + ms[2] + ' files (' + ms[3] + ' bytes)';
  1694. } ).join('\n'),
  1695. category: query => query.querypage.results.map( result => {
  1696. return result.value + '× [' + result.title.escapeFormatting() + '](' + wiki.toLink() + 'Category:' + result.title.toTitle(true) + ')';
  1697. } ).join('\n'),
  1698. gadget: query => query.querypage.results.map( result => {
  1699. result.title = result.title.replace( /^(?:.*:)?gadget-/, '' );
  1700. return '**' + result.title.escapeFormatting() + '**: ' + result.value + ' users (' + result.ns + ' active)';
  1701. } ).join('\n'),
  1702. recentchanges: query => query.recentchanges.map( result => {
  1703. return '[' + result.title.escapeFormatting() + '](' + wiki.toLink() + result.title.toTitle(true) + ( result.type === 'edit' ? '?diff=' + result.revid + '&oldid=' + result.old_revid : '' ) + ')';
  1704. } ).join('\n')
  1705. }
  1706. var querypages = {
  1707. ancientpages: ['&list=querypage&qplimit=10&qppage=Ancientpages', queryfunctions.timestamp],
  1708. brokenredirects: ['&list=querypage&qplimit=10&qppage=BrokenRedirects', queryfunctions.redirect],
  1709. deadendpages: ['&list=querypage&qplimit=10&qppage=Deadendpages', queryfunctions.title],
  1710. disambiguations: ['&list=querypage&qplimit=10&qppage=Disambiguations', queryfunctions.title],
  1711. doubleredirects: ['&list=querypage&qplimit=10&qppage=DoubleRedirects', queryfunctions.doubleredirect],
  1712. listduplicatedfiles: ['&list=querypage&qplimit=10&qppage=ListDuplicatedFiles', queryfunctions.times],
  1713. listredirects: ['&list=querypage&qplimit=10&qppage=Listredirects', queryfunctions.redirect],
  1714. lonelypages: ['&list=querypage&qplimit=10&qppage=Lonelypages', queryfunctions.title],
  1715. longpages: ['&list=querypage&qplimit=10&qppage=Longpages', queryfunctions.size],
  1716. mediastatistics: ['&list=querypage&qplimit=10&qppage=MediaStatistics', queryfunctions.media],
  1717. mostcategories: ['&list=querypage&qplimit=10&qppage=Mostcategories', queryfunctions.times],
  1718. mostimages: ['&list=querypage&qplimit=10&qppage=Mostimages', queryfunctions.times],
  1719. mostinterwikis: ['&list=querypage&qplimit=10&qppage=Mostinterwikis', queryfunctions.times],
  1720. mostlinkedcategories: ['&list=querypage&qplimit=10&qppage=Mostlinkedcategories', queryfunctions.times],
  1721. mostlinkedtemplates: ['&list=querypage&qplimit=10&qppage=Mostlinkedtemplates', queryfunctions.times],
  1722. mostlinked: ['&list=querypage&qplimit=10&qppage=Mostlinked', queryfunctions.times],
  1723. mostrevisions: ['&list=querypage&qplimit=10&qppage=Mostrevisions', queryfunctions.times],
  1724. fewestrevisions: ['&list=querypage&qplimit=10&qppage=Fewestrevisions', queryfunctions.times],
  1725. shortpages: ['&list=querypage&qplimit=10&qppage=Shortpages', queryfunctions.size],
  1726. uncategorizedcategories: ['&list=querypage&qplimit=10&qppage=Uncategorizedcategories', queryfunctions.title],
  1727. uncategorizedpages: ['&list=querypage&qplimit=10&qppage=Uncategorizedpages', queryfunctions.title],
  1728. uncategorizedimages: ['&list=querypage&qplimit=10&qppage=Uncategorizedimages', queryfunctions.title],
  1729. uncategorizedtemplates: ['&list=querypage&qplimit=10&qppage=Uncategorizedtemplates', queryfunctions.title],
  1730. unusedcategories: ['&list=querypage&qplimit=10&qppage=Unusedcategories', queryfunctions.title],
  1731. unusedimages: ['&list=querypage&qplimit=10&qppage=Unusedimages', queryfunctions.title],
  1732. wantedcategories: ['&list=querypage&qplimit=10&qppage=Wantedcategories', queryfunctions.times],
  1733. wantedfiles: ['&list=querypage&qplimit=10&qppage=Wantedfiles', queryfunctions.times],
  1734. wantedpages: ['&list=querypage&qplimit=10&qppage=Wantedpages', queryfunctions.times],
  1735. wantedtemplates: ['&list=querypage&qplimit=10&qppage=Wantedtemplates', queryfunctions.times],
  1736. unwatchedpages: ['&list=querypage&qplimit=10&qppage=Unwatchedpages', queryfunctions.title],
  1737. unusedtemplates: ['&list=querypage&qplimit=10&qppage=Unusedtemplates', queryfunctions.title],
  1738. withoutinterwiki: ['&list=querypage&qplimit=10&qppage=Withoutinterwiki', queryfunctions.title],
  1739. mostpopularcategories: ['&list=querypage&qplimit=10&qppage=Mostpopularcategories', queryfunctions.category],
  1740. mostimagesincontent: ['&list=querypage&qplimit=10&qppage=MostLinkedFilesInContent', queryfunctions.times],
  1741. unusedvideos: ['&list=querypage&qplimit=10&qppage=UnusedVideos', queryfunctions.title],
  1742. withoutimages: ['&list=querypage&qplimit=10&qppage=Withoutimages', queryfunctions.title],
  1743. nonportableinfoboxes: ['&list=querypage&qplimit=10&qppage=Nonportableinfoboxes', queryfunctions.title],
  1744. popularpages: ['&list=querypage&qplimit=10&qppage=Popularpages', queryfunctions.title],
  1745. pageswithoutinfobox: ['&list=querypage&qplimit=10&qppage=Pageswithoutinfobox', queryfunctions.title],
  1746. templateswithouttype: ['&list=querypage&qplimit=10&qppage=Templateswithouttype', queryfunctions.title],
  1747. allinfoboxes: ['&list=querypage&qplimit=10&qppage=AllInfoboxes', queryfunctions.title],
  1748. gadgetusage: ['&list=querypage&qplimit=10&qppage=GadgetUsage', queryfunctions.gadget],
  1749. recentchanges: ['&list=recentchanges&rctype=edit|new|log&rclimit=10', queryfunctions.recentchanges]
  1750. }
  1751. request( {
  1752. uri: wiki + 'api.php?action=query&meta=allmessages&amenableparser=true&amtitle=' + encodeURIComponent( title ) + '&ammessages=' + encodeURIComponent( specialpage ) + '-summary' + ( specialpage in querypages ? querypages[specialpage][0] : '' ) + '&format=json',
  1753. json: true
  1754. }, function( error, response, body ) {
  1755. if ( body && body.warnings ) log_warn(body.warnings);
  1756. if ( error || !response || response.statusCode !== 200 || !body ) {
  1757. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the special page: ' + ( error || body && body.error && body.error.info ) );
  1758. }
  1759. else {
  1760. if ( body.query.allmessages[0]['*'] ) {
  1761. var description = body.query.allmessages[0]['*'].toPlaintext();
  1762. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1763. embed.setDescription( description );
  1764. }
  1765. if ( msg.channel.type === 'text' && msg.guild.id in patreons && specialpage in querypages ) {
  1766. var text = querypages[specialpage][1](body.query);
  1767. var split = Discord.Util.splitMessage( text, {maxLength:1000} );
  1768. if ( split.length < text.length ) text = split[0];
  1769. embed.addField( lang.search.special, ( text || lang.search.empty ) );
  1770. }
  1771. }
  1772. msg.sendChannel( spoiler + '<' + embed.url + '>' + spoiler, embed );
  1773. if ( reaction ) reaction.removeEmoji();
  1774. } );
  1775. }
  1776. function gamepedia_user(lang, msg, namespace, username, wiki, linksuffix, querypage, contribs, reaction, spoiler) {
  1777. if ( /^(?:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{2})?|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}(?:\/\d{2,3})?)$/.test(username) ) {
  1778. request( {
  1779. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=blocks&bkprop=user|by|timestamp|expiry|reason&bkip=' + encodeURIComponent( username ) + '&format=json',
  1780. json: true
  1781. }, function( error, response, body ) {
  1782. if ( body && body.warnings ) log_warn(body.warnings);
  1783. if ( error || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.blocks ) {
  1784. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  1785. console.log( '- This wiki doesn\'t exist!' );
  1786. msg.reactEmoji('nowiki');
  1787. }
  1788. else if ( body && body.error && ( body.error.code === 'param_ip' || body.error.code === 'cidrtoobroad' ) ) {
  1789. if ( querypage.missing !== undefined || querypage.ns === -1 ) msg.reactEmoji('error');
  1790. else {
  1791. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  1792. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  1793. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  1794. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  1795. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  1796. embed.setTitle( displaytitle );
  1797. }
  1798. if ( querypage.pageprops && querypage.pageprops.description ) {
  1799. var description = htmlToPlain( querypage.pageprops.description );
  1800. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1801. embed.setDescription( description );
  1802. }
  1803. else if ( querypage.extract ) {
  1804. var extract = querypage.extract.escapeFormatting();
  1805. if ( extract.length > 2000 ) extract = extract.substring(0, 2000) + '\u2026';
  1806. embed.setDescription( extract );
  1807. }
  1808. if ( querypage.pageimage && querypage.original ) {
  1809. var pageimage = querypage.original.source;
  1810. embed.setThumbnail( pageimage );
  1811. } else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  1812. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1813. }
  1814. }
  1815. else {
  1816. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  1817. msg.sendChannelError( spoiler + '<' + wiki.toLink() + ( querypage.noRedirect ? namespace : contribs ) + username.toTitle() + linksuffix + '>' + spoiler );
  1818. }
  1819. if ( reaction ) reaction.removeEmoji();
  1820. }
  1821. else {
  1822. if ( !querypage.noRedirect || ( querypage.missing === undefined && querypage.ns !== -1 ) ) namespace = contribs;
  1823. var blocks = body.query.blocks.map( block => {
  1824. var isBlocked = false;
  1825. var blockedtimestamp = new Date(block.timestamp).toLocaleString(lang.dateformat, timeoptions);
  1826. var blockexpiry = block.expiry;
  1827. if ( blockexpiry === 'infinity' ) {
  1828. blockexpiry = lang.user.block.until_infinity;
  1829. isBlocked = true;
  1830. } else if ( blockexpiry ) {
  1831. if ( Date.parse(blockexpiry) > Date.now() ) isBlocked = true;
  1832. blockexpiry = new Date(blockexpiry).toLocaleString(lang.dateformat, timeoptions);
  1833. }
  1834. if ( isBlocked ) return [lang.user.block.header.replaceSave( '%s', block.user ).escapeFormatting(), lang.user.block[( block.reason ? 'text' : 'noreason' )].replaceSave( '%1$s', blockedtimestamp ).replaceSave( '%2$s', blockexpiry ).replaceSave( '%3$s', '[[User:' + block.by + '|' + block.by + ']]' ).replaceSave( '%4$s', block.reason )];
  1835. } ).filter( block => block !== undefined );
  1836. if ( username.includes( '/' ) ) {
  1837. var rangeprefix = username;
  1838. if ( username.includes( ':' ) ) {
  1839. var range = parseInt(username.replace( /^.+\/(\d{2,3})$/, '$1' ), 10);
  1840. if ( range === 128 ) username = username.replace( /^(.+)\/\d{2,3}$/, '$1' );
  1841. else if ( range >= 112 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){7}).+$/, '$1' );
  1842. else if ( range >= 96 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){6}).+$/, '$1' );
  1843. else if ( range >= 80 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){5}).+$/, '$1' );
  1844. else if ( range >= 64 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){4}).+$/, '$1' );
  1845. else if ( range >= 48 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){3}).+$/, '$1' );
  1846. else if ( range >= 32 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){2}).+$/, '$1' );
  1847. else if ( range >= 19 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){1}).+$/, '$1' );
  1848. }
  1849. else {
  1850. var range = parseInt(username.substring(username.length - 2), 10);
  1851. if ( range === 32 ) username = username.replace( /^(.+)\/\d{2}$/, '$1' );
  1852. else if ( range >= 24 ) rangeprefix = username.replace( /^((?:\d{1,3}\.){3}).+$/, '$1' );
  1853. else if ( range >= 16 ) rangeprefix = username.replace( /^((?:\d{1,3}\.){2}).+$/, '$1' );
  1854. }
  1855. }
  1856. request( {
  1857. uri: wiki + 'api.php?action=query&list=usercontribs&ucprop=&uclimit=50' + ( username.includes( '/' ) ? '&ucuserprefix=' + encodeURIComponent( rangeprefix ) : '&ucuser=' + encodeURIComponent( username ) ) + '&format=json',
  1858. json: true
  1859. }, function( ucerror, ucresponse, ucbody ) {
  1860. if ( rangeprefix && !username.includes( '/' ) ) username = rangeprefix;
  1861. if ( ucbody && ucbody.warnings ) log_warn(ucbody.warnings);
  1862. if ( ucerror || !ucresponse || ucresponse.statusCode !== 200 || !ucbody || ucbody.batchcomplete === undefined || !ucbody.query || !ucbody.query.usercontribs ) {
  1863. if ( ucbody && ucbody.error && ucbody.error.code === 'baduser_ucuser' ) {
  1864. msg.reactEmoji('error');
  1865. }
  1866. else {
  1867. console.log( '- ' + ( ucresponse && ucresponse.statusCode ) + ': Error while getting the search results: ' + ( ucerror || ucbody && ucbody.error && ucbody.error.info ) );
  1868. msg.sendChannelError( spoiler + '<' + wiki.toLink() + namespace + username.toTitle() + linksuffix + '>' + spoiler );
  1869. }
  1870. if ( reaction ) reaction.removeEmoji();
  1871. }
  1872. else {
  1873. var editcount = [lang.user.info.editcount, ( username.includes( '/' ) && ( ( username.includes( ':' ) && range % 16 ) || range % 8 ) ? '~' : '' ) + ucbody.query.usercontribs.length + ( ucbody.continue ? '+' : '' )];
  1874. var pagelink = wiki.toLink() + namespace + username.toTitle() + linksuffix;
  1875. if ( msg.showEmbed() ) {
  1876. var text = '<' + pagelink + '>';
  1877. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( username ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink() + contribs + username.toTitle(true) + ')' );
  1878. if ( blocks.length ) blocks.forEach( block => embed.addField( block[0], block[1].toMarkdown(wiki) ) );
  1879. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) embed.addField( '\u200b', '<a:loading:641343250661113886> **' + lang.user.info.loading + '**' );
  1880. }
  1881. else {
  1882. var embed = {};
  1883. var text = '<' + pagelink + '>\n\n' + editcount.join(' ');
  1884. if ( blocks.length ) blocks.forEach( block => text += '\n\n**' + block[0] + '**\n' + block[1].toPlaintext() );
  1885. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) text += '\n\n<a:loading:641343250661113886> **' + lang.user.info.loading + '**';
  1886. }
  1887. msg.sendChannel( spoiler + text + spoiler, embed ).then( message => global_block(lang, message, username, text, embed, wiki, spoiler) );
  1888. if ( reaction ) reaction.removeEmoji();
  1889. }
  1890. } );
  1891. }
  1892. } );
  1893. } else {
  1894. request( {
  1895. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=users&usprop=blockinfo|groups|groupmemberships|editcount|registration|gender&ususers=' + encodeURIComponent( username ) + '&format=json',
  1896. json: true
  1897. }, function( error, response, body ) {
  1898. if ( body && body.warnings ) log_warn(body.warnings);
  1899. if ( error || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.users[0] ) {
  1900. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  1901. console.log( '- This wiki doesn\'t exist!' );
  1902. msg.reactEmoji('nowiki');
  1903. }
  1904. else {
  1905. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  1906. msg.sendChannelError( spoiler + '<' + wiki.toLink() + namespace + username.toTitle() + linksuffix + '>' + spoiler );
  1907. }
  1908. }
  1909. else {
  1910. var queryuser = body.query.users[0];
  1911. if ( queryuser.missing !== undefined || queryuser.invalid !== undefined ) {
  1912. if ( querypage.missing !== undefined || querypage.ns === -1 ) msg.reactEmoji('🤷');
  1913. else {
  1914. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  1915. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  1916. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  1917. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  1918. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  1919. embed.setTitle( displaytitle );
  1920. }
  1921. if ( querypage.pageprops && querypage.pageprops.description ) {
  1922. var description = htmlToPlain( querypage.pageprops.description );
  1923. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1924. embed.setDescription( description );
  1925. }
  1926. else if ( querypage.extract ) {
  1927. var extract = querypage.extract.escapeFormatting();
  1928. if ( extract.length > 2000 ) extract = extract.substring(0, 2000) + '\u2026';
  1929. embed.setDescription( extract );
  1930. }
  1931. if ( querypage.pageimage && querypage.original ) {
  1932. var pageimage = querypage.original.source;
  1933. embed.setThumbnail( pageimage );
  1934. } else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  1935. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  1936. }
  1937. if ( reaction ) reaction.removeEmoji();
  1938. }
  1939. else {
  1940. username = queryuser.name;
  1941. var gender = [lang.user.info.gender];
  1942. switch (queryuser.gender) {
  1943. case 'male':
  1944. gender.push(lang.user.gender.male);
  1945. break;
  1946. case 'female':
  1947. gender.push(lang.user.gender.female);
  1948. break;
  1949. default:
  1950. gender.push(lang.user.gender.unknown);
  1951. }
  1952. var registration = [lang.user.info.registration, new Date(queryuser.registration).toLocaleString(lang.dateformat, timeoptions)];
  1953. var editcount = [lang.user.info.editcount, queryuser.editcount];
  1954. var groups = queryuser.groups;
  1955. var group = [lang.user.info.group];
  1956. var grouplist = lang.user.groups;
  1957. for ( var i = 0; i < grouplist.length; i++ ) {
  1958. if ( groups.includes( grouplist[i][0] ) && ( group.length === 1 || grouplist[i][0] !== 'user' ) ) {
  1959. var thisSite = allSites.find( site => site.wiki_domain === body.query.general.servername );
  1960. if ( grouplist[i][0] === 'wiki_manager' && thisSite && thisSite.wiki_managers.includes( username ) ) {
  1961. group.push('**' + grouplist[i][1] + '**');
  1962. }
  1963. else if ( !groups.includes( 'global_' + grouplist[i][0] ) || queryuser.groupmemberships.some( member => member.group === grouplist[i][0] ) ) {
  1964. group.push(grouplist[i][1]);
  1965. }
  1966. }
  1967. }
  1968. var isBlocked = false;
  1969. var blockedtimestamp = new Date(queryuser.blockedtimestamp).toLocaleString(lang.dateformat, timeoptions);
  1970. var blockexpiry = queryuser.blockexpiry;
  1971. if ( blockexpiry === 'infinity' ) {
  1972. blockexpiry = lang.user.block.until_infinity;
  1973. isBlocked = true;
  1974. } else if ( blockexpiry ) {
  1975. var blockexpirydate = blockexpiry.replace( /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2,3})/, '$1-$2-$3T$4:$5:$6Z' );
  1976. blockexpiry = new Date(blockexpirydate).toLocaleString(lang.dateformat, timeoptions);
  1977. if ( Date.parse(blockexpirydate) > Date.now() ) isBlocked = true;
  1978. }
  1979. var blockedby = '[[User:' + queryuser.blockedby + '|' + queryuser.blockedby + ']]';
  1980. var blockreason = queryuser.blockreason;
  1981. var block = [lang.user.block.header.replaceSave( '%s', username ).escapeFormatting(), lang.user.block[( blockreason ? 'text' : 'noreason' )].replaceSave( '%1$s', blockedtimestamp ).replaceSave( '%2$s', blockexpiry ).replaceSave( '%3$s', blockedby ).replaceSave( '%4$s', blockreason )];
  1982. var pagelink = wiki.toLink() + namespace + username.toTitle() + linksuffix;
  1983. if ( msg.showEmbed() ) {
  1984. var text = '<' + pagelink + '>';
  1985. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( username.escapeFormatting() ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink() + contribs + username.toTitle(true) + ')', true ).addField( group[0], group.slice(1).join(',\n'), true ).addField( gender[0], gender[1], true ).addField( registration[0], registration[1], true );
  1986. if ( querypage.pageprops && querypage.pageprops.description ) {
  1987. var description = htmlToPlain( querypage.pageprops.description );
  1988. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  1989. embed.setDescription( description );
  1990. }
  1991. else if ( querypage.extract ) {
  1992. var extract = querypage.extract.escapeFormatting();
  1993. if ( extract.length > 2000 ) extract = extract.substring(0, 2000) + '\u2026';
  1994. embed.setDescription( extract );
  1995. }
  1996. }
  1997. else {
  1998. var embed = {};
  1999. var text = '<' + pagelink + '>\n\n' + gender.join(' ') + '\n' + registration.join(' ') + '\n' + editcount.join(' ') + '\n' + group[0] + ' ' + group.slice(1).join(', ');
  2000. }
  2001. if ( wiki.endsWith( '.gamepedia.com/' ) ) request( {
  2002. uri: wiki + 'api.php?action=profile&do=getPublicProfile&user_name=' + encodeURIComponent( username ) + '&format=json',
  2003. json: true
  2004. }, function( perror, presponse, pbody ) {
  2005. if ( perror || !presponse || presponse.statusCode !== 200 || !pbody || pbody.error || pbody.errormsg || !pbody.profile ) {
  2006. console.log( '- ' + ( presponse && presponse.statusCode ) + ': Error while getting the user profile: ' + ( perror || pbody && ( pbody.error && pbody.error.info || pbody.errormsg ) ) );
  2007. }
  2008. else if ( pbody.profile['link-discord'] ) {
  2009. if ( msg.channel.type === 'text' ) var discordmember = msg.guild.members.find( member => {
  2010. return member.user.tag === pbody.profile['link-discord'].replace( /^\s*([^@#:]{2,32}?)\s*#(\d{4,6})\s*$/, '$1#$2' );
  2011. } );
  2012. var discordname = [lang.user.info.discord,pbody.profile['link-discord'].escapeFormatting()];
  2013. if ( discordmember ) {
  2014. if ( msg.showEmbed() ) discordname[1] = discordmember.toString();
  2015. else if ( discordmember.nickname ) discordname[1] += ' (' + discordmember.nickname.escapeFormatting() + ')';
  2016. }
  2017. if ( msg.showEmbed() ) embed.addField( discordname[0], discordname[1], true );
  2018. else text += '\n' + discordname.join(' ');
  2019. }
  2020. if ( msg.showEmbed() ) {
  2021. if ( isBlocked ) embed.addField( block[0], block[1].toMarkdown(wiki) );
  2022. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) embed.addField( '\u200b', '<a:loading:641343250661113886> **' + lang.user.info.loading + '**' );
  2023. }
  2024. else {
  2025. if ( isBlocked ) text += '\n\n**' + block[0] + '**\n' + block[1].toPlaintext();
  2026. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) text += '\n\n<a:loading:641343250661113886> **' + lang.user.info.loading + '**';
  2027. }
  2028. msg.sendChannel( spoiler + text + spoiler, embed ).then( message => global_block(lang, message, username, text, embed, wiki, spoiler) );
  2029. if ( reaction ) reaction.removeEmoji();
  2030. } );
  2031. else {
  2032. if ( isBlocked ) {
  2033. if ( msg.showEmbed() ) embed.addField( block[0], block[1].toMarkdown(wiki) );
  2034. else text += '\n\n**' + block[0] + '**\n' + block[1].toPlaintext();
  2035. }
  2036. msg.sendChannel( spoiler + text + spoiler, embed );
  2037. if ( reaction ) reaction.removeEmoji();
  2038. }
  2039. }
  2040. }
  2041. } );
  2042. }
  2043. }
  2044. function fandom_user(lang, msg, namespace, username, wiki, linksuffix, querypage, contribs, reaction, spoiler) {
  2045. if ( /^(?:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{2})?|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}(?:\/\d{2,3})?)$/.test(username) ) {
  2046. request( {
  2047. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=blocks&bkprop=user|by|timestamp|expiry|reason&bkip=' + encodeURIComponent( username ) + '&format=json',
  2048. json: true
  2049. }, function( error, response, body ) {
  2050. if ( body && body.warnings ) log_warn(body.warnings);
  2051. if ( error || !response || response.statusCode !== 200 || !body || !body.query || !body.query.blocks ) {
  2052. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  2053. console.log( '- This wiki doesn\'t exist!' );
  2054. msg.reactEmoji('nowiki');
  2055. if ( reaction ) reaction.removeEmoji();
  2056. }
  2057. else if ( body && body.error && ( body.error.code === 'param_ip' || body.error.code === 'cidrtoobroad' ) ) {
  2058. if ( querypage.missing !== undefined || querypage.ns === -1 ) {
  2059. msg.reactEmoji('error');
  2060. if ( reaction ) reaction.removeEmoji();
  2061. }
  2062. else {
  2063. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  2064. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  2065. request( {
  2066. uri: wiki.toLink() + encodeURIComponent( querypage.title.replace( / /g, '_' ) )
  2067. }, function( descerror, descresponse, descbody ) {
  2068. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  2069. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  2070. } else {
  2071. var thumbnail = wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png';
  2072. var parser = new htmlparser.Parser( {
  2073. onopentag: (tagname, attribs) => {
  2074. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  2075. var description = attribs.content.escapeFormatting();
  2076. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  2077. embed.setDescription( description );
  2078. }
  2079. if ( tagname === 'meta' && attribs.property === 'og:image' ) {
  2080. thumbnail = attribs.content;
  2081. }
  2082. }
  2083. }, {decodeEntities:true} );
  2084. parser.write( descbody );
  2085. parser.end();
  2086. embed.setThumbnail( thumbnail );
  2087. }
  2088. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  2089. if ( reaction ) reaction.removeEmoji();
  2090. } );
  2091. }
  2092. }
  2093. else {
  2094. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  2095. msg.sendChannelError( spoiler + '<' + wiki.toLink() + ( querypage.noRedirect ? namespace : contribs ) + username.toTitle() + linksuffix + '>' + spoiler );
  2096. if ( reaction ) reaction.removeEmoji();
  2097. }
  2098. }
  2099. else {
  2100. if ( !querypage.noRedirect || ( querypage.missing === undefined && querypage.ns !== -1 ) ) namespace = contribs;
  2101. var blocks = body.query.blocks.map( block => {
  2102. var isBlocked = false;
  2103. var blockedtimestamp = new Date(block.timestamp).toLocaleString(lang.dateformat, timeoptions);
  2104. var blockexpiry = block.expiry;
  2105. if ( blockexpiry === 'infinity' ) {
  2106. blockexpiry = lang.user.block.until_infinity;
  2107. isBlocked = true;
  2108. } else if ( blockexpiry ) {
  2109. if ( Date.parse(blockexpiry) > Date.now() ) isBlocked = true;
  2110. blockexpiry = new Date(blockexpiry).toLocaleString(lang.dateformat, timeoptions);
  2111. }
  2112. if ( isBlocked ) return [lang.user.block.header.replaceSave( '%s', block.user ).escapeFormatting(), lang.user.block[( block.reason ? 'text' : 'noreason' )].replaceSave( '%1$s', blockedtimestamp ).replaceSave( '%2$s', blockexpiry ).replaceSave( '%3$s', '[[User:' + block.by + '|' + block.by + ']]' ).replaceSave( '%4$s', block.reason )];
  2113. } ).filter( block => block !== undefined );
  2114. if ( username.includes( '/' ) ) {
  2115. var rangeprefix = username;
  2116. if ( username.includes( ':' ) ) {
  2117. var range = parseInt(username.replace( /^.+\/(\d{2,3})$/, '$1' ), 10);
  2118. if ( range === 128 ) username = username.replace( /^(.+)\/\d{2,3}$/, '$1' );
  2119. else if ( range >= 112 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){7}).+$/, '$1' );
  2120. else if ( range >= 96 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){6}).+$/, '$1' );
  2121. else if ( range >= 80 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){5}).+$/, '$1' );
  2122. else if ( range >= 64 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){4}).+$/, '$1' );
  2123. else if ( range >= 48 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){3}).+$/, '$1' );
  2124. else if ( range >= 32 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){2}).+$/, '$1' );
  2125. else if ( range >= 19 ) rangeprefix = username.replace( /^((?:[\dA-F]{1,4}:){1}).+$/, '$1' );
  2126. }
  2127. else {
  2128. var range = parseInt(username.substring(username.length - 2), 10);
  2129. if ( range === 32 ) username = username.replace( /^(.+)\/\d{2}$/, '$1' );
  2130. else if ( range >= 24 ) rangeprefix = username.replace( /^((?:\d{1,3}\.){3}).+$/, '$1' );
  2131. else if ( range >= 16 ) rangeprefix = username.replace( /^((?:\d{1,3}\.){2}).+$/, '$1' );
  2132. }
  2133. }
  2134. request( {
  2135. uri: wiki + 'api.php?action=query&list=usercontribs&ucprop=&uclimit=50&ucuser=' + encodeURIComponent( username ) + '&format=json',
  2136. json: true
  2137. }, function( ucerror, ucresponse, ucbody ) {
  2138. if ( rangeprefix && !username.includes( '/' ) ) username = rangeprefix;
  2139. if ( ucbody && ucbody.warnings ) log_warn(ucbody.warnings);
  2140. if ( ucerror || !ucresponse || ucresponse.statusCode !== 200 || !ucbody || !ucbody.query || !ucbody.query.usercontribs ) {
  2141. if ( ucbody && ucbody.error && ucbody.error.code === 'baduser_ucuser' ) {
  2142. msg.reactEmoji('error');
  2143. }
  2144. else {
  2145. console.log( '- ' + ( ucresponse && ucresponse.statusCode ) + ': Error while getting the search results: ' + ( ucerror || ucbody && ucbody.error && ucbody.error.info ) );
  2146. msg.sendChannelError( spoiler + '<' + wiki.toLink() + namespace + username.toTitle() + linksuffix + '>' + spoiler );
  2147. }
  2148. if ( reaction ) reaction.removeEmoji();
  2149. }
  2150. else {
  2151. var editcount = [lang.user.info.editcount, ( username.includes( '/' ) ? '~' : '' ) + ucbody.query.usercontribs.length + ( ucbody.continue ? '+' : '' )];
  2152. var pagelink = wiki.toLink() + namespace + username.toTitle() + linksuffix;
  2153. if ( msg.showEmbed() ) {
  2154. var text = '<' + pagelink + '>';
  2155. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( username ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink() + contribs + username.toTitle(true) + ')' );
  2156. if ( blocks.length ) blocks.forEach( block => embed.addField( block[0], block[1].toMarkdown(wiki) ) );
  2157. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) embed.addField( '\u200b', '<a:loading:641343250661113886> **' + lang.user.info.loading + '**' );
  2158. }
  2159. else {
  2160. var embed = {};
  2161. var text = '<' + pagelink + '>\n\n' + editcount.join(' ');
  2162. if ( blocks.length ) blocks.forEach( block => text += '\n\n**' + block[0] + '**\n' + block[1].toPlaintext() );
  2163. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) text += '\n\n<a:loading:641343250661113886> **' + lang.user.info.loading + '**';
  2164. }
  2165. msg.sendChannel( spoiler + text + spoiler, embed ).then( message => global_block(lang, message, username, text, embed, wiki, spoiler) );
  2166. if ( reaction ) reaction.removeEmoji();
  2167. }
  2168. } );
  2169. }
  2170. } );
  2171. } else {
  2172. request( {
  2173. uri: wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-Wiki_Manager&amenableparser=true&siprop=general&list=users&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + encodeURIComponent( username ) + '&format=json',
  2174. json: true
  2175. }, function( error, response, body ) {
  2176. if ( body && body.warnings ) log_warn(body.warnings);
  2177. if ( error || !response || response.statusCode !== 200 || !body || !body.query || !body.query.users ) {
  2178. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  2179. console.log( '- This wiki doesn\'t exist!' );
  2180. msg.reactEmoji('nowiki');
  2181. }
  2182. else {
  2183. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  2184. msg.sendChannelError( spoiler + '<' + wiki.toLink() + namespace + username.toTitle() + linksuffix + '>' + spoiler );
  2185. }
  2186. if ( reaction ) reaction.removeEmoji();
  2187. }
  2188. else {
  2189. var queryuser = body.query.users[0];
  2190. if ( !queryuser ) {
  2191. if ( querypage.missing !== undefined || querypage.ns === -1 ) {
  2192. msg.reactEmoji('🤷');
  2193. if ( reaction ) reaction.removeEmoji();
  2194. }
  2195. else {
  2196. var pagelink = wiki.toLink() + querypage.title.toTitle() + linksuffix;
  2197. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  2198. request( {
  2199. uri: wiki.toLink() + encodeURIComponent( querypage.title.replace( / /g, '_' ) )
  2200. }, function( descerror, descresponse, descbody ) {
  2201. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  2202. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  2203. } else {
  2204. var thumbnail = wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png';
  2205. var parser = new htmlparser.Parser( {
  2206. onopentag: (tagname, attribs) => {
  2207. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  2208. var description = attribs.content.escapeFormatting();
  2209. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  2210. embed.setDescription( description );
  2211. }
  2212. if ( tagname === 'meta' && attribs.property === 'og:image' ) {
  2213. thumbnail = attribs.content;
  2214. }
  2215. }
  2216. }, {decodeEntities:true} );
  2217. parser.write( descbody );
  2218. parser.end();
  2219. embed.setThumbnail( thumbnail );
  2220. }
  2221. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  2222. if ( reaction ) reaction.removeEmoji();
  2223. } );
  2224. }
  2225. }
  2226. else {
  2227. username = queryuser.name;
  2228. var gender = [lang.user.info.gender];
  2229. switch (queryuser.gender) {
  2230. case 'male':
  2231. gender.push(lang.user.gender.male);
  2232. break;
  2233. case 'female':
  2234. gender.push(lang.user.gender.female);
  2235. break;
  2236. default:
  2237. gender.push(lang.user.gender.unknown);
  2238. }
  2239. var registration = [lang.user.info.registration, new Date(queryuser.registration).toLocaleString(lang.dateformat, timeoptions)];
  2240. var editcount = [lang.user.info.editcount, queryuser.editcount];
  2241. var groups = queryuser.groups;
  2242. var group = [lang.user.info.group];
  2243. var grouplist = lang.user.groups;
  2244. for ( var i = 0; i < grouplist.length; i++ ) {
  2245. if ( groups.includes( grouplist[i][0] ) && ( group.length === 1 || grouplist[i][0] !== 'user' ) ) {
  2246. if ( grouplist[i][0] === 'wiki-manager' && body.query.allmessages[0]['*'] === username ) {
  2247. group.push('**' + grouplist[i][1] + '**');
  2248. }
  2249. else group.push(grouplist[i][1]);
  2250. }
  2251. }
  2252. var isBlocked = false;
  2253. var blockedtimestamp = new Date(queryuser.blockedtimestamp).toLocaleString(lang.dateformat, timeoptions);
  2254. var blockexpiry = queryuser.blockexpiry;
  2255. if ( blockexpiry === 'infinity' ) {
  2256. blockexpiry = lang.user.block.until_infinity;
  2257. isBlocked = true;
  2258. } else if ( blockexpiry ) {
  2259. var blockexpirydate = blockexpiry.replace( /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2,3})/, '$1-$2-$3T$4:$5:$6Z' );
  2260. blockexpiry = new Date(blockexpirydate).toLocaleString(lang.dateformat, timeoptions);
  2261. if ( Date.parse(blockexpirydate) > Date.now() ) isBlocked = true;
  2262. }
  2263. var blockedby = '[[User:' + queryuser.blockedby + '|' + queryuser.blockedby + ']]';
  2264. var blockreason = queryuser.blockreason;
  2265. var block = [lang.user.block.header.replaceSave( '%s', username ).escapeFormatting(), lang.user.block['nofrom' + ( blockreason ? 'text' : 'noreason' )].replaceSave( '%1$s', blockedtimestamp ).replaceSave( '%2$s', blockexpiry ).replaceSave( '%3$s', blockedby ).replaceSave( '%4$s', blockreason )];
  2266. var pagelink = wiki.toLink() + namespace + username.toTitle() + linksuffix;
  2267. if ( msg.showEmbed() ) {
  2268. var text = '<' + pagelink + '>';
  2269. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( username.escapeFormatting() ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink() + contribs + username.toTitle(true) + ')', true ).addField( group[0], group.slice(1).join(',\n'), true ).addField( gender[0], gender[1], true ).addField( registration[0], registration[1], true );
  2270. }
  2271. else {
  2272. var embed = {};
  2273. var text = '<' + pagelink + '>\n\n' + gender.join(' ') + '\n' + registration.join(' ') + '\n' + editcount.join(' ') + '\n' + group[0] + ' ' + group.slice(1).join(', ');
  2274. }
  2275. request( {
  2276. uri: 'https://services.fandom.com/user-attribute/user/' + queryuser.userid + '?format=json'
  2277. }, function( perror, presponse, pbody ) {
  2278. try {
  2279. if ( pbody ) pbody = JSON.parse(pbody);
  2280. if ( perror || !presponse || presponse.statusCode !== 200 || !pbody || pbody.title || !pbody._embedded || !pbody._embedded.properties ) {
  2281. if ( !( pbody && pbody.status === 404 ) ) console.log( '- ' + ( presponse && presponse.statusCode ) + ': Error while getting the user profile: ' + ( perror || pbody && pbody.title ) );
  2282. }
  2283. else {
  2284. var profile = pbody._embedded.properties;
  2285. var discordfield = profile.find( field => field.name === 'discordHandle' );
  2286. var avatarfield = profile.find( field => field.name === 'avatar' );
  2287. if ( discordfield && discordfield.value ) {
  2288. discordfield.value = htmlToPlain( discordfield.value );
  2289. if ( msg.channel.type === 'text' ) var discordmember = msg.guild.members.find( member => {
  2290. return member.user.tag.escapeFormatting() === discordfield.value.replace( /^\s*([^@#:]{2,32}?)\s*#(\d{4,6})\s*$/, '$1#$2' );
  2291. } );
  2292. var discordname = [lang.user.info.discord,discordfield.value];
  2293. if ( discordmember ) {
  2294. if ( msg.showEmbed() ) discordname[1] = discordmember.toString();
  2295. else if ( discordmember.nickname ) discordname[1] += ' (' + discordmember.nickname.escapeFormatting() + ')';
  2296. }
  2297. if ( msg.showEmbed() ) embed.addField( discordname[0], discordname[1], true );
  2298. else text += '\n' + discordname.join(' ');
  2299. }
  2300. if ( msg.showEmbed() && avatarfield && avatarfield.value ) embed.setThumbnail( avatarfield.value );
  2301. }
  2302. }
  2303. catch ( jsonerror ) {
  2304. console.log( '- ' + ( presponse && presponse.statusCode ) + ': Error while getting the user profile: ' + ( perror || jsonerror ) );
  2305. }
  2306. if ( msg.showEmbed() ) {
  2307. if ( isBlocked ) embed.addField( block[0], block[1].toMarkdown(wiki) );
  2308. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) embed.addField( '\u200b', '<a:loading:641343250661113886> **' + lang.user.info.loading + '**' );
  2309. }
  2310. else {
  2311. if ( isBlocked ) text += '\n\n**' + block[0] + '**\n' + block[1].toPlaintext();
  2312. if ( msg.channel.type === 'text' && msg.guild.id in patreons ) text += '\n\n<a:loading:641343250661113886> **' + lang.user.info.loading + '**';
  2313. }
  2314. msg.sendChannel( spoiler + text + spoiler, embed ).then( message => global_block(lang, message, username, text, embed, wiki, spoiler) );
  2315. if ( reaction ) reaction.removeEmoji();
  2316. } );
  2317. }
  2318. }
  2319. } );
  2320. }
  2321. }
  2322. function global_block(lang, msg, username, text, embed, wiki, spoiler) {
  2323. if ( !msg || msg.channel.type !== 'text' || !( msg.guild.id in patreons ) ) return;
  2324. if ( msg.showEmbed() ) embed.fields.pop();
  2325. else {
  2326. let splittext = text.split('\n\n');
  2327. splittext.pop();
  2328. text = splittext.join('\n\n');
  2329. }
  2330. if ( wiki.isFandom() ) request( {
  2331. uri: 'https://community.fandom.com/Special:Contributions/' + encodeURIComponent( username ) + '?limit=1',
  2332. jar: cookieJar
  2333. }, function( error, response, body ) {
  2334. if ( error || !response || response.statusCode !== 200 || !body ) {
  2335. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the global block: ' + error );
  2336. }
  2337. else {
  2338. let $ = cheerio.load(body);
  2339. if ( $('#mw-content-text .errorbox').length ) {
  2340. if ( msg.showEmbed() ) embed.addField( lang.user.gblock.disabled, '\u200b' );
  2341. else text += '\n\n**' + lang.user.gblock.disabled + '**';
  2342. }
  2343. else if ( $('.mw-warning-with-logexcerpt').length && !$(".mw-warning-with-logexcerpt .mw-logline-block").length ) {
  2344. if ( msg.showEmbed() ) embed.addField( lang.user.gblock.header.replaceSave( '%s', username ).escapeFormatting(), '\u200b' );
  2345. else text += '\n\n**' + lang.user.gblock.header.replaceSave( '%s', username ).escapeFormatting() + '**';
  2346. }
  2347. }
  2348. if ( !/^(?:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{2})?|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}(?:\/\d{2,3})?)$/.test(username) ) request( {
  2349. uri: 'https://community.fandom.com/wiki/Special:Editcount/' + encodeURIComponent( username ),
  2350. jar: cookieJar
  2351. }, function( gerror, gresponse, gbody ) {
  2352. if ( gerror || !gresponse || gresponse.statusCode !== 200 || !gbody ) {
  2353. console.log( '- ' + ( gresponse && gresponse.statusCode ) + ': Error while getting the global edit count: ' + gerror );
  2354. }
  2355. else {
  2356. let $ = cheerio.load(gbody);
  2357. var globaledits = $('#editcount .TablePager th').eq(7).text().replace( /[,\.]/g, '' );
  2358. if ( globaledits ) {
  2359. if ( msg.showEmbed() ) embed.fields.splice(1, 0, {name:lang.user.info.globaleditcount,value:globaledits,inline:true});
  2360. else {
  2361. let splittext = text.split('\n');
  2362. splittext.splice(5, 0, lang.user.info.globaleditcount + ' ' + globaledits);
  2363. text = splittext.join('\n');
  2364. }
  2365. }
  2366. }
  2367. msg.edit( spoiler + text + spoiler, embed ).catch(log_error);
  2368. } );
  2369. else msg.edit( spoiler + text + spoiler, embed ).catch(log_error);
  2370. } );
  2371. else if ( wiki.endsWith( '.gamepedia.com/' ) ) request( {
  2372. uri: 'https://help.gamepedia.com/Special:GlobalBlockList/' + encodeURIComponent( username ) + '?uselang=qqx',
  2373. jar: cookieJar
  2374. }, function( error, response, body ) {
  2375. if ( error || !response || response.statusCode !== 200 || !body ) {
  2376. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the global block: ' + error );
  2377. }
  2378. else {
  2379. let $ = cheerio.load(body);
  2380. var gblock = $('.mw-blocklist');
  2381. if ( gblock.length ) {
  2382. var reason = gblock.find('.TablePager_col_reason').text().replace( /\)$/, '' ).split(', ');
  2383. var timestamp = new Date(gblock.find('.TablePager_col_timestamp').text().replace( /(\d{2}:\d{2}), (\d{1,2}) \((\w+)\) (\d{4})/, '$3 $2, $4 $1 UTC' )).toLocaleString(lang.dateformat, timeoptions);
  2384. var expiry = gblock.find('.TablePager_col_expiry').text();
  2385. if ( expiry.startsWith( '(infiniteblock)' ) ) expiry = lang.user.block.until_infinity;
  2386. else expiry = new Date(expiry.replace( /(\d{2}:\d{2}), (\d{1,2}) \((\w+)\) (\d{4})/, '$3 $2, $4 $1 UTC' )).toLocaleString(lang.dateformat, timeoptions);
  2387. if ( msg.showEmbed() ) {
  2388. var gblocktitle = lang.user.gblock.header.replaceSave( '%s', username ).escapeFormatting();
  2389. var globalblock = embed.fields.find( field => field.inline === false && field.name === lang.user.block.header.replaceSave( '%s', username ).escapeFormatting() && field.value.replace( /\[([^\]]*)\]\([^\)]*\)/g, '$1' ) === lang.user.block[( reason.length > 4 ? 'text' : 'noreason' )].replaceSave( '%1$s', timestamp ).replaceSave( '%2$s', expiry ).replaceSave( '%3$s', reason[1] ).replaceSave( '%4$s', reason.slice(4).join(', ') ).escapeFormatting() );
  2390. if ( globalblock ) globalblock.name = gblocktitle;
  2391. else {
  2392. var gblocktext = lang.user.gblock[( reason.length > 4 ? 'text' : 'noreason' )].replaceSave( '%1$s', timestamp ).replaceSave( '%2$s', expiry ).replaceSave( '%3$s', '[[User:' + reason[1] + '|' + reason[1] + ']]' ).replaceSave( '%4$s', '[[Special:Contribs/' + username.toTitle() + '|' + reason[2] + ']]' ).replaceSave( '%5$s', reason.slice(4).join(', ') );
  2393. embed.addField( gblocktitle, gblocktext.toMarkdown( reason[3].replace( /Special:BlockList$/, '' ) ) );
  2394. }
  2395. }
  2396. else {
  2397. let splittext = text.split('\n\n');
  2398. var globalblock = splittext.indexOf('**' + lang.user.block.header.replaceSave( '%s', username ).escapeFormatting() + '**\n' + lang.user.block[( reason.length > 4 ? 'text' : 'noreason' )].replaceSave( '%1$s', timestamp ).replaceSave( '%2$s', expiry ).replaceSave( '%3$s', reason[1] ).replaceSave( '%4$s', reason.slice(4).join(', ') ).escapeFormatting());
  2399. if ( globalblock !== -1 ) splittext[globalblock] = '**' + lang.user.gblock.header.replaceSave( '%s', username ).escapeFormatting() + '**\n' + lang.user.block[( reason.length > 4 ? 'text' : 'noreason' )].replaceSave( '%1$s', timestamp ).replaceSave( '%2$s', expiry ).replaceSave( '%3$s', reason[1] ).replaceSave( '%4$s', reason.slice(4).join(', ') ).escapeFormatting();
  2400. else splittext.push('**' + lang.user.gblock.header.replaceSave( '%s', username ).escapeFormatting() + '**\n' + lang.user.gblock[( reason.length > 4 ? 'text' : 'noreason' )].replaceSave( '%1$s', timestamp ).replaceSave( '%2$s', expiry ).replaceSave( '%3$s', reason[1] ).replaceSave( '%4$s', reason[2] ).replaceSave( '%5$s', reason.slice(4).join(', ') ).escapeFormatting());
  2401. text = splittext.join('\n\n');
  2402. }
  2403. }
  2404. }
  2405. if ( !/^(?:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{2})?|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}(?:\/\d{2,3})?)$/.test(username) ) request( {
  2406. uri: 'https://help.gamepedia.com/UserProfile:' + encodeURIComponent( username ),
  2407. jar: cookieJar
  2408. }, function( gerror, gresponse, gbody ) {
  2409. if ( gerror || !gresponse || gresponse.statusCode !== 200 || !gbody ) {
  2410. console.log( '- ' + ( gresponse && gresponse.statusCode ) + ': Error while getting the global edit count: ' + gerror );
  2411. }
  2412. else {
  2413. let $ = cheerio.load(gbody);
  2414. var wikisedited = $('.curseprofile .rightcolumn .section.stats dd').eq(0).text().replace( /[,\.]/g, '' );
  2415. if ( wikisedited ) {
  2416. if ( msg.showEmbed() ) embed.fields.splice(1, 0, {name:lang.user.info.wikisedited,value:wikisedited,inline:true});
  2417. else {
  2418. let splittext = text.split('\n');
  2419. splittext.splice(5, 0, lang.user.info.wikisedited + ' ' + wikisedited);
  2420. text = splittext.join('\n');
  2421. }
  2422. }
  2423. var globaledits = $('.curseprofile .rightcolumn .section.stats dd').eq(2).text().replace( /[,\.]/g, '' );
  2424. if ( globaledits ) {
  2425. if ( msg.showEmbed() ) embed.fields.splice(1, 0, {name:lang.user.info.globaleditcount,value:globaledits,inline:true});
  2426. else {
  2427. let splittext = text.split('\n');
  2428. splittext.splice(5, 0, lang.user.info.globaleditcount + ' ' + globaledits);
  2429. text = splittext.join('\n');
  2430. }
  2431. }
  2432. }
  2433. msg.edit( spoiler + text + spoiler, embed ).catch(log_error);
  2434. } );
  2435. else msg.edit( spoiler + text + spoiler, embed ).catch(log_error);
  2436. } );
  2437. /*
  2438. var verified = false;
  2439. var roles = [];
  2440. var failedroles = [];
  2441. var failedverifications = [];
  2442. db.each( 'SELECT wiki, editcount, usergroup, role FROM verification WHERE guild = ? AND (channel = ? OR channel IS NULL) ORDER BY channel DESC', [msg.guild.id, msg.channel.id], (dberror, row) => {
  2443. if ( dberror ) {
  2444. console.log( '- Error while getting the verification setting: ' + dberror );
  2445. return dberror;
  2446. }
  2447. if ( !data.blocked && msg.member === data.discord && wiki === row.wiki && data.editcount >= row.editcount && data.usergroups.includes( row.usergroup ) ) {
  2448. verified = true;
  2449. var role = msg.guild.roles.get(row.role);
  2450. if ( role ) {
  2451. if ( role.comparePositionTo(msg.guild.me.highestRole) < 0 ) roles.push(role);
  2452. else if ( msg.member.roles.has(role.id) ) roles.push(role);
  2453. else failedroles.push(role);
  2454. }
  2455. else failedverifications.push(row);
  2456. }
  2457. }, (dberror) => {
  2458. if ( dberror ) {
  2459. console.log( '- Error while getting the verification settings: ' + dberror );
  2460. return dberror;
  2461. }
  2462. console.log( '- Verification settings successfully loaded.' );
  2463. if ( verified ) {
  2464. if ( msg.guild.me.permissions.has('MANAGE_ROLES') ) {
  2465. return data.discord.addRoles( roles.filter( role => !msg.member.roles.has(role.id) ), data.discord.user.tag + ' successfully verified as "' + username + '".' ).then( member => {
  2466. var desc = 'Verification successful!\n\n**Added roles:** ' + ( roles.join(', ') || '*none*' );
  2467. if ( failedroles.length ) desc += '\nDue to some missing permissions I couldn\'t add the following roles: ' + failedroles.join(', ');
  2468. embed.setDescription( desc );
  2469. msg.sendChannel( spoiler + text + spoiler, embed );
  2470. }, error => {
  2471. var desc = 'Verification successful!\n\n**Added roles:** *none*\nDue to some missing permissions I couldn\'t add the following roles:\n';
  2472. if ( roles.length ) desc += roles.join(', ');
  2473. if ( failedroles.length ) desc += ( roles.length ? ', ' : '' ) + failedroles.join(', ');
  2474. embed.setDescription( desc );
  2475. msg.sendChannel( spoiler + text + spoiler, embed );
  2476. log_error(error);
  2477. } );
  2478. }
  2479. var desc = 'Verification successful!\n\n**Added roles:** *none*\nDue to some missing permissions I couldn\'t add the following roles:\n';
  2480. if ( roles.length ) desc += roles.join(', ');
  2481. if ( failedroles.length ) desc += ( roles.length ? ', ' : '' ) + failedroles.join(', ');
  2482. embed.setDescription( desc );
  2483. msg.sendChannel( spoiler + text + spoiler, embed );
  2484. }
  2485. else {
  2486. }
  2487. } );
  2488. */
  2489. }
  2490. function fandom_discussion(lang, msg, wiki, title, query, reaction, spoiler) {
  2491. if ( !title ) {
  2492. var pagelink = wiki + 'f';
  2493. var embed = new Discord.RichEmbed().setAuthor( query.general.sitename ).setTitle( lang.discussion.main ).setURL( pagelink );
  2494. request( {
  2495. uri: wiki + 'f'
  2496. }, function( descerror, descresponse, descbody ) {
  2497. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  2498. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  2499. } else {
  2500. var thumbnail = wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png';
  2501. var parser = new htmlparser.Parser( {
  2502. onopentag: (tagname, attribs) => {
  2503. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  2504. var description = attribs.content.escapeFormatting();
  2505. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  2506. embed.setDescription( description );
  2507. }
  2508. if ( tagname === 'meta' && attribs.property === 'og:image' ) {
  2509. thumbnail = attribs.content;
  2510. }
  2511. }
  2512. }, {decodeEntities:true} );
  2513. parser.write( descbody );
  2514. parser.end();
  2515. embed.setThumbnail( thumbnail );
  2516. }
  2517. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  2518. if ( reaction ) reaction.removeEmoji();
  2519. } );
  2520. }
  2521. else if ( title.split(' ')[0].toLowerCase() === 'post' || title.split(' ')[0].toLowerCase() === lang.discussion.post ) {
  2522. title = title.split(' ').slice(1).join(' ');
  2523. request( {
  2524. uri: 'https://services.fandom.com/discussion/' + query.wikidesc.id + '/posts?limit=50&format=json'
  2525. }, function( error, response, body ) {
  2526. try {
  2527. if ( body ) body = JSON.parse(body);
  2528. if ( error || !response || response.statusCode !== 200 || !body || body.title || !body._embedded || !body._embedded['doc:posts'] ) {
  2529. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the posts: ' + ( error || body && body.title ) );
  2530. msg.sendChannelError( spoiler + '<' + wiki + 'f' + '>' + spoiler );
  2531. if ( reaction ) reaction.removeEmoji();
  2532. }
  2533. else if ( body._embedded['doc:posts'].length ) {
  2534. var posts = body._embedded['doc:posts'];
  2535. var embed = new Discord.RichEmbed().setAuthor( query.general.sitename );
  2536. if ( posts.some( post => post.id === title ) ) {
  2537. fandom_discussionsend(lang, msg, wiki, posts.find( post => post.id === title ), embed, spoiler);
  2538. if ( reaction ) reaction.removeEmoji();
  2539. }
  2540. else if ( /^\d+$/.test(title) ) {
  2541. request( {
  2542. uri: 'https://services.fandom.com/discussion/' + query.wikidesc.id + '/posts/' + title + '?format=json'
  2543. }, function( perror, presponse, pbody ) {
  2544. try {
  2545. if ( pbody ) pbody = JSON.parse(pbody);
  2546. if ( perror || !presponse || presponse.statusCode !== 200 || !pbody || pbody.id !== title ) {
  2547. if ( pbody && pbody.title === 'The requested resource was not found.' ) {
  2548. if ( posts.some( post => post.rawContent.toLowerCase().includes( title.toLowerCase() ) ) ) {
  2549. fandom_discussionsend(lang, msg, wiki, posts.find( post => post.rawContent.toLowerCase().includes( title.toLowerCase() ) ), embed, spoiler);
  2550. }
  2551. else msg.reactEmoji('🤷');
  2552. }
  2553. else {
  2554. console.log( '- ' + ( presponse && presponse.statusCode ) + ': Error while getting the post: ' + ( perror || pbody && pbody.title ) );
  2555. msg.sendChannelError( spoiler + '<' + wiki + 'f' + '>' + spoiler );
  2556. }
  2557. if ( reaction ) reaction.removeEmoji();
  2558. }
  2559. else if ( pbody.title ) {
  2560. fandom_discussionsend(lang, msg, wiki, pbody, embed, spoiler);
  2561. if ( reaction ) reaction.removeEmoji();
  2562. }
  2563. else request( {
  2564. uri: 'https://services.fandom.com/discussion/' + query.wikidesc.id + '/threads/' + pbody.threadId + '?format=json'
  2565. }, function( therror, thresponse, thbody ) {
  2566. try {
  2567. if ( thbody ) thbody = JSON.parse(thbody);
  2568. if ( therror || !thresponse || thresponse.statusCode !== 200 || !thbody || thbody.id !== pbody.threadId ) {
  2569. console.log( '- ' + ( thresponse && thresponse.statusCode ) + ': Error while getting the thread: ' + ( therror || thbody && thbody.title ) );
  2570. msg.sendChannelError( spoiler + '<' + wiki + 'f/p/' + pbody.threadId + '>' + spoiler );
  2571. }
  2572. else embed.setTitle( thbody.title.escapeFormatting() );
  2573. }
  2574. catch ( jsonerror ) {
  2575. console.log( '- ' + ( thresponse && thresponse.statusCode ) + ': Error while getting the thread: ' + ( therror || jsonerror ) );
  2576. msg.sendChannelError( spoiler + '<' + wiki + 'f/p/' + pbody.threadId + '>' + spoiler );
  2577. }
  2578. fandom_discussionsend(lang, msg, wiki, pbody, embed, spoiler);
  2579. if ( reaction ) reaction.removeEmoji();
  2580. } );
  2581. }
  2582. catch ( jsonerror ) {
  2583. console.log( '- ' + ( presponse && presponse.statusCode ) + ': Error while getting the post: ' + ( perror || jsonerror ) );
  2584. msg.sendChannelError( spoiler + '<' + wiki + 'f' + '>' + spoiler );
  2585. if ( reaction ) reaction.removeEmoji();
  2586. }
  2587. } );
  2588. }
  2589. else if ( posts.some( post => post.rawContent.toLowerCase().includes( title.toLowerCase() ) ) ) {
  2590. fandom_discussionsend(lang, msg, wiki, posts.find( post => post.rawContent.toLowerCase().includes( title.toLowerCase() ) ), embed, spoiler);
  2591. if ( reaction ) reaction.removeEmoji();
  2592. }
  2593. else {
  2594. msg.reactEmoji('🤷');
  2595. if ( reaction ) reaction.removeEmoji();
  2596. }
  2597. }
  2598. else {
  2599. msg.reactEmoji('🤷');
  2600. if ( reaction ) reaction.removeEmoji();
  2601. }
  2602. }
  2603. catch ( jsonerror ) {
  2604. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the posts: ' + ( error || jsonerror ) );
  2605. msg.sendChannelError( spoiler + '<' + wiki + 'f' + '>' + spoiler );
  2606. if ( reaction ) reaction.removeEmoji();
  2607. }
  2608. } );
  2609. }
  2610. else {
  2611. request( {
  2612. uri: 'https://services.fandom.com/discussion/' + query.wikidesc.id + '/threads?sortKey=trending&limit=50&format=json'
  2613. }, function( error, response, body ) {
  2614. try {
  2615. if ( body ) body = JSON.parse(body);
  2616. if ( error || !response || response.statusCode !== 200 || !body || body.title || !body._embedded || !body._embedded.threads ) {
  2617. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the threads: ' + ( error || body && body.title ) );
  2618. msg.sendChannelError( spoiler + '<' + wiki + 'f' + '>' + spoiler );
  2619. if ( reaction ) reaction.removeEmoji();
  2620. }
  2621. else if ( body._embedded.threads.length ) {
  2622. var threads = body._embedded.threads;
  2623. var embed = new Discord.RichEmbed().setAuthor( query.general.sitename );
  2624. if ( threads.some( thread => thread.id === title ) ) {
  2625. fandom_discussionsend(lang, msg, wiki, threads.find( thread => thread.id === title ), embed, spoiler);
  2626. if ( reaction ) reaction.removeEmoji();
  2627. }
  2628. else if ( threads.some( thread => thread.title === title ) ) {
  2629. fandom_discussionsend(lang, msg, wiki, threads.find( thread => thread.title === title ), embed, spoiler);
  2630. if ( reaction ) reaction.removeEmoji();
  2631. }
  2632. else if ( threads.some( thread => thread.title.toLowerCase() === title.toLowerCase() ) ) {
  2633. fandom_discussionsend(lang, msg, wiki, threads.find( thread => thread.title.toLowerCase() === title.toLowerCase() ), embed, spoiler);
  2634. if ( reaction ) reaction.removeEmoji();
  2635. }
  2636. else if ( threads.some( thread => thread.title.includes( title ) ) ) {
  2637. fandom_discussionsend(lang, msg, wiki, threads.find( thread => thread.title.includes( title ) ), embed, spoiler);
  2638. if ( reaction ) reaction.removeEmoji();
  2639. }
  2640. else if ( threads.some( thread => thread.title.toLowerCase().includes( title.toLowerCase() ) ) ) {
  2641. fandom_discussionsend(lang, msg, wiki, threads.find( thread => thread.title.toLowerCase().includes( title.toLowerCase() ) ), embed, spoiler);
  2642. if ( reaction ) reaction.removeEmoji();
  2643. }
  2644. else if ( /^\d+$/.test(title) ) {
  2645. request( {
  2646. uri: 'https://services.fandom.com/discussion/' + query.wikidesc.id + '/threads/' + title + '?format=json'
  2647. }, function( therror, thresponse, thbody ) {
  2648. try {
  2649. if ( thbody ) thbody = JSON.parse(thbody);
  2650. if ( therror || !thresponse || thresponse.statusCode !== 200 || !thbody || thbody.id !== title ) {
  2651. if ( thbody && thbody.status === 404 ) {
  2652. if (threads.some( thread => thread.rawContent.toLowerCase().includes( title.toLowerCase() ) ) ) {
  2653. fandom_discussionsend(lang, msg, wiki, threads.find( thread => thread.rawContent.toLowerCase().includes( title.toLowerCase() ) ), embed, spoiler);
  2654. }
  2655. else msg.reactEmoji('🤷');
  2656. }
  2657. else {
  2658. console.log( '- ' + ( thresponse && thresponse.statusCode ) + ': Error while getting the thread: ' + ( therror || thbody && thbody.title ) );
  2659. msg.sendChannelError( spoiler + '<' + wiki + 'f/p/' + title + '>' + spoiler );
  2660. }
  2661. }
  2662. else fandom_discussionsend(lang, msg, wiki, thbody, embed, spoiler);
  2663. }
  2664. catch ( jsonerror ) {
  2665. console.log( '- ' + ( thresponse && thresponse.statusCode ) + ': Error while getting the thread: ' + ( therror || jsonerror ) );
  2666. msg.sendChannelError( spoiler + '<' + wiki + 'f/p/' + title + '>' + spoiler );
  2667. }
  2668. if ( reaction ) reaction.removeEmoji();
  2669. } );
  2670. }
  2671. else if ( threads.some( thread => thread.rawContent.toLowerCase().includes( title.toLowerCase() ) ) ) {
  2672. fandom_discussionsend(lang, msg, wiki, threads.find( thread => thread.rawContent.toLowerCase().includes( title.toLowerCase() ) ), embed, spoiler);
  2673. if ( reaction ) reaction.removeEmoji();
  2674. }
  2675. else {
  2676. msg.reactEmoji('🤷');
  2677. if ( reaction ) reaction.removeEmoji();
  2678. }
  2679. }
  2680. else {
  2681. msg.reactEmoji('🤷');
  2682. if ( reaction ) reaction.removeEmoji();
  2683. }
  2684. }
  2685. catch ( jsonerror ) {
  2686. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the threads: ' + ( error || jsonerror ) );
  2687. msg.sendChannelError( spoiler + '<' + wiki + 'f' + '>' + spoiler );
  2688. if ( reaction ) reaction.removeEmoji();
  2689. }
  2690. } );
  2691. }
  2692. }
  2693. function fandom_discussionsend(lang, msg, wiki, discussion, embed, spoiler) {
  2694. if ( discussion.title ) {
  2695. embed.setTitle( discussion.title.escapeFormatting() );
  2696. var pagelink = wiki + 'f/p/' + ( discussion.threadId || discussion.id );
  2697. }
  2698. else {
  2699. if ( discussion._embedded.thread ) embed.setTitle( discussion._embedded.thread[0].title.escapeFormatting() );
  2700. var pagelink = wiki + 'f/p/' + discussion.threadId + '/r/' + discussion.id;
  2701. }
  2702. var text = '<' + pagelink + '>';
  2703. embed.setURL( pagelink ).setFooter( discussion.createdBy.name, discussion.createdBy.avatarUrl ).setTimestamp( discussion.creationDate.epochSecond * 1000 );
  2704. var description = '';
  2705. switch ( discussion.funnel ) {
  2706. case 'IMAGE':
  2707. embed.setImage( discussion._embedded.contentImages[0].url );
  2708. break;
  2709. case 'POLL':
  2710. discussion.poll.answers.forEach( answer => embed.addField( answer.text.escapeFormatting(), ( lang.discussion.votes[answer.votes] || lang.discussion.votes['*' + answer.votes % 100] || lang.discussion.votes['*' + answer.votes % 10] || lang.discussion.votes.default ).replace( '%s', answer.votes ), true ) );
  2711. break;
  2712. case 'QUIZ':
  2713. description = discussion.quiz.title.escapeFormatting();
  2714. if ( discussion._embedded.openGraph ) embed.setThumbnail( discussion._embedded.openGraph[0].imageUrl );
  2715. break;
  2716. default:
  2717. if ( discussion.jsonModel ) {
  2718. try {
  2719. description = discussion_formatting(JSON.parse(discussion.jsonModel)).replace( /(?:\*\*\*\*|(?<!\\)\_\_)/g, '' ).replace( /{@wiki}/g, wiki );
  2720. if ( discussion._embedded.contentImages.length ) {
  2721. if ( description.trim() === '{@0}' ) {
  2722. embed.setImage( discussion._embedded.contentImages[0].url );
  2723. description = '';
  2724. }
  2725. else {
  2726. description = description.replace( /\{\@(\d+)\}/g, (match, n) => {
  2727. return '[__' + lang.discussion.image.escapeFormatting() + '__](' + discussion._embedded.contentImages[n].url + ')';
  2728. } );
  2729. embed.setThumbnail( discussion._embedded.contentImages[0].url );
  2730. }
  2731. }
  2732. else embed.setThumbnail( wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png' );
  2733. }
  2734. catch ( jsonerror ) {
  2735. console.log( '- Error while getting the formatting: ' + jsonerror );
  2736. description = discussion.rawContent.escapeFormatting();
  2737. }
  2738. }
  2739. else if ( discussion.renderedContent ) {
  2740. var parser = new htmlparser.Parser( {
  2741. ontext: (htmltext) => {
  2742. description += htmltext.escapeFormatting();
  2743. },
  2744. onclosetag: (tagname) => {
  2745. if ( tagname === 'p' ) description += '\n';
  2746. }
  2747. }, {decodeEntities:true} );
  2748. parser.write( discussion.renderedContent );
  2749. parser.end();
  2750. }
  2751. else {
  2752. description = discussion.rawContent.escapeFormatting();
  2753. }
  2754. }
  2755. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  2756. embed.setDescription( description );
  2757. msg.sendChannel( spoiler + text + spoiler, embed );
  2758. }
  2759. function gamepedia_diff(lang, msg, args, wiki, reaction, spoiler, embed) {
  2760. if ( args[0] ) {
  2761. var error = false;
  2762. var title = '';
  2763. var revision = 0;
  2764. var diff = 0;
  2765. var relative = 'prev';
  2766. if ( /^\d+$/.test(args[0]) ) {
  2767. revision = parseInt(args[0], 10);
  2768. if ( args[1] ) {
  2769. if ( /^\d+$/.test(args[1]) ) {
  2770. diff = parseInt(args[1], 10);
  2771. }
  2772. else if ( args[1] === 'prev' || args[1] === 'next' || args[1] === 'cur' ) {
  2773. relative = args[1];
  2774. }
  2775. else error = true;
  2776. }
  2777. }
  2778. else if ( args[0] === 'prev' || args[0] === 'next' || args[0] === 'cur' ) {
  2779. relative = args[0];
  2780. if ( args[1] ) {
  2781. if ( /^\d+$/.test(args[1]) ) {
  2782. revision = parseInt(args[1], 10);
  2783. }
  2784. else error = true;
  2785. }
  2786. else error = true;
  2787. }
  2788. else title = args.join(' ');
  2789. if ( error ) {
  2790. msg.reactEmoji('error');
  2791. if ( reaction ) reaction.removeEmoji();
  2792. }
  2793. else if ( diff ) {
  2794. gamepedia_diffsend(lang, msg, [diff, revision], wiki, reaction, spoiler);
  2795. }
  2796. else {
  2797. request( {
  2798. uri: wiki + 'api.php?action=compare&prop=ids|diff' + ( title ? '&fromtitle=' + encodeURIComponent( title ) : '&fromrev=' + revision ) + '&torelative=' + relative + '&format=json',
  2799. json: true
  2800. }, function( error, response, body ) {
  2801. if ( body && body.warnings ) log_warn(body.warnings);
  2802. if ( error || !response || response.statusCode !== 200 || !body || !body.compare ) {
  2803. var noerror = false;
  2804. if ( body && body.error ) {
  2805. switch ( body.error.code ) {
  2806. case 'nosuchrevid':
  2807. noerror = true;
  2808. break;
  2809. case 'missingtitle':
  2810. noerror = true;
  2811. break;
  2812. case 'invalidtitle':
  2813. noerror = true;
  2814. break;
  2815. case 'missingcontent':
  2816. noerror = true;
  2817. break;
  2818. default:
  2819. noerror = false;
  2820. }
  2821. }
  2822. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  2823. console.log( '- This wiki doesn\'t exist!' );
  2824. msg.reactEmoji('nowiki');
  2825. }
  2826. else if ( noerror ) {
  2827. msg.replyMsg( lang.diff.badrev );
  2828. }
  2829. else {
  2830. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  2831. msg.sendChannelError( spoiler + '<' + wiki.toLink() + title.toTitle() + '?diff=' + relative + ( title ? '' : '&oldid=' + revision ) + '>' + spoiler );
  2832. }
  2833. if ( reaction ) reaction.removeEmoji();
  2834. }
  2835. else {
  2836. if ( body.compare.fromarchive !== undefined || body.compare.toarchive !== undefined ) {
  2837. msg.reactEmoji('error');
  2838. if ( reaction ) reaction.removeEmoji();
  2839. } else {
  2840. var argids = [];
  2841. var ids = body.compare;
  2842. if ( ids.fromrevid && !ids.torevid ) argids = [ids.fromrevid];
  2843. else if ( !ids.fromrevid && ids.torevid ) argids = [ids.torevid];
  2844. else {
  2845. argids = [ids.torevid, ids.fromrevid];
  2846. var compare = ['', ''];
  2847. if ( ids.fromtexthidden === undefined && ids.totexthidden === undefined && ids['*'] !== undefined ) {
  2848. var more = '\n__' + lang.diff.info.more + '__';
  2849. var current_tag = '';
  2850. var small_prev_ins = '';
  2851. var small_prev_del = '';
  2852. var ins_length = more.length;
  2853. var del_length = more.length;
  2854. var added = false;
  2855. var parser = new htmlparser.Parser( {
  2856. onopentag: (tagname, attribs) => {
  2857. if ( tagname === 'ins' || tagname == 'del' ) {
  2858. current_tag = tagname;
  2859. }
  2860. if ( tagname === 'td' && attribs.class === 'diff-addedline' ) {
  2861. current_tag = tagname+'a';
  2862. }
  2863. if ( tagname === 'td' && attribs.class === 'diff-deletedline' ) {
  2864. current_tag = tagname+"d";
  2865. }
  2866. if ( tagname === 'td' && attribs.class === 'diff-marker' ) {
  2867. added = true;
  2868. }
  2869. },
  2870. ontext: (htmltext) => {
  2871. if ( current_tag === 'ins' && ins_length <= 1000 ) {
  2872. ins_length += ( '**' + htmltext.escapeFormatting() + '**' ).length;
  2873. if ( ins_length <= 1000 ) small_prev_ins += '**' + htmltext.escapeFormatting() + '**';
  2874. else small_prev_ins += more;
  2875. }
  2876. if ( current_tag === 'del' && del_length <= 1000 ) {
  2877. del_length += ( '~~' + htmltext.escapeFormatting() + '~~' ).length;
  2878. if ( del_length <= 1000 ) small_prev_del += '~~' + htmltext.escapeFormatting() + '~~';
  2879. else small_prev_del += more;
  2880. }
  2881. if ( ( current_tag === 'afterins' || current_tag === 'tda') && ins_length <= 1000 ) {
  2882. ins_length += htmltext.escapeFormatting().length;
  2883. if ( ins_length <= 1000 ) small_prev_ins += htmltext.escapeFormatting();
  2884. else small_prev_ins += more;
  2885. }
  2886. if ( ( current_tag === 'afterdel' || current_tag === 'tdd') && del_length <= 1000 ) {
  2887. del_length += htmltext.escapeFormatting().length;
  2888. if ( del_length <= 1000 ) small_prev_del += htmltext.escapeFormatting();
  2889. else small_prev_del += more;
  2890. }
  2891. if ( added ) {
  2892. if ( htmltext === '+' && ins_length <= 1000 ) {
  2893. ins_length++;
  2894. if ( ins_length <= 1000 ) small_prev_ins += '\n';
  2895. else small_prev_ins += more;
  2896. }
  2897. if ( htmltext === '−' && del_length <= 1000 ) {
  2898. del_length++;
  2899. if ( del_length <= 1000 ) small_prev_del += '\n';
  2900. else small_prev_del += more;
  2901. }
  2902. added = false;
  2903. }
  2904. },
  2905. onclosetag: (tagname) => {
  2906. if ( tagname === 'ins' ) {
  2907. current_tag = 'afterins';
  2908. } else if ( tagname === 'del' ) {
  2909. current_tag = 'afterdel';
  2910. } else {
  2911. current_tag = '';
  2912. }
  2913. }
  2914. }, {decodeEntities:true} );
  2915. parser.write( ids['*'] );
  2916. parser.end();
  2917. if ( small_prev_del.length ) {
  2918. if ( small_prev_del.replace( /\~\~/g, '' ).trim().length ) {
  2919. compare[0] = small_prev_del.replace( /\~\~\~\~/g, '' );
  2920. } else compare[0] = '__' + lang.diff.info.whitespace + '__';
  2921. }
  2922. if ( small_prev_ins.length ) {
  2923. if ( small_prev_ins.replace( /\*\*/g, '' ).trim().length ) {
  2924. compare[1] = small_prev_ins.replace( /\*\*\*\*/g, '' );
  2925. } else compare[1] = '__' + lang.diff.info.whitespace + '__';
  2926. }
  2927. }
  2928. else if ( ids.fromtexthidden !== undefined ) compare[0] = '__' + lang.diff.hidden + '__';
  2929. else if ( ids.totexthidden !== undefined ) compare[1] = '__' + lang.diff.hidden + '__';
  2930. }
  2931. gamepedia_diffsend(lang, msg, argids, wiki, reaction, spoiler, compare);
  2932. }
  2933. }
  2934. } );
  2935. }
  2936. }
  2937. else {
  2938. if ( embed ) msg.sendChannel( spoiler + '<' + embed.url + '>' + spoiler, embed );
  2939. else msg.reactEmoji('error');
  2940. if ( reaction ) reaction.removeEmoji();
  2941. }
  2942. }
  2943. function gamepedia_diffsend(lang, msg, args, wiki, reaction, spoiler, compare) {
  2944. request( {
  2945. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=tags&tglimit=500&tgprop=displayname&prop=revisions&rvprop=ids|timestamp|flags|user|size|comment|tags' + ( args.length === 1 || args[0] === args[1] ? '|content' : '' ) + '&revids=' + args.join('|') + '&format=json',
  2946. json: true
  2947. }, function( error, response, body ) {
  2948. if ( body && body.warnings ) log_warn(body.warnings);
  2949. if ( error || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query ) {
  2950. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  2951. console.log( '- This wiki doesn\'t exist!' );
  2952. msg.reactEmoji('nowiki');
  2953. }
  2954. else {
  2955. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  2956. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Diff/' + ( args[1] ? args[1] + '/' : '' ) + args[0] + '>' + spoiler );
  2957. }
  2958. if ( reaction ) reaction.removeEmoji();
  2959. }
  2960. else {
  2961. if ( body.query.badrevids ) {
  2962. msg.replyMsg( lang.diff.badrev );
  2963. if ( reaction ) reaction.removeEmoji();
  2964. }
  2965. else if ( body.query.pages && !body.query.pages['-1'] ) {
  2966. var pages = Object.values(body.query.pages);
  2967. if ( pages.length !== 1 ) {
  2968. msg.sendChannel( spoiler + '<' + wiki.toLink() + 'Special:Diff/' + ( args[1] ? args[1] + '/' : '' ) + args[0] + '>' + spoiler );
  2969. if ( reaction ) reaction.removeEmoji();
  2970. }
  2971. else {
  2972. var title = pages[0].title;
  2973. var revisions = pages[0].revisions.sort( (first, second) => Date.parse(second.timestamp) - Date.parse(first.timestamp) );
  2974. var diff = revisions[0].revid;
  2975. var oldid = ( revisions[1] ? revisions[1].revid : 0 );
  2976. var editor = [lang.diff.info.editor, ( revisions[0].userhidden !== undefined ? lang.diff.hidden : revisions[0].user )];
  2977. var timestamp = [lang.diff.info.timestamp, new Date(revisions[0].timestamp).toLocaleString(lang.dateformat, timeoptions)];
  2978. var difference = revisions[0].size - ( revisions[1] ? revisions[1].size : 0 );
  2979. var size = [lang.diff.info.size, lang.diff.info.bytes.replace( '%s', ( difference > 0 ? '+' : '' ) + difference )];
  2980. var comment = [lang.diff.info.comment, ( revisions[0].commenthidden !== undefined ? lang.diff.hidden : ( revisions[0].comment ? revisions[0].comment.toFormatting(msg.showEmbed(), wiki, title) : lang.diff.nocomment ) )];
  2981. if ( revisions[0].tags.length ) var tags = [lang.diff.info.tags, body.query.tags.filter( tag => revisions[0].tags.includes( tag.name ) ).map( tag => tag.displayname ).join(', ')];
  2982. var pagelink = wiki.toLink() + title.toTitle() + '?diff=' + diff + '&oldid=' + oldid;
  2983. if ( msg.showEmbed() ) {
  2984. var text = '<' + pagelink + '>';
  2985. var editorlink = '[' + editor[1] + '](' + wiki.toLink() + 'User:' + editor[1].toTitle(true) + ')';
  2986. if ( revisions[0].anon !== undefined ) {
  2987. editorlink = '[' + editor[1] + '](' + wiki.toLink() + 'Special:Contributions/' + editor[1].toTitle(true) + ')';
  2988. }
  2989. if ( editor[1] === lang.diff.hidden ) editorlink = editor[1];
  2990. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( ( title + '?diff=' + diff + '&oldid=' + oldid ).escapeFormatting() ).setURL( pagelink ).addField( editor[0], editorlink, true ).addField( size[0], size[1], true ).addField( comment[0], comment[1] ).setFooter( timestamp[1] );
  2991. if ( tags ) {
  2992. var taglink = '';
  2993. var tagtext = '';
  2994. var tagparser = new htmlparser.Parser( {
  2995. onopentag: (tagname, attribs) => {
  2996. if ( tagname === 'a' ) taglink = attribs.href;
  2997. },
  2998. ontext: (htmltext) => {
  2999. if ( taglink ) tagtext += '[' + htmltext.escapeFormatting() + '](' + taglink + ')'
  3000. else tagtext += htmltext.escapeFormatting();
  3001. },
  3002. onclosetag: (tagname) => {
  3003. if ( tagname === 'a' ) taglink = '';
  3004. }
  3005. }, {decodeEntities:true} );
  3006. tagparser.write( tags[1] );
  3007. tagparser.end();
  3008. embed.addField( tags[0], tagtext );
  3009. }
  3010. var more = '\n__' + lang.diff.info.more + '__';
  3011. if ( !compare && oldid ) request( {
  3012. uri: wiki + 'api.php?action=compare&prop=diff&fromrev=' + oldid + '&torev=' + diff + '&format=json',
  3013. json: true
  3014. }, function( cperror, cpresponse, cpbody ) {
  3015. if ( cpbody && cpbody.warnings ) log_warn(cpbody.warnings);
  3016. if ( cperror || !cpresponse || cpresponse.statusCode !== 200 || !cpbody || !cpbody.compare || cpbody.compare['*'] === undefined ) {
  3017. var noerror = false;
  3018. if ( cpbody && cpbody.error ) {
  3019. switch ( cpbody.error.code ) {
  3020. case 'nosuchrevid':
  3021. noerror = true;
  3022. break;
  3023. case 'missingcontent':
  3024. noerror = true;
  3025. break;
  3026. default:
  3027. noerror = false;
  3028. }
  3029. }
  3030. if ( !noerror ) console.log( '- ' + ( cpresponse && cpresponse.statusCode ) + ': Error while getting the diff: ' + ( cperror || cpbody && cpbody.error && cpbody.error.info ) );
  3031. }
  3032. else if ( cpbody.compare.fromtexthidden === undefined && cpbody.compare.totexthidden === undefined && cpbody.compare.fromarchive === undefined && cpbody.compare.toarchive === undefined ) {
  3033. var current_tag = '';
  3034. var small_prev_ins = '';
  3035. var small_prev_del = '';
  3036. var ins_length = more.length;
  3037. var del_length = more.length;
  3038. var added = false;
  3039. var parser = new htmlparser.Parser( {
  3040. onopentag: (tagname, attribs) => {
  3041. if ( tagname === 'ins' || tagname == 'del' ) {
  3042. current_tag = tagname;
  3043. }
  3044. if ( tagname === 'td' && attribs.class === 'diff-addedline' ) {
  3045. current_tag = tagname+'a';
  3046. }
  3047. if ( tagname === 'td' && attribs.class === 'diff-deletedline' ) {
  3048. current_tag = tagname+"d";
  3049. }
  3050. if ( tagname === 'td' && attribs.class === 'diff-marker' ) {
  3051. added = true;
  3052. }
  3053. },
  3054. ontext: (htmltext) => {
  3055. if ( current_tag === 'ins' && ins_length <= 1000 ) {
  3056. ins_length += ( '**' + htmltext.escapeFormatting() + '**' ).length;
  3057. if ( ins_length <= 1000 ) small_prev_ins += '**' + htmltext.escapeFormatting() + '**';
  3058. else small_prev_ins += more;
  3059. }
  3060. if ( current_tag === 'del' && del_length <= 1000 ) {
  3061. del_length += ( '~~' + htmltext.escapeFormatting() + '~~' ).length;
  3062. if ( del_length <= 1000 ) small_prev_del += '~~' + htmltext.escapeFormatting() + '~~';
  3063. else small_prev_del += more;
  3064. }
  3065. if ( ( current_tag === 'afterins' || current_tag === 'tda') && ins_length <= 1000 ) {
  3066. ins_length += htmltext.escapeFormatting().length;
  3067. if ( ins_length <= 1000 ) small_prev_ins += htmltext.escapeFormatting();
  3068. else small_prev_ins += more;
  3069. }
  3070. if ( ( current_tag === 'afterdel' || current_tag === 'tdd') && del_length <= 1000 ) {
  3071. del_length += htmltext.escapeFormatting().length;
  3072. if ( del_length <= 1000 ) small_prev_del += htmltext.escapeFormatting();
  3073. else small_prev_del += more;
  3074. }
  3075. if ( added ) {
  3076. if ( htmltext === '+' && ins_length <= 1000 ) {
  3077. ins_length++;
  3078. if ( ins_length <= 1000 ) small_prev_ins += '\n';
  3079. else small_prev_ins += more;
  3080. }
  3081. if ( htmltext === '−' && del_length <= 1000 ) {
  3082. del_length++;
  3083. if ( del_length <= 1000 ) small_prev_del += '\n';
  3084. else small_prev_del += more;
  3085. }
  3086. added = false;
  3087. }
  3088. },
  3089. onclosetag: (tagname) => {
  3090. if ( tagname === 'ins' ) {
  3091. current_tag = 'afterins';
  3092. } else if ( tagname === 'del' ) {
  3093. current_tag = 'afterdel';
  3094. } else {
  3095. current_tag = '';
  3096. }
  3097. }
  3098. }, {decodeEntities:true} );
  3099. parser.write( cpbody.compare['*'] );
  3100. parser.end();
  3101. if ( small_prev_del.length ) {
  3102. if ( small_prev_del.replace( /\~\~/g, '' ).trim().length ) {
  3103. embed.addField( lang.diff.info.removed, small_prev_del.replace( /\~\~\~\~/g, '' ), true );
  3104. } else embed.addField( lang.diff.info.removed, '__' + lang.diff.info.whitespace + '__', true );
  3105. }
  3106. if ( small_prev_ins.length ) {
  3107. if ( small_prev_ins.replace( /\*\*/g, '' ).trim().length ) {
  3108. embed.addField( lang.diff.info.added, small_prev_ins.replace( /\*\*\*\*/g, '' ), true );
  3109. } else embed.addField( lang.diff.info.added, '__' + lang.diff.info.whitespace + '__', true );
  3110. }
  3111. }
  3112. else if ( cpbody.compare.fromtexthidden !== undefined ) {
  3113. embed.addField( lang.diff.info.removed, '__' + lang.diff.hidden + '__', true );
  3114. }
  3115. else if ( cpbody.compare.totexthidden !== undefined ) {
  3116. embed.addField( lang.diff.info.added, '__' + lang.diff.hidden + '__', true );
  3117. }
  3118. msg.sendChannel( spoiler + text + spoiler, embed );
  3119. if ( reaction ) reaction.removeEmoji();
  3120. } );
  3121. else {
  3122. if ( compare ) {
  3123. if ( compare[0].length ) embed.addField( lang.diff.info.removed, compare[0], true );
  3124. if ( compare[1].length ) embed.addField( lang.diff.info.added, compare[1], true );
  3125. }
  3126. else if ( revisions[0]['*'] ) {
  3127. var content = revisions[0]['*'].escapeFormatting();
  3128. if ( content.trim().length ) {
  3129. if ( content.length <= 1000 ) content = '**' + content + '**';
  3130. else {
  3131. content = content.substring(0, 1000 - more.length);
  3132. content = '**' + content.substring(0, content.lastIndexOf('\n')) + '**' + more;
  3133. }
  3134. embed.addField( lang.diff.info.added, content, true );
  3135. } else embed.addField( lang.diff.info.added, '__' + lang.diff.info.whitespace + '__', true );
  3136. }
  3137. msg.sendChannel( spoiler + text + spoiler, embed );
  3138. if ( reaction ) reaction.removeEmoji();
  3139. }
  3140. }
  3141. else {
  3142. var embed = {};
  3143. var text = '<' + pagelink + '>\n\n' + editor.join(' ') + '\n' + timestamp.join(' ') + '\n' + size.join(' ') + '\n' + comment.join(' ');
  3144. if ( tags ) text += htmlToPlain( '\n' + tags.join(' ') );
  3145. msg.sendChannel( spoiler + text + spoiler, embed );
  3146. if ( reaction ) reaction.removeEmoji();
  3147. }
  3148. }
  3149. }
  3150. else {
  3151. msg.reactEmoji('error');
  3152. if ( reaction ) reaction.removeEmoji();
  3153. }
  3154. }
  3155. } );
  3156. }
  3157. function fandom_diff(lang, msg, args, wiki, reaction, spoiler, embed) {
  3158. if ( args[0] ) {
  3159. var error = false;
  3160. var title = '';
  3161. var revision = 0;
  3162. var diff = 'prev';
  3163. if ( /^\d+$/.test(args[0]) ) {
  3164. revision = args[0];
  3165. if ( args[1] ) {
  3166. if ( /^\d+$/.test(args[1]) ) {
  3167. diff = args[1];
  3168. }
  3169. else if ( args[1] === 'prev' || args[1] === 'next' ) {
  3170. diff = args[1];
  3171. }
  3172. else error = true;
  3173. }
  3174. }
  3175. else if ( args[0] === 'prev' || args[0] === 'next' ) {
  3176. diff = args[0];
  3177. if ( args[1] ) {
  3178. if ( /^\d+$/.test(args[1]) ) {
  3179. revision = args[1];
  3180. }
  3181. else error = true;
  3182. }
  3183. else error = true;
  3184. }
  3185. else title = args.join(' ');
  3186. if ( error ) msg.reactEmoji('error');
  3187. else if ( /^\d+$/.test(diff) ) {
  3188. var argids = [];
  3189. if ( parseInt(revision, 10) > parseInt(diff, 10) ) argids = [revision, diff];
  3190. else if ( parseInt(revision, 10) === parseInt(diff, 10) ) argids = [revision];
  3191. else argids = [diff, revision];
  3192. fandom_diffsend(lang, msg, argids, wiki, reaction, spoiler);
  3193. }
  3194. else {
  3195. request( {
  3196. uri: wiki + 'api.php?action=query&prop=revisions&rvprop=' + ( title ? '&titles=' + encodeURIComponent( title ) : '&revids=' + revision ) + '&rvdiffto=' + diff + '&format=json',
  3197. json: true
  3198. }, function( error, response, body ) {
  3199. if ( body && body.warnings ) log_warn(body.warnings);
  3200. if ( error || !response || response.statusCode !== 200 || !body || !body.query ) {
  3201. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  3202. console.log( '- This wiki doesn\'t exist!' );
  3203. msg.reactEmoji('nowiki');
  3204. }
  3205. else {
  3206. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  3207. msg.sendChannelError( spoiler + '<' + wiki.toLink() + title.toTitle() + '?diff=' + diff + ( title ? '' : '&oldid=' + revision ) + '>' + spoiler );
  3208. }
  3209. if ( reaction ) reaction.removeEmoji();
  3210. }
  3211. else {
  3212. if ( body.query.badrevids ) {
  3213. msg.replyMsg( lang.diff.badrev );
  3214. if ( reaction ) reaction.removeEmoji();
  3215. } else if ( body.query.pages && !body.query.pages[-1] ) {
  3216. var revisions = Object.values(body.query.pages)[0].revisions[0];
  3217. if ( revisions.texthidden === undefined ) {
  3218. var argids = [];
  3219. var ids = revisions.diff;
  3220. if ( !ids.from ) argids = [ids.to];
  3221. else {
  3222. argids = [ids.to, ids.from];
  3223. var compare = ['', ''];
  3224. if ( ids['*'] !== undefined ) {
  3225. var more = '\n__' + lang.diff.info.more + '__';
  3226. var current_tag = '';
  3227. var small_prev_ins = '';
  3228. var small_prev_del = '';
  3229. var ins_length = more.length;
  3230. var del_length = more.length;
  3231. var added = false;
  3232. var parser = new htmlparser.Parser( {
  3233. onopentag: (tagname, attribs) => {
  3234. if ( tagname === 'ins' || tagname == 'del' ) {
  3235. current_tag = tagname;
  3236. }
  3237. if ( tagname === 'td' && attribs.class === 'diff-addedline' ) {
  3238. current_tag = tagname+'a';
  3239. }
  3240. if ( tagname === 'td' && attribs.class === 'diff-deletedline' ) {
  3241. current_tag = tagname+"d";
  3242. }
  3243. if ( tagname === 'td' && attribs.class === 'diff-marker' ) {
  3244. added = true;
  3245. }
  3246. },
  3247. ontext: (htmltext) => {
  3248. if ( current_tag === 'ins' && ins_length <= 1000 ) {
  3249. ins_length += ( '**' + htmltext.escapeFormatting() + '**' ).length;
  3250. if ( ins_length <= 1000 ) small_prev_ins += '**' + htmltext.escapeFormatting() + '**';
  3251. else small_prev_ins += more;
  3252. }
  3253. if ( current_tag === 'del' && del_length <= 1000 ) {
  3254. del_length += ( '~~' + htmltext.escapeFormatting() + '~~' ).length;
  3255. if ( del_length <= 1000 ) small_prev_del += '~~' + htmltext.escapeFormatting() + '~~';
  3256. else small_prev_del += more;
  3257. }
  3258. if ( ( current_tag === 'afterins' || current_tag === 'tda') && ins_length <= 1000 ) {
  3259. ins_length += htmltext.escapeFormatting().length;
  3260. if ( ins_length <= 1000 ) small_prev_ins += htmltext.escapeFormatting();
  3261. else small_prev_ins += more;
  3262. }
  3263. if ( ( current_tag === 'afterdel' || current_tag === 'tdd') && del_length <= 1000 ) {
  3264. del_length += htmltext.escapeFormatting().length;
  3265. if ( del_length <= 1000 ) small_prev_del += htmltext.escapeFormatting();
  3266. else small_prev_del += more;
  3267. }
  3268. if ( added ) {
  3269. if ( htmltext === '+' && ins_length <= 1000 ) {
  3270. ins_length++;
  3271. if ( ins_length <= 1000 ) small_prev_ins += '\n';
  3272. else small_prev_ins += more;
  3273. }
  3274. if ( htmltext === '−' && del_length <= 1000 ) {
  3275. del_length++;
  3276. if ( del_length <= 1000 ) small_prev_del += '\n';
  3277. else small_prev_del += more;
  3278. }
  3279. added = false;
  3280. }
  3281. },
  3282. onclosetag: (tagname) => {
  3283. if ( tagname === 'ins' ) {
  3284. current_tag = 'afterins';
  3285. } else if ( tagname === 'del' ) {
  3286. current_tag = 'afterdel';
  3287. } else {
  3288. current_tag = '';
  3289. }
  3290. }
  3291. }, {decodeEntities:true} );
  3292. parser.write( ids['*'] );
  3293. parser.end();
  3294. if ( small_prev_del.length ) {
  3295. if ( small_prev_del.replace( /\~\~/g, '' ).trim().length ) {
  3296. compare[0] = small_prev_del.replace( /\~\~\~\~/g, '' );
  3297. } else compare[0] = '__' + lang.diff.info.whitespace + '__';
  3298. }
  3299. if ( small_prev_ins.length ) {
  3300. if ( small_prev_ins.replace( /\*\*/g, '' ).trim().length ) {
  3301. compare[1] = small_prev_ins.replace( /\*\*\*\*/g, '' );
  3302. } else compare[1] = '__' + lang.diff.info.whitespace + '__';
  3303. }
  3304. }
  3305. }
  3306. fandom_diffsend(lang, msg, argids, wiki, reaction, spoiler, compare);
  3307. } else {
  3308. msg.replyMsg( lang.diff.badrev );
  3309. if ( reaction ) reaction.removeEmoji();
  3310. }
  3311. } else {
  3312. if ( body.query.pages && body.query.pages[-1] ) msg.replyMsg( lang.diff.badrev );
  3313. else msg.reactEmoji('error');
  3314. if ( reaction ) reaction.removeEmoji();
  3315. }
  3316. }
  3317. } );
  3318. }
  3319. }
  3320. else {
  3321. if ( embed ) msg.sendChannel( spoiler + '<' + embed.url + '>' + spoiler, embed );
  3322. else msg.reactEmoji('error');
  3323. if ( reaction ) reaction.removeEmoji();
  3324. }
  3325. }
  3326. function fandom_diffsend(lang, msg, args, wiki, reaction, spoiler, compare) {
  3327. request( {
  3328. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=tags&tglimit=500&tgprop=displayname&prop=revisions&rvprop=ids|timestamp|flags|user|size|comment|tags' + ( args.length === 1 || args[0] === args[1] ? '|content' : '' ) + '&revids=' + args.join('|') + '&format=json',
  3329. json: true
  3330. }, function( error, response, body ) {
  3331. if ( body && body.warnings ) log_warn(body.warnings);
  3332. if ( error || !response || response.statusCode !== 200 || !body || !body.query ) {
  3333. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  3334. console.log( '- This wiki doesn\'t exist!' );
  3335. msg.reactEmoji('nowiki');
  3336. }
  3337. else {
  3338. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  3339. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Diff/' + ( args[1] ? args[1] + '/' : '' ) + args[0] + '>' + spoiler );
  3340. }
  3341. if ( reaction ) reaction.removeEmoji();
  3342. }
  3343. else {
  3344. if ( body.query.badrevids ) {
  3345. msg.replyMsg( lang.diff.badrev );
  3346. if ( reaction ) reaction.removeEmoji();
  3347. }
  3348. else if ( body.query.pages && !body.query.pages['-1'] ) {
  3349. var pages = Object.values(body.query.pages);
  3350. if ( pages.length !== 1 ) {
  3351. msg.sendChannel( spoiler + '<' + wiki.toLink() + 'Special:Diff/' + ( args[1] ? args[1] + '/' : '' ) + args[0] + '>' + spoiler );
  3352. if ( reaction ) reaction.removeEmoji();
  3353. }
  3354. else {
  3355. var title = pages[0].title;
  3356. var revisions = pages[0].revisions.sort( (first, second) => Date.parse(second.timestamp) - Date.parse(first.timestamp) );
  3357. var diff = revisions[0].revid;
  3358. var oldid = ( revisions[1] ? revisions[1].revid : 0 );
  3359. var editor = [lang.diff.info.editor, ( revisions[0].userhidden !== undefined ? lang.diff.hidden : revisions[0].user )];
  3360. var timestamp = [lang.diff.info.timestamp, new Date(revisions[0].timestamp).toLocaleString(lang.dateformat, timeoptions)];
  3361. var difference = revisions[0].size - ( revisions[1] ? revisions[1].size : 0 );
  3362. var size = [lang.diff.info.size, lang.diff.info.bytes.replace( '%s', ( difference > 0 ? '+' : '' ) + difference )];
  3363. var comment = [lang.diff.info.comment, ( revisions[0].commenthidden !== undefined ? lang.diff.hidden : ( revisions[0].comment ? revisions[0].comment.toFormatting(msg.showEmbed(), wiki, title) : lang.diff.nocomment ) )];
  3364. if ( revisions[0].tags.length ) var tags = [lang.diff.info.tags, body.query.tags.filter( tag => revisions[0].tags.includes( tag.name ) ).map( tag => tag.displayname ).join(', ')];
  3365. var pagelink = wiki.toLink() + title.toTitle() + '?diff=' + diff + '&oldid=' + oldid;
  3366. if ( msg.showEmbed() ) {
  3367. var text = '<' + pagelink + '>';
  3368. var editorlink = '[' + editor[1] + '](' + wiki.toLink() + 'User:' + editor[1].toTitle(true) + ')';
  3369. if ( revisions[0].anon !== undefined ) {
  3370. editorlink = '[' + editor[1] + '](' + wiki.toLink() + 'Special:Contributions/' + editor[1].toTitle(true) + ')';
  3371. }
  3372. if ( editor[1] === lang.diff.hidden ) editorlink = editor[1];
  3373. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( ( title + '?diff=' + diff + '&oldid=' + oldid ).escapeFormatting() ).setURL( pagelink ).addField( editor[0], editorlink, true ).addField( size[0], size[1], true ).addField( comment[0], comment[1] ).setFooter( timestamp[1] );
  3374. if ( tags ) {
  3375. var taglink = '';
  3376. var tagtext = '';
  3377. var tagparser = new htmlparser.Parser( {
  3378. onopentag: (tagname, attribs) => {
  3379. if ( tagname === 'a' ) taglink = attribs.href;
  3380. },
  3381. ontext: (htmltext) => {
  3382. if ( taglink ) tagtext += '[' + htmltext.escapeFormatting() + '](' + taglink + ')'
  3383. else tagtext += htmltext.escapeFormatting();
  3384. },
  3385. onclosetag: (tagname) => {
  3386. if ( tagname === 'a' ) taglink = '';
  3387. }
  3388. }, {decodeEntities:true} );
  3389. tagparser.write( tags[1] );
  3390. tagparser.end();
  3391. embed.addField( tags[0], tagtext );
  3392. }
  3393. var more = '\n__' + lang.diff.info.more + '__';
  3394. if ( !compare && oldid ) request( {
  3395. uri: wiki + 'api.php?action=query&prop=revisions&rvprop=&revids=' + oldid + '&rvdiffto=' + diff + '&format=json',
  3396. json: true
  3397. }, function( cperror, cpresponse, cpbody ) {
  3398. if ( cpbody && cpbody.warnings ) log_warn(cpbody.warnings);
  3399. if ( cperror || !cpresponse || cpresponse.statusCode !== 200 || !cpbody || !cpbody.query || cpbody.query.badrevids || !cpbody.query.pages && cpbody.query.pages[-1] ) {
  3400. console.log( '- ' + ( cpresponse && cpresponse.statusCode ) + ': Error while getting the diff: ' + ( cperror || cpbody && cpbody.error && cpbody.error.info ) );
  3401. }
  3402. else {
  3403. var revision = Object.values(cpbody.query.pages)[0].revisions[0];
  3404. if ( revision.texthidden === undefined && revision.diff && revision.diff['*'] !== undefined ) {
  3405. var current_tag = '';
  3406. var small_prev_ins = '';
  3407. var small_prev_del = '';
  3408. var ins_length = more.length;
  3409. var del_length = more.length;
  3410. var added = false;
  3411. var parser = new htmlparser.Parser( {
  3412. onopentag: (tagname, attribs) => {
  3413. if ( tagname === 'ins' || tagname == 'del' ) {
  3414. current_tag = tagname;
  3415. }
  3416. if ( tagname === 'td' && attribs.class === 'diff-addedline' ) {
  3417. current_tag = tagname+'a';
  3418. }
  3419. if ( tagname === 'td' && attribs.class === 'diff-deletedline' ) {
  3420. current_tag = tagname+"d";
  3421. }
  3422. if ( tagname === 'td' && attribs.class === 'diff-marker' ) {
  3423. added = true;
  3424. }
  3425. },
  3426. ontext: (htmltext) => {
  3427. if ( current_tag === 'ins' && ins_length <= 1000 ) {
  3428. ins_length += ( '**' + htmltext.escapeFormatting() + '**' ).length;
  3429. if ( ins_length <= 1000 ) small_prev_ins += '**' + htmltext.escapeFormatting() + '**';
  3430. else small_prev_ins += more;
  3431. }
  3432. if ( current_tag === 'del' && del_length <= 1000 ) {
  3433. del_length += ( '~~' + htmltext.escapeFormatting() + '~~' ).length;
  3434. if ( del_length <= 1000 ) small_prev_del += '~~' + htmltext.escapeFormatting() + '~~';
  3435. else small_prev_del += more;
  3436. }
  3437. if ( ( current_tag === 'afterins' || current_tag === 'tda') && ins_length <= 1000 ) {
  3438. ins_length += htmltext.escapeFormatting().length;
  3439. if ( ins_length <= 1000 ) small_prev_ins += htmltext.escapeFormatting();
  3440. else small_prev_ins += more;
  3441. }
  3442. if ( ( current_tag === 'afterdel' || current_tag === 'tdd') && del_length <= 1000 ) {
  3443. del_length += htmltext.escapeFormatting().length;
  3444. if ( del_length <= 1000 ) small_prev_del += htmltext.escapeFormatting();
  3445. else small_prev_del += more;
  3446. }
  3447. if ( added ) {
  3448. if ( htmltext === '+' && ins_length <= 1000 ) {
  3449. ins_length++;
  3450. if ( ins_length <= 1000 ) small_prev_ins += '\n';
  3451. else small_prev_ins += more;
  3452. }
  3453. if ( htmltext === '−' && del_length <= 1000 ) {
  3454. del_length++;
  3455. if ( del_length <= 1000 ) small_prev_del += '\n';
  3456. else small_prev_del += more;
  3457. }
  3458. added = false;
  3459. }
  3460. },
  3461. onclosetag: (tagname) => {
  3462. if ( tagname === 'ins' ) {
  3463. current_tag = 'afterins';
  3464. } else if ( tagname === 'del' ) {
  3465. current_tag = 'afterdel';
  3466. } else {
  3467. current_tag = '';
  3468. }
  3469. }
  3470. }, {decodeEntities:true} );
  3471. parser.write( revision.diff['*'] );
  3472. parser.end();
  3473. if ( small_prev_del.length ) {
  3474. if ( small_prev_del.replace( /\~\~/g, '' ).trim().length ) {
  3475. embed.addField( lang.diff.info.removed, small_prev_del.replace( /\~\~\~\~/g, '' ), true );
  3476. } else embed.addField( lang.diff.info.removed, '__' + lang.diff.info.whitespace + '__', true );
  3477. }
  3478. if ( small_prev_ins.length ) {
  3479. if ( small_prev_ins.replace( /\*\*/g, '' ).trim().length ) {
  3480. embed.addField( lang.diff.info.added, small_prev_ins.replace( /\*\*\*\*/g, '' ), true );
  3481. } else embed.addField( lang.diff.info.added, '__' + lang.diff.info.whitespace + '__', true );
  3482. }
  3483. }
  3484. else if ( revision.texthidden !== undefined ) {
  3485. embed.addField( lang.diff.info.added, '__' + lang.diff.hidden + '__', true );
  3486. }
  3487. else if ( revision.diff && revision.diff['*'] === undefined ) {
  3488. embed.addField( lang.diff.info.removed, '__' + lang.diff.hidden + '__', true );
  3489. }
  3490. }
  3491. msg.sendChannel( spoiler + text + spoiler, embed );
  3492. if ( reaction ) reaction.removeEmoji();
  3493. } );
  3494. else {
  3495. if ( compare ) {
  3496. if ( compare[0].length ) embed.addField( lang.diff.info.removed, compare[0], true );
  3497. if ( compare[1].length ) embed.addField( lang.diff.info.added, compare[1], true );
  3498. }
  3499. else if ( revisions[0]['*'] ) {
  3500. var content = revisions[0]['*'].escapeFormatting();
  3501. if ( content.trim().length ) {
  3502. if ( content.length <= 1000 ) content = '**' + content + '**';
  3503. else {
  3504. content = content.substring(0, 1000 - more.length);
  3505. content = '**' + content.substring(0, content.lastIndexOf('\n')) + '**' + more;
  3506. }
  3507. embed.addField( lang.diff.info.added, content, true );
  3508. } else embed.addField( lang.diff.info.added, '__' + lang.diff.info.whitespace + '__', true );
  3509. }
  3510. msg.sendChannel( spoiler + text + spoiler, embed );
  3511. if ( reaction ) reaction.removeEmoji();
  3512. }
  3513. }
  3514. else {
  3515. var embed = {};
  3516. var text = '<' + pagelink + '>\n\n' + editor.join(' ') + '\n' + timestamp.join(' ') + '\n' + size.join(' ') + '\n' + comment.join(' ');
  3517. if ( tags ) text += htmlToPlain( '\n' + tags.join(' ') );
  3518. msg.sendChannel( spoiler + text + spoiler, embed );
  3519. if ( reaction ) reaction.removeEmoji();
  3520. }
  3521. }
  3522. }
  3523. else {
  3524. msg.reactEmoji('error');
  3525. if ( reaction ) reaction.removeEmoji();
  3526. }
  3527. }
  3528. if ( reaction ) reaction.removeEmoji();
  3529. } );
  3530. }
  3531. function gamepedia_random(lang, msg, wiki, reaction, spoiler) {
  3532. request( {
  3533. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general&prop=pageimages|pageprops|extracts&piprop=original|name&ppprop=description|displaytitle&exsentences=10&exintro=true&explaintext=true&generator=random&grnnamespace=0&format=json',
  3534. json: true
  3535. }, function( error, response, body ) {
  3536. if ( body && body.warnings ) log_warn(body.warnings);
  3537. if ( error || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.pages ) {
  3538. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  3539. console.log( '- This wiki doesn\'t exist!' );
  3540. msg.reactEmoji('nowiki');
  3541. }
  3542. else {
  3543. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  3544. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Random>' + spoiler );
  3545. }
  3546. }
  3547. else {
  3548. var querypage = Object.values(body.query.pages)[0];
  3549. var pagelink = wiki.toLink() + querypage.title.toTitle();
  3550. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  3551. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  3552. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  3553. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  3554. embed.setTitle( displaytitle );
  3555. }
  3556. if ( querypage.pageprops && querypage.pageprops.description ) {
  3557. var description = htmlToPlain( querypage.pageprops.description );
  3558. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  3559. embed.setDescription( description );
  3560. }
  3561. else if ( querypage.extract ) {
  3562. var extract = querypage.extract.escapeFormatting();
  3563. if ( extract.length > 2000 ) extract = extract.substring(0, 2000) + '\u2026';
  3564. embed.setDescription( extract );
  3565. }
  3566. if ( querypage.pageimage && querypage.original && querypage.title !== body.query.general.mainpage ) {
  3567. embed.setThumbnail( querypage.original.source );
  3568. }
  3569. else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  3570. msg.sendChannel( '🎲 ' + spoiler + '<' + pagelink + '>' + spoiler, embed );
  3571. }
  3572. if ( reaction ) reaction.removeEmoji();
  3573. } );
  3574. }
  3575. function fandom_random(lang, msg, wiki, reaction, spoiler) {
  3576. request( {
  3577. uri: wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=description&siprop=general&generator=random&grnnamespace=0&format=json',
  3578. json: true
  3579. }, function( error, response, body ) {
  3580. if ( body && body.warnings ) log_warn(body.warnings);
  3581. if ( error || !response || response.statusCode !== 200 || !body || !body.query || !body.query.pages ) {
  3582. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  3583. console.log( '- This wiki doesn\'t exist!' );
  3584. msg.reactEmoji('nowiki');
  3585. }
  3586. else {
  3587. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  3588. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Random>' + spoiler );
  3589. }
  3590. if ( reaction ) reaction.removeEmoji();
  3591. }
  3592. else {
  3593. var querypage = Object.values(body.query.pages)[0];
  3594. var pagelink = wiki.toLink() + querypage.title.toTitle();
  3595. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  3596. if ( querypage.title === body.query.general.mainpage && body.query.allmessages[0]['*'] ) {
  3597. embed.setDescription( body.query.allmessages[0]['*'] );
  3598. embed.setThumbnail( wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png' );
  3599. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, embed );
  3600. if ( reaction ) reaction.removeEmoji();
  3601. }
  3602. else request( {
  3603. uri: wiki.toLink() + encodeURIComponent( querypage.title.replace( / /g, '_' ) )
  3604. }, function( descerror, descresponse, descbody ) {
  3605. if ( descerror || !descresponse || descresponse.statusCode !== 200 || !descbody ) {
  3606. console.log( '- ' + ( descresponse && descresponse.statusCode ) + ': Error while getting the description: ' + descerror );
  3607. } else {
  3608. var thumbnail = wiki.toLink() + 'Special:FilePath/Wiki-wordmark.png';
  3609. var parser = new htmlparser.Parser( {
  3610. onopentag: (tagname, attribs) => {
  3611. if ( tagname === 'meta' && attribs.property === 'og:description' ) {
  3612. var description = attribs.content.escapeFormatting();
  3613. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  3614. embed.setDescription( description );
  3615. }
  3616. if ( tagname === 'meta' && attribs.property === 'og:image' && querypage.title !== body.query.general.mainpage ) {
  3617. thumbnail = attribs.content;
  3618. }
  3619. }
  3620. }, {decodeEntities:true} );
  3621. parser.write( descbody );
  3622. parser.end();
  3623. embed.setThumbnail( thumbnail );
  3624. }
  3625. msg.sendChannel( '🎲 ' + spoiler + '<' + pagelink + '>' + spoiler, embed );
  3626. if ( reaction ) reaction.removeEmoji();
  3627. } );
  3628. }
  3629. } );
  3630. }
  3631. function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
  3632. request( {
  3633. uri: wiki + 'api.php?action=query&meta=siteinfo&siprop=general|statistics&titles=Special:Statistics&format=json',
  3634. json: true
  3635. }, function( error, response, body ) {
  3636. if ( body && body.warnings ) log_warn(body.warnings);
  3637. if ( error || !response || response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.pages ) {
  3638. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  3639. console.log( '- This wiki doesn\'t exist!' );
  3640. msg.reactEmoji('nowiki');
  3641. }
  3642. else {
  3643. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  3644. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Statistics>' + spoiler );
  3645. }
  3646. }
  3647. else {
  3648. var site = false;
  3649. if ( allSites.some( site => site.wiki_domain === body.query.general.servername ) ) {
  3650. site = allSites.find( site => site.wiki_domain === body.query.general.servername );
  3651. var name = [lang.overview.name, site.wiki_display_name];
  3652. var created = [lang.overview.created, new Date(parseInt(site.created + '000', 10)).toLocaleString(lang.dateformat, timeoptions)];
  3653. var manager = [lang.overview.manager, site.wiki_managers];
  3654. var official = [lang.overview.official, ( site.official_wiki ? lang.overview.yes : lang.overview.no )];
  3655. var description = [lang.overview.description, site.wiki_description];
  3656. var image = [lang.overview.image, site.wiki_image];
  3657. if ( description[1] ) {
  3658. description[1] = description[1].escapeFormatting();
  3659. if ( description[1].length > 1000 ) description[1] = description[1].substring(0, 1000) + '\u2026';
  3660. }
  3661. if ( image[1] && image[1].startsWith( '/' ) ) image[1] = wiki.substring(0, wiki.length - 1) + image[1];
  3662. }
  3663. var articles = [lang.overview.articles, body.query.statistics.articles];
  3664. var pages = [lang.overview.pages, body.query.statistics.pages];
  3665. var edits = [lang.overview.edits, body.query.statistics.edits];
  3666. var users = [lang.overview.users, body.query.statistics.activeusers];
  3667. var title = body.query.pages['-1'].title;
  3668. var pagelink = wiki.toLink() + title.toTitle();
  3669. if ( msg.showEmbed() ) {
  3670. var text = '<' + pagelink + '>';
  3671. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( title.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
  3672. if ( site ) {
  3673. var managerlist = manager[1].map( manager => '[' + manager + '](' + wiki.toLink() + 'User:' + manager.toTitle(true) + ') ([' + lang.overview.talk + '](' + wiki.toLink() + 'User_talk:' + manager.toTitle(true) + '))' ).join('\n');
  3674. embed.addField( name[0], name[1], true ).addField( created[0], created[1], true ).addField( manager[0], ( managerlist || lang.overview.none ), true ).addField( official[0], official[1], true );
  3675. }
  3676. embed.addField( articles[0], articles[1], true ).addField( pages[0], pages[1], true ).addField( edits[0], edits[1], true ).addField( users[0], users[1], true ).setTimestamp( client.readyTimestamp ).setFooter( lang.overview.inaccurate );
  3677. if ( site ) {
  3678. if ( description[1] ) embed.addField( description[0], description[1] )
  3679. if ( image[1] ) embed.addField( image[0], image[1] ).setImage( image[1] );
  3680. }
  3681. }
  3682. else {
  3683. var embed = {};
  3684. var text = '<' + pagelink + '>\n\n';
  3685. if ( site ) text += name.join(' ') + '\n' + created.join(' ') + '\n' + manager[0] + ' ' + ( manager[1].join(', ') || lang.overview.none ) + '\n' + official.join(' ') + '\n';
  3686. text += articles.join(' ') + '\n' + pages.join(' ') + '\n' + edits.join(' ') + '\n' + users.join(' ');
  3687. if ( site ) {
  3688. if ( description[1] ) text += '\n' + description.join(' ');
  3689. if ( image[1] ) {
  3690. text += '\n' + image.join(' ');
  3691. if ( msg.uploadFiles() ) embed.files = [{attachment:image[1],name:( spoiler ? 'SPOILER ' : '' ) + name[1] + image[1].substring(image[1].lastIndexOf('.'))}];
  3692. }
  3693. }
  3694. text += '\n\n*' + lang.overview.inaccurate + '*';
  3695. }
  3696. msg.sendChannel( spoiler + text + spoiler, embed );
  3697. }
  3698. if ( reaction ) reaction.removeEmoji();
  3699. } );
  3700. }
  3701. function fandom_overview(lang, msg, wiki, reaction, spoiler) {
  3702. request( {
  3703. uri: wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-Wiki_Manager&amenableparser=true&siprop=general|statistics|wikidesc&titles=Special:Statistics&format=json',
  3704. json: true
  3705. }, function( error, response, body ) {
  3706. if ( body && body.warnings ) log_warn(body.warnings);
  3707. if ( error || !response || response.statusCode !== 200 || !body || !body.query || !body.query.pages ) {
  3708. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  3709. console.log( '- This wiki doesn\'t exist!' );
  3710. msg.reactEmoji('nowiki');
  3711. }
  3712. else {
  3713. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the search results: ' + ( error || body && body.error && body.error.info ) );
  3714. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Statistics>' + spoiler );
  3715. }
  3716. if ( reaction ) reaction.removeEmoji();
  3717. }
  3718. else request( {
  3719. uri: 'https://community.fandom.com/api/v1/Wikis/Details?ids=' + body.query.wikidesc.id + '&format=json',
  3720. json: true
  3721. }, function( overror, ovresponse, ovbody ) {
  3722. if ( overror || !ovresponse || ovresponse.statusCode !== 200 || !ovbody || ovbody.exception || !ovbody.items || !ovbody.items[body.query.wikidesc.id] ) {
  3723. console.log( '- ' + ( ovresponse && ovresponse.statusCode ) + ': Error while getting the wiki details: ' + ( overror || ovbody && ovbody.exception && ovbody.exception.details ) );
  3724. msg.sendChannelError( spoiler + '<' + wiki.toLink() + 'Special:Statistics>' + spoiler );
  3725. if ( reaction ) reaction.removeEmoji();
  3726. }
  3727. else {
  3728. var site = ovbody.items[body.query.wikidesc.id];
  3729. var vertical = [lang.overview.vertical, site.hub];
  3730. var topic = [lang.overview.topic, site.topic];
  3731. var founder = [lang.overview.founder, site.founding_user_id];
  3732. var manager = [lang.overview.manager, body.query.allmessages[0]['*']];
  3733. var created = [lang.overview.created, new Date(site.creation_date).toLocaleString(lang.dateformat, timeoptions)];
  3734. var articles = [lang.overview.articles, body.query.statistics.articles];
  3735. var pages = [lang.overview.pages, body.query.statistics.pages];
  3736. var edits = [lang.overview.edits, body.query.statistics.edits];
  3737. var users = [lang.overview.users, body.query.statistics.activeusers];
  3738. var description = [lang.overview.description, site.desc];
  3739. var image = [lang.overview.image, site.image];
  3740. if ( description[1] ) {
  3741. description[1] = description[1].escapeFormatting();
  3742. if ( description[1].length > 1000 ) description[1] = description[1].substring(0, 1000) + '\u2026';
  3743. }
  3744. if ( image[1] && image[1].startsWith( '/' ) ) image[1] = wiki.substring(0, wiki.length - 1) + image[1];
  3745. var title = body.query.pages['-1'].title;
  3746. var pagelink = wiki.toLink() + title.toTitle();
  3747. if ( msg.showEmbed() ) {
  3748. var text = '<' + pagelink + '>';
  3749. var embed = new Discord.RichEmbed().setAuthor( body.query.general.sitename ).setTitle( title.escapeFormatting() ).setURL( pagelink ).setThumbnail( site.wordmark ).addField( vertical[0], vertical[1], true ).addField( topic[0], topic[1], true );
  3750. }
  3751. else {
  3752. var embed = {};
  3753. var text = '<' + pagelink + '>\n\n' + vertical.join(' ') + '\n' + topic.join(' ');
  3754. }
  3755. if ( founder[1] > 0 ) request( {
  3756. uri: wiki + 'api.php?action=query&list=users&usprop=&usids=' + founder[1] + '&format=json',
  3757. json: true
  3758. }, function( userror, usresponse, usbody ) {
  3759. if ( usbody && usbody.warnings ) log_warn(usbody.warnings);
  3760. if ( userror || !usresponse || usresponse.statusCode !== 200 || !usbody || !usbody.query || !usbody.query.users || !usbody.query.users[0] ) {
  3761. console.log( '- ' + ( usresponse && usresponse.statusCode ) + ': Error while getting the wiki founder: ' + ( userror || usbody && usbody.error && usbody.error.info ) );
  3762. founder[1] = 'ID: ' + founder[1];
  3763. }
  3764. else {
  3765. var user = usbody.query.users[0].name;
  3766. if ( msg.showEmbed() ) founder[1] = '[' + user + '](' + wiki.toLink() + 'User:' + user.toTitle(true) + ')';
  3767. else founder[1] = user;
  3768. }
  3769. if ( msg.showEmbed() ) {
  3770. embed.addField( founder[0], founder[1], true );
  3771. if ( manager[1] ) embed.addField( manager[0], '[' + manager[1] + '](' + wiki.toLink() + 'User:' + manager[1].toTitle(true) + ') ([' + lang.overview.talk + '](' + wiki.toLink() + 'User_talk:' + manager[1].toTitle(true) + '))', true );
  3772. embed.addField( created[0], created[1], true ).addField( articles[0], articles[1], true ).addField( pages[0], pages[1], true ).addField( edits[0], edits[1], true ).addField( users[0], users[1], true ).setFooter( lang.overview.inaccurate );
  3773. if ( description[1] ) embed.addField( description[0], description[1] );
  3774. if ( image[1] ) embed.addField( image[0], image[1] ).setImage( image[1] );
  3775. }
  3776. else {
  3777. text += '\n' + founder.join(' ') + ( manager[1] ? '\n' + manager.join(' ') : '' ) + '\n' + created.join(' ') + '\n' + articles.join(' ') + '\n' + pages.join(' ') + '\n' + edits.join(' ') + '\n' + users.join(' ');
  3778. if ( description[1] ) text += '\n' + description.join(' ');
  3779. if ( image[1] ) {
  3780. text += '\n' + image.join(' ');
  3781. if ( msg.uploadFiles() ) embed.files = [image[1]];
  3782. }
  3783. text += '\n\n*' + lang.overview.inaccurate + '*';
  3784. }
  3785. msg.sendChannel( spoiler + text + spoiler, embed );
  3786. if ( reaction ) reaction.removeEmoji();
  3787. } );
  3788. else {
  3789. founder[1] = lang.overview.none;
  3790. if ( msg.showEmbed() ) {
  3791. embed.addField( founder[0], founder[1], true ).addField( created[0], created[1], true ).addField( articles[0], articles[1], true ).addField( pages[0], pages[1], true ).addField( edits[0], edits[1], true ).addField( users[0], users[1], true ).setFooter( lang.overview.inaccurate );
  3792. if ( description[1] ) embed.addField( description[0], description[1] );
  3793. if ( image[1] ) embed.addField( image[0], image[1] ).setImage( image[1] );
  3794. }
  3795. else {
  3796. text += '\n' + founder.join(' ') + '\n' + created.join(' ') + '\n' + articles.join(' ') + '\n' + pages.join(' ') + '\n' + edits.join(' ') + '\n' + users.join(' ');
  3797. if ( description[1] ) text += '\n' + description.join(' ');
  3798. if ( image[1] ) {
  3799. text += '\n' + image.join(' ');
  3800. if ( msg.uploadFiles() ) embed.files = [image[1]];
  3801. }
  3802. text += '\n\n*' + lang.overview.inaccurate + '*';
  3803. }
  3804. msg.sendChannel( spoiler + text + spoiler, embed );
  3805. if ( reaction ) reaction.removeEmoji();
  3806. }
  3807. }
  3808. } );
  3809. } );
  3810. }
  3811. function minecraft_bug(lang, mclang, msg, args, title, cmd, querystring, fragment, reaction, spoiler) {
  3812. var invoke = args[0];
  3813. args = args.slice(1);
  3814. if ( invoke && /\d+$/.test(invoke) && !args.length ) {
  3815. var project = '';
  3816. if ( /^\d+$/.test(invoke) ) project = 'MC-';
  3817. request( {
  3818. uri: 'https://bugs.mojang.com/rest/api/2/issue/' + encodeURIComponent( project + invoke ) + '?fields=summary,issuelinks,fixVersions,resolution,status',
  3819. json: true
  3820. }, function( error, response, body ) {
  3821. var link = 'https://bugs.mojang.com/browse/';
  3822. if ( error || !response || response.statusCode !== 200 || !body || body['status-code'] === 404 || body.errorMessages || body.errors ) {
  3823. if ( body && body.errorMessages ) {
  3824. if ( body.errorMessages.includes( 'Issue Does Not Exist' ) ) {
  3825. msg.reactEmoji('🤷');
  3826. }
  3827. else if ( body.errorMessages.includes( 'You do not have the permission to see the specified issue.' ) ) {
  3828. msg.sendChannel( spoiler + mclang.bug.private + '\n<' + link + project + invoke + '>' + spoiler );
  3829. }
  3830. else {
  3831. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the issue: ' + body.errorMessages.join(' - ') );
  3832. msg.reactEmoji('error');
  3833. }
  3834. }
  3835. else {
  3836. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the issue: ' + ( error || body && body.message ) );
  3837. if ( body && body['status-code'] === 404 ) msg.reactEmoji('error');
  3838. else msg.sendChannelError( spoiler + '<' + link + project + invoke + '>' + spoiler );
  3839. }
  3840. }
  3841. else {
  3842. if ( !body.fields ) {
  3843. msg.reactEmoji('error');
  3844. }
  3845. else {
  3846. var bugs = body.fields.issuelinks.filter( bug => bug.outwardIssue || ( bug.inwardIssue && bug.type.name != 'Duplicate' ) );
  3847. if ( bugs.length ) {
  3848. var embed = new Discord.RichEmbed();
  3849. var extrabugs = [];
  3850. bugs.forEach( bug => {
  3851. var ward = ( bug.outwardIssue ? 'outward' : 'inward' );
  3852. var issue = bug[ward + 'Issue'];
  3853. var name = bug.type[ward] + ' ' + issue.key;
  3854. var value = issue.fields.status.name + ': [' + issue.fields.summary.escapeFormatting() + '](' + link + issue.key + ')';
  3855. if ( embed.fields.length < 25 ) embed.addField( name, value );
  3856. else extrabugs.push({name,value,inline:false});
  3857. } );
  3858. if ( extrabugs.length ) embed.setFooter( mclang.bug.more.replaceSave( '%s', extrabugs.length ) );
  3859. }
  3860. var status = '**' + ( body.fields.resolution ? body.fields.resolution.name : body.fields.status.name ) + ':** ';
  3861. var fixed = '';
  3862. if ( body.fields.resolution && body.fields.fixVersions && body.fields.fixVersions.length ) {
  3863. fixed = '\n' + mclang.bug.fixed + ' ' + body.fields.fixVersions.map( v => v.name ).join(', ');
  3864. }
  3865. msg.sendChannel( spoiler + status + body.fields.summary.escapeFormatting() + '\n<' + link + body.key + '>' + fixed + spoiler, embed );
  3866. }
  3867. }
  3868. if ( reaction ) reaction.removeEmoji();
  3869. } );
  3870. }
  3871. else if ( invoke && invoke.toLowerCase() === 'version' && args.length && args.join(' ').length < 100 ) {
  3872. var jql = 'fixVersion="' + args.join(' ').replace( /(["\\])/g, '\\$1' ).toSearch() + '"+order+by+key';
  3873. request( {
  3874. uri: 'https://bugs.mojang.com/rest/api/2/search?fields=summary,resolution,status&jql=' + jql + '&maxResults=25',
  3875. json: true
  3876. }, function( error, response, body ) {
  3877. var link = 'https://bugs.mojang.com/issues/?jql=' + jql;
  3878. if ( error || !response || response.statusCode !== 200 || !body || body['status-code'] === 404 || body.errorMessages || body.errors ) {
  3879. if ( body && body.errorMessages ) {
  3880. if ( body.errorMessages.includes( 'The value \'' + args.join(' ') + '\' does not exist for the field \'fixVersion\'.' ) ) {
  3881. msg.reactEmoji('🤷');
  3882. }
  3883. else {
  3884. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the issues: ' + body.errorMessages.join(' - ') );
  3885. msg.reactEmoji('error');
  3886. }
  3887. }
  3888. else {
  3889. console.log( '- ' + ( response && response.statusCode ) + ': Error while getting the issues: ' + ( error || body && body.message ) );
  3890. if ( body && body['status-code'] === 404 ) msg.reactEmoji('error');
  3891. else msg.sendChannelError( spoiler + '<' + link + '>' + spoiler );
  3892. }
  3893. }
  3894. else {
  3895. if ( !body.issues ) {
  3896. msg.reactEmoji('error');
  3897. }
  3898. else {
  3899. if ( body.total > 0 ) {
  3900. var embed = new Discord.RichEmbed();
  3901. body.issues.forEach( bug => {
  3902. var status = ( bug.fields.resolution ? bug.fields.resolution.name : bug.fields.status.name );
  3903. var value = status + ': [' + bug.fields.summary.escapeFormatting() + '](https://bugs.mojang.com/browse/' + bug.key + ')';
  3904. embed.addField( bug.key, value );
  3905. } );
  3906. if ( body.total > 25 ) embed.setFooter( mclang.bug.more.replaceSave( '%s', body.total - 25 ) );
  3907. }
  3908. var total = '**' + args.join(' ') + ':** ' + mclang.bug.total.replaceSave( '%s', body.total );
  3909. msg.sendChannel( spoiler + total + '\n<' + link + '>' + spoiler, embed );
  3910. }
  3911. }
  3912. if ( reaction ) reaction.removeEmoji();
  3913. } );
  3914. }
  3915. else {
  3916. msg.notminecraft = true;
  3917. gamepedia_check_wiki(lang, msg, title, mclang.link, cmd, reaction, spoiler, querystring, fragment);
  3918. }
  3919. }
  3920. function minecraft_command(lang, mclang, msg, befehl, args, title, cmd, querystring, fragment, reaction, spoiler) {
  3921. befehl = befehl.toLowerCase();
  3922. var aliasCmd = ( minecraft.cmd.aliase[befehl] || befehl );
  3923. if ( aliasCmd in minecraft.cmd.list ) {
  3924. var regex = new RegExp('/' + aliasCmd, 'g');
  3925. var cmdSyntax = minecraft.cmd.list[aliasCmd].join( '\n' ).replaceSave( regex, '/' + befehl );
  3926. msg.sendChannel( spoiler + '```md\n' + cmdSyntax + '```<' + mclang.link + mclang.cmd.page + aliasCmd + '>' + spoiler, {split:{maxLength:2000,prepend:spoiler + '```md\n',append:'```' + spoiler}} );
  3927. if ( reaction ) reaction.removeEmoji();
  3928. }
  3929. else {
  3930. msg.reactEmoji('❓');
  3931. msg.notminecraft = true;
  3932. gamepedia_check_wiki(lang, msg, title, mclang.link, cmd, reaction, spoiler, querystring, fragment);
  3933. }
  3934. }
  3935. function minecraft_command2(lang, mclang, msg, args, title, cmd, querystring, fragment, reaction, spoiler) {
  3936. if ( args.join('') ) {
  3937. if ( args[0].startsWith( '/' ) ) minecraft_command(lang, mclang, msg, args[0].substring(1), args.slice(1), title, cmd, querystring, fragment, reaction, spoiler);
  3938. else minecraft_command(lang, mclang, msg, args[0], args.slice(1), title, cmd, querystring, fragment, reaction, spoiler);
  3939. }
  3940. else {
  3941. msg.notminecraft = true;
  3942. gamepedia_check_wiki(lang, msg, title, mclang.link, cmd, reaction, spoiler, querystring, fragment);
  3943. }
  3944. }
  3945. function cmd_multiline(lang, msg, args, line, wiki) {
  3946. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) {
  3947. if ( msg.isAdmin() ) msg.reactEmoji('error', true);
  3948. else msg.reactEmoji('❌');
  3949. }
  3950. }
  3951. function cmd_get(lang, msg, args, line, wiki) {
  3952. var id = args.join().replace( /^\\?<(?:@!?|#)(\d+)>$/, '$1' );
  3953. if ( /^\d+$/.test(id) ) {
  3954. if ( client.guilds.has(id) ) {
  3955. var guild = client.guilds.get(id);
  3956. var guildname = ['Guild:', guild.name.escapeFormatting() + ' `' + guild.id + '`' + ( pause[guild.id] ? '\\*' : '' )];
  3957. var guildowner = ['Owner:', guild.owner.user.tag.escapeFormatting() + ' `' + guild.ownerID + '` ' + guild.owner.toString()];
  3958. var guildsize = ['Size:', guild.memberCount + ' members (' + guild.members.filter( member => member.user.bot ).size + ' bots)'];
  3959. var guildpermissions = ['Missing permissions:', ( guild.me.permissions.has(defaultPermissions) ? '*none*' : '`' + guild.me.permissions.missing(defaultPermissions).join('`, `') + '`' )];
  3960. var guildsettings = ['Settings:', '*unknown*'];
  3961. db.all( 'SELECT channel, prefix, lang, wiki, inline FROM discord WHERE guild = ? ORDER BY channel ASC', [guild.id], (dberror, rows) => {
  3962. if ( dberror ) {
  3963. console.log( '- Error while getting the settings: ' + dberror );
  3964. }
  3965. else if ( rows.length ) {
  3966. row = rows.find( row => !row.channel );
  3967. row.patreon = guild.id in patreons;
  3968. row.voice = guild.id in voice;
  3969. guildsettings[1] = '```json\n' + JSON.stringify( rows, null, '\t' ) + '\n```';
  3970. }
  3971. else guildsettings[1] = '*default*';
  3972. if ( msg.showEmbed() ) {
  3973. var embed = new Discord.RichEmbed().addField( guildname[0], guildname[1] ).addField( guildowner[0], guildowner[1] ).addField( guildsize[0], guildsize[1] ).addField( guildpermissions[0], guildpermissions[1] );
  3974. var split = Discord.Util.splitMessage( guildsettings[1], {char:',\n',maxLength:1000,prepend:'```json\n',append:',\n```'} );
  3975. if ( split.length < guildsettings[1].length ) {
  3976. if ( split.length > 5 ) {
  3977. msg.sendChannel( '', embed, true );
  3978. msg.sendChannel( guildsettings.join(' '), {split:{char:',\n',prepend:'```json\n',append:',\n```'}}, true );
  3979. return;
  3980. }
  3981. split.forEach( guildsettingspart => embed.addField( guildsettings[0], guildsettingspart ) );
  3982. }
  3983. else embed.addField( guildsettings[0], split );
  3984. msg.sendChannel( '', embed, true );
  3985. }
  3986. else {
  3987. var text = guildname.join(' ') + '\n' + guildowner.join(' ') + '\n' + guildsize.join(' ') + '\n' + guildpermissions.join(' ') + '\n' + guildsettings.join(' ');
  3988. msg.sendChannel( text, {split:{char:',\n',prepend:'```json\n',append:',\n```'}}, true );
  3989. }
  3990. } );
  3991. } else if ( client.guilds.some( guild => guild.members.has(id) ) ) {
  3992. var username = [];
  3993. var guildlist = ['Guilds:'];
  3994. var guilds = client.guilds.filter( guild => guild.members.has(id) );
  3995. guildlist.push('\n' + guilds.map( function(guild) {
  3996. var member = guild.members.get(id);
  3997. if ( !username.length ) username.push('User:', member.user.tag.escapeFormatting() + ' `' + member.id + '` ' + member.toString());
  3998. return guild.name.escapeFormatting() + ' `' + guild.id + '`' + ( member.permissions.has('MANAGE_GUILD') ? '\\*' : '' );
  3999. } ).join('\n'));
  4000. if ( guildlist[1].length > 1000 ) guildlist[1] = guilds.size;
  4001. if ( msg.showEmbed() ) {
  4002. var text = '';
  4003. var embed = new Discord.RichEmbed().addField( username[0], username[1] ).addField( guildlist[0], guildlist[1] );
  4004. }
  4005. else {
  4006. var embed = {};
  4007. var text = username.join(' ') + '\n' + guildlist.join(' ');
  4008. }
  4009. msg.sendChannel( text, embed, true );
  4010. } else if ( client.guilds.some( guild => guild.channels.filter( chat => chat.type === 'text' ).has(id) ) ) {
  4011. var channel = client.guilds.find( guild => guild.channels.filter( chat => chat.type === 'text' ).has(id) ).channels.get(id);
  4012. var channelguild = ['Guild:', channel.guild.name.escapeFormatting() + ' `' + channel.guild.id + '`' + ( pause[channel.guild.id] ? '\\*' : '' )];
  4013. var channelname = ['Channel:', '#' + channel.name.escapeFormatting() + ' `' + channel.id + '` ' + channel.toString()];
  4014. var channelpermissions = ['Missing permissions:', ( channel.memberPermissions(channel.guild.me).has(defaultPermissions) ? '*none*' : '`' + channel.memberPermissions(channel.guild.me).missing(defaultPermissions).join('`, `') + '`' )];
  4015. var channellang = ['Language:', '*unknown*'];
  4016. var channelwiki = ['Default Wiki:', '*unknown*'];
  4017. var channelinline = ['Inline commands:', '*unknown*'];
  4018. db.get( 'SELECT lang, wiki, inline FROM discord WHERE guild = ? AND (channel = ? OR channel IS NULL) ORDER BY channel DESC', [channel.guild.id, channel.id], (dberror, row) => {
  4019. if ( dberror ) {
  4020. console.log( '- Error while getting the settings: ' + dberror );
  4021. }
  4022. else if ( row ) {
  4023. channellang[1] = row.lang;
  4024. channelwiki[1] = row.wiki;
  4025. channelinline[1] = ( row.inline ? 'disabled' : 'enabled' );
  4026. }
  4027. else {
  4028. channellang[1] = defaultSettings.lang;
  4029. channelwiki[1] = defaultSettings.wiki;
  4030. channelinline[1] = 'enabled';
  4031. }
  4032. if ( msg.showEmbed() ) {
  4033. var text = '';
  4034. var embed = new Discord.RichEmbed().addField( channelguild[0], channelguild[1] ).addField( channelname[0], channelname[1] ).addField( channelpermissions[0], channelpermissions[1] ).addField( channellang[0], channellang[1] ).addField( channelwiki[0], channelwiki[1] ).addField( channelinline[0], channelinline[1] );
  4035. }
  4036. else {
  4037. var embed = {};
  4038. var text = channelguild.join(' ') + '\n' + channelname.join(' ') + '\n' + channelpermissions.join(' ') + '\n' + channellang.join(' ') + '\n' + channelwiki[0] + ' <' + channelwiki[1] + '>\n' + channelinline.join(' ');
  4039. }
  4040. msg.sendChannel( text, embed, true );
  4041. } );
  4042. } else msg.replyMsg( 'I couldn\'t find a result for `' + id + '`', {}, true );
  4043. } else if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  4044. }
  4045. function cmd_patreon(lang, msg, args, line, wiki) {
  4046. if ( msg.channel.id !== process.env.channel || !args.join('') ) {
  4047. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  4048. return;
  4049. }
  4050. if ( args[0] === 'enable' && /^\d+$/.test(args.slice(1).join(' ')) ) {
  4051. if ( !client.guilds.has(args[1]) ) return msg.replyMsg( 'I\'m not on a server with the id `' + args[1] + '`.', {}, true );
  4052. if ( args[1] in patreons ) return msg.replyMsg( '"' + client.guilds.get(args[1]) + '" has the patreon features already enabled.', {}, true );
  4053. return db.get( 'SELECT count, COUNT(guild) guilds FROM patreons LEFT JOIN discord ON discord.patreon = patreons.patreon WHERE patreons.patreon = ? GROUP BY patreons.patreon', [msg.author.id], (dberror, row) => {
  4054. if ( dberror ) {
  4055. console.log( '- Error while getting the patreon: ' + dberror );
  4056. msg.replyMsg( 'I got an error while searching for you, please try again later.', {}, true );
  4057. return dberror;
  4058. }
  4059. if ( !row ) return msg.replyMsg( 'you can\'t have any server.', {}, true );
  4060. if ( row.count <= row.guilds ) return msg.replyMsg( 'you already reached your maximal server count.', {}, true );
  4061. db.run( 'UPDATE discord SET patreon = ? WHERE guild = ? AND channel IS NULL', [msg.author.id, args[1]], function (error) {
  4062. if ( error ) {
  4063. console.log( '- Error while updating the guild: ' + error );
  4064. msg.replyMsg( 'I got an error while updating the server, please try again later.', {}, true );
  4065. return error;
  4066. }
  4067. if ( !this.changes ) return db.run( 'INSERT INTO discord(guild, patreon) VALUES(?, ?)', [args[1], msg.author.id], function (inserror) {
  4068. if ( inserror ) {
  4069. console.log( '- Error while adding the guild: ' + inserror );
  4070. msg.replyMsg( 'I got an error while updating the server, please try again later.', {}, true );
  4071. return inserror;
  4072. }
  4073. console.log( '- Guild successfully added.' );
  4074. patreons[args[1]] = process.env.prefix;
  4075. msg.replyMsg( 'the patreon features are now enabled on "' + client.guilds.get(args[1]) + '".', {}, true );
  4076. } );
  4077. console.log( '- Guild successfully updated.' );
  4078. patreons[args[1]] = process.env.prefix;
  4079. msg.replyMsg( 'the patreon features are now enabled on "' + client.guilds.get(args[1]) + '".', {}, true );
  4080. } );
  4081. } );
  4082. }
  4083. if ( args[0] === 'disable' && /^\d+$/.test(args.slice(1).join(' ')) ) {
  4084. if ( !client.guilds.has(args[1]) ) return msg.replyMsg( 'I\'m not on a server with the id `' + args[1] + '`.', {}, true );
  4085. if ( !( args[1] in patreons ) ) return msg.replyMsg( '"' + client.guilds.get(args[1]) + '" doesn\'t have the patreon features enabled.', {}, true );
  4086. return db.get( 'SELECT lang, inline FROM discord WHERE guild = ? AND patreon = ?', [args[1], msg.author.id], (dberror, row) => {
  4087. if ( dberror ) {
  4088. console.log( '- Error while getting the guild: ' + dberror );
  4089. msg.replyMsg( 'I got an error while searching for the server, please try again later.', {}, true );
  4090. return dberror;
  4091. }
  4092. if ( !row ) return msg.replyMsg( 'you didn\'t enable the patreon features for "' + client.guilds.get(args[1]) + '"!', {}, true );
  4093. db.run( 'UPDATE discord SET lang = ?, inline = ?, prefix = ?, patreon = NULL WHERE guild = ?', [row.lang, row.inline, process.env.prefix, args[1]], function (error) {
  4094. if ( error ) {
  4095. console.log( '- Error while updating the guild: ' + error );
  4096. msg.replyMsg( 'I got an error while updating the server, please try again later.', {}, true );
  4097. return error;
  4098. }
  4099. console.log( '- Guild successfully updated.' );
  4100. delete patreons[args[1]];
  4101. msg.replyMsg( 'the patreon features are now disabled on "' + client.guilds.get(args[1]) + '".', {}, true );
  4102. } );
  4103. } );
  4104. }
  4105. if ( args[1] ) args[1] = args[1].replace( /^\\?<@!?(\d+)>$/, '$1' );
  4106. if ( args[0] === 'check' ) {
  4107. if ( !args.slice(1).join('') ) return db.get( 'SELECT count, GROUP_CONCAT(guild) guilds FROM patreons LEFT JOIN discord ON discord.patreon = patreons.patreon WHERE patreons.patreon = ? GROUP BY patreons.patreon', [msg.author.id], (dberror, row) => {
  4108. if ( dberror ) {
  4109. console.log( '- Error while getting the patreon: ' + dberror );
  4110. msg.replyMsg( 'I got an error while searching for you, please try again later.', {}, true );
  4111. return dberror;
  4112. }
  4113. if ( !row ) return msg.replyMsg( 'you can\'t have any server.', {}, true );
  4114. var text = 'you can have up to ' + row.count + ' server.\n\n';
  4115. if ( row.guilds ) {
  4116. var guilds = row.guilds.split(',').map( guild => '`' + guild + '` ' + client.guilds.get(guild) );
  4117. text += 'Currently you have ' + guilds.length + ' server:\n' + guilds.join('\n');
  4118. }
  4119. else text += '*You don\'t have any server yet.*';
  4120. msg.replyMsg( text, {}, true );
  4121. } );
  4122. if ( msg.isOwner() && /^\d+$/.test(args.slice(1).join(' ')) ) return db.get( 'SELECT count, GROUP_CONCAT(guild) guilds FROM patreons LEFT JOIN discord ON discord.patreon = patreons.patreon WHERE patreons.patreon = ? GROUP BY patreons.patreon', [args[1]], (dberror, row) => {
  4123. if ( dberror ) {
  4124. console.log( '- Error while getting the patreon: ' + dberror );
  4125. msg.replyMsg( 'I got an error while searching for <@' + args[1] + '>, please try again later.', {}, true );
  4126. return dberror;
  4127. }
  4128. if ( !row ) return msg.replyMsg( '<@' + args[1] + '> can\'t have any server.', {}, true );
  4129. var text = '<@' + args[1] + '> can have up to ' + row.count + ' server.\n\n';
  4130. if ( row.guilds ) {
  4131. var guilds = row.guilds.split(',').map( guild => '`' + guild + '` ' + client.guilds.get(guild) );
  4132. text += 'Currently they have ' + guilds.length + ' server:\n' + guilds.join('\n');
  4133. }
  4134. else text += '*They don\'t have any server yet.*';
  4135. msg.replyMsg( text, {}, true );
  4136. } );
  4137. }
  4138. if ( args[0] === 'edit' && msg.isOwner() && /^\d+ [\+\-]?\d+$/.test(args.slice(1).join(' ')) ) return db.get( 'SELECT count, GROUP_CONCAT(guild) guilds FROM patreons LEFT JOIN discord ON discord.patreon = patreons.patreon WHERE patreons.patreon = ? GROUP BY patreons.patreon', [args[1]], (dberror, row) => {
  4139. if ( dberror ) {
  4140. console.log( '- Error while getting the patreon: ' + dberror );
  4141. msg.replyMsg( 'I got an error while searching for <@' + args[1] + '>, please try again later.', {}, true );
  4142. return dberror;
  4143. }
  4144. var value = parseInt(args[1], 10);
  4145. var count = ( row ? row.count : 0 );
  4146. var guilds = ( row && row.guilds ? row.guilds.split(',') : [] );
  4147. if ( args[1].startsWith( '+' ) || args[1].startsWith( '-' ) ) count += value;
  4148. else count = value;
  4149. if ( count <= 0 ) return db.run( 'DELETE FROM patreons WHERE patreon = ?', [args[1]], function (error) {
  4150. if ( error ) {
  4151. console.log( '- Error while deleting the patreon: ' + error );
  4152. msg.replyMsg( 'I got an error while deleting <@' + args[1] + '>, please try again later.', {}, true );
  4153. return error;
  4154. }
  4155. console.log( '- Patreon successfully deleted.' );
  4156. if ( !guilds.length ) return msg.replyMsg( '<@' + args[1] + '> is no longer a patreon.', {}, true );
  4157. db.each( 'SELECT guild, lang, inline FROM discord WHERE guild IN (' + guilds.map( guild => '?' ).join(', ') + ') AND channel IS NULL', guilds, (eacherror, eachrow) => {
  4158. if ( eacherror ) {
  4159. console.log( '- Error while getting the guild: ' + eacherror );
  4160. msg.replyMsg( 'I couldn\'t disable the patreon features.', {}, true );
  4161. return eacherror;
  4162. }
  4163. db.run( 'UPDATE discord SET lang = ?, inline = ?, prefix = ? WHERE guild = ?', [eachrow.lang, eachrow.inline, process.env.prefix, eachrow.guild], function (uperror) {
  4164. if ( uperror ) {
  4165. console.log( '- Error while updating the guild: ' + uperror );
  4166. msg.replyMsg( 'I couldn\'t disable the patreon features for `' + eachrow.guild + '`.', {}, true );
  4167. return uperror;
  4168. }
  4169. console.log( '- Guild successfully updated.' );
  4170. delete patreons[eachrow.guild];
  4171. } );
  4172. }, (eacherror) => {
  4173. if ( eacherror ) {
  4174. console.log( '- Error while getting the guilds: ' + eacherror );
  4175. msg.replyMsg( 'I couldn\'t disable the patreon features for `' + guilds.join('`, `') + '`.', {}, true );
  4176. return eacherror;
  4177. }
  4178. msg.replyMsg( '<@' + args[1] + '> is no longer a patreon.', {}, true );
  4179. } );
  4180. } );
  4181. if ( !row ) return db.run( 'INSERT INTO patreons(patreon, count) VALUES(?, ?)', [args[1], count], function (error) {
  4182. if ( error ) {
  4183. console.log( '- Error while adding the patreon: ' + error );
  4184. msg.replyMsg( 'I got an error while adding <@' + args[1] + '>, please try again later.', {}, true );
  4185. return error;
  4186. }
  4187. console.log( '- Patreon successfully added.' );
  4188. msg.replyMsg( '<@' + args[1] + '> can now have up to ' + count + ' server.', {}, true );
  4189. } );
  4190. db.run( 'UPDATE patreons SET count = ? WHERE patreon = ?', [count, args[1]], function (error) {
  4191. if ( error ) {
  4192. console.log( '- Error while updating the patreon: ' + error );
  4193. msg.replyMsg( 'I got an error while updating <@' + args[1] + '>, please try again later.', {}, true );
  4194. return error;
  4195. }
  4196. console.log( '- Patreon successfully updated.' );
  4197. var text = '<@' + args[1] + '> can now have up to ' + count + ' server.';
  4198. if ( count < guilds.length ) text += '\n\n**They are now above their server limit!**';
  4199. msg.replyMsg( text, {}, true );
  4200. } );
  4201. } );
  4202. if ( msg.channel.type !== 'text' || !pause[msg.guild.id] ) cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  4203. }
  4204. function discussion_formatting(jsonModel) {
  4205. var description = '';
  4206. switch ( jsonModel.type ) {
  4207. case 'doc':
  4208. if ( jsonModel.content ) jsonModel.content.forEach( content => description += discussion_formatting(content) );
  4209. break;
  4210. case 'paragraph':
  4211. if ( jsonModel.content ) jsonModel.content.forEach( content => description += discussion_formatting(content) );
  4212. description += '\n';
  4213. break;
  4214. case 'text':
  4215. var prepend = '';
  4216. var append = '';
  4217. if ( jsonModel.marks ) {
  4218. jsonModel.marks.forEach( mark => {
  4219. switch ( mark.type ) {
  4220. case 'mention':
  4221. prepend += '[';
  4222. append = ']({@wiki}f/u/' + mark.attrs.userId + ')' + append;
  4223. break;
  4224. case 'link':
  4225. prepend += '[';
  4226. append = '](' + mark.attrs.href + ')' + append;
  4227. break;
  4228. case 'strong':
  4229. prepend += '**';
  4230. append = '**' + append;
  4231. break;
  4232. case 'em':
  4233. prepend += '_';
  4234. append = '_' + append;
  4235. break;
  4236. }
  4237. } );
  4238. }
  4239. description += prepend + jsonModel.text.escapeFormatting() + append;
  4240. break;
  4241. case 'image':
  4242. description += '{@' + jsonModel.attrs.id + '}\n';
  4243. break;
  4244. case 'code_block':
  4245. description += '```\n';
  4246. if ( jsonModel.content ) jsonModel.content.forEach( content => description += discussion_formatting(content) );
  4247. description += '\n```\n';
  4248. break;
  4249. case 'bulletList':
  4250. jsonModel.content.forEach( listItem => {
  4251. description += ' • ';
  4252. if ( listItem.content ) listItem.content.forEach( content => description += discussion_formatting(content) );
  4253. } );
  4254. break;
  4255. case 'orderedList':
  4256. var n = 1;
  4257. jsonModel.content.forEach( listItem => {
  4258. description += ' ' + n + '. ';
  4259. n++;
  4260. if ( listItem.content ) listItem.content.forEach( content => description += discussion_formatting(content) );
  4261. } );
  4262. break;
  4263. }
  4264. return description;
  4265. }
  4266. function htmlToPlain(html) {
  4267. var text = '';
  4268. var parser = new htmlparser.Parser( {
  4269. ontext: (htmltext) => {
  4270. text += htmltext.escapeFormatting();
  4271. }
  4272. }, {decodeEntities:true} );
  4273. parser.write( html );
  4274. parser.end();
  4275. return text;
  4276. };
  4277. function htmlToDiscord(html) {
  4278. var text = '';
  4279. var parser = new htmlparser.Parser( {
  4280. onopentag: (tagname, attribs) => {
  4281. switch (tagname) {
  4282. case 'b':
  4283. text += '**';
  4284. break;
  4285. case 'i':
  4286. text += '*';
  4287. break;
  4288. case 's':
  4289. text += '~~';
  4290. break;
  4291. case 'u':
  4292. text += '__';
  4293. break;
  4294. }
  4295. },
  4296. ontext: (htmltext) => {
  4297. text += htmltext.escapeFormatting();
  4298. },
  4299. onclosetag: (tagname) => {
  4300. switch (tagname) {
  4301. case 'b':
  4302. text += '**';
  4303. break;
  4304. case 'i':
  4305. text += '*';
  4306. break;
  4307. case 's':
  4308. text += '~~';
  4309. break;
  4310. case 'u':
  4311. text += '__';
  4312. break;
  4313. }
  4314. }
  4315. }, {decodeEntities:true} );
  4316. parser.write( html );
  4317. parser.end();
  4318. return text;
  4319. };
  4320. String.prototype.noWiki = function(href) {
  4321. if ( !href ) return false;
  4322. else if ( this.endsWith( '.gamepedia.com/' ) ) return 'https://www.gamepedia.com/' === href;
  4323. else return [
  4324. this.replace( /^https:\/\/([a-z\d-]{1,50}\.(?:fandom\.com|wikia\.org))\/(?:[a-z-]{1,8}\/)?$/, 'https://community.fandom.com/wiki/Community_Central:Not_a_valid_community?from=$1' ),
  4325. this + 'language-wikis'
  4326. ].includes( href );
  4327. };
  4328. String.prototype.isFandom = function() {
  4329. return /^https:\/\/[a-z\d-]{1,50}\.(?:fandom\.com|wikia\.org)\/(?:[a-z-]{1,8}\/)?$/.test(this);
  4330. };
  4331. String.prototype.toLink = function() {
  4332. if ( this.endsWith( '.org/w/' ) ) return this.substring(0, this.length - 2) + 'wiki/';
  4333. else return this;
  4334. };
  4335. String.prototype.toLink = function() {
  4336. if ( this.endsWith( '.gamepedia.com/' ) ) return this;
  4337. else if ( this.isFandom() ) return this + 'wiki/';
  4338. else if ( this.endsWith( '.org/w/' ) ) return this.substring(0, this.length - 2) + 'wiki/';
  4339. else return this;
  4340. };
  4341. String.prototype.isMention = function(guild) {
  4342. var text = this.trim();
  4343. return text === '@' + client.user.username || text.replace( /^<@!?(\d+)>$/, '$1' ) === client.user.id || ( guild && text === '@' + guild.me.displayName );
  4344. };
  4345. Discord.Message.prototype.isAdmin = function() {
  4346. return this.channel.type === 'text' && this.member && ( this.member.permissions.has('MANAGE_GUILD') || ( this.isOwner() && this.evalUsed ) );
  4347. };
  4348. Discord.Message.prototype.isOwner = function() {
  4349. return this.author.id === process.env.owner;
  4350. };
  4351. Discord.Message.prototype.showEmbed = function() {
  4352. return this.channel.type !== 'text' || this.channel.permissionsFor(client.user).has('EMBED_LINKS');
  4353. };
  4354. Discord.Message.prototype.uploadFiles = function() {
  4355. return this.channel.type !== 'text' || this.channel.permissionsFor(client.user).has('ATTACH_FILES');
  4356. };
  4357. Array.prototype.toEmojis = function() {
  4358. var text = this.join(' ');
  4359. var regex = /(<a?:)(\d+)(>)/g;
  4360. if ( regex.test(text) ) {
  4361. regex.lastIndex = 0;
  4362. var emojis = client.emojis;
  4363. var entry = null;
  4364. while ( ( entry = regex.exec(text) ) !== null ) {
  4365. if ( emojis.has(entry[2]) ) {
  4366. text = text.replaceSave(entry[0], emojis.get(entry[2]).toString());
  4367. } else {
  4368. text = text.replaceSave(entry[0], entry[1] + 'unknown_emoji:' + entry[2] + entry[3]);
  4369. }
  4370. }
  4371. return text.split(' ');
  4372. }
  4373. else return this;
  4374. };
  4375. String.prototype.toTitle = function(isMarkdown = false) {
  4376. var title = this.replace( / /g, '_' ).replace( /\%/g, '%25' ).replace( /\\/g, '%5C' ).replace( /\?/g, '%3F' ).replace( /@(here|everyone)/g, '%40$1' );
  4377. if ( isMarkdown ) title = title.replace( /([\(\)])/g, '\\$1' );
  4378. return title;
  4379. };
  4380. String.prototype.toSearch = function() {
  4381. return encodeURIComponent( this ).replace( /%20/g, '+' );
  4382. };
  4383. String.prototype.toSection = function() {
  4384. return encodeURIComponent( this.replace( / /g, '_' ) ).replace( /\'/g, '%27' ).replace( /\(/g, '%28' ).replace( /\)/g, '%29' ).replace( /\%/g, '.' );
  4385. };
  4386. String.prototype.toFormatting = function(showEmbed = false, ...args) {
  4387. if ( showEmbed ) return this.toMarkdown(...args);
  4388. else return this.toPlaintext();
  4389. };
  4390. String.prototype.toMarkdown = function(wiki, title = '') {
  4391. var text = this.replace( /[\(\)\\]/g, '\\$&' );
  4392. var link = null;
  4393. while ( ( link = /\[\[(?:([^\|\]]+)\|)?([^\]]+)\]\]([a-z]*)/g.exec(text) ) !== null ) {
  4394. if ( link[1] ) {
  4395. var page = ( /^[#\/]/.test(link[1]) ? title.toTitle(true) + ( /^#/.test(link[1]) ? '#' + link[1].substring(1).toSection() : link[1].toTitle(true) ) : link[1].toTitle(true) );
  4396. text = text.replaceSave( link[0], '[' + link[2] + link[3] + '](' + wiki.toLink() + page + ')' );
  4397. } else {
  4398. var page = ( /^[#\/]/.test(link[2]) ? title.toTitle(true) + ( /^#/.test(link[2]) ? '#' + link[2].substring(1).toSection() : link[2].toTitle(true) ) : link[2].toTitle(true) );
  4399. text = text.replaceSave( link[0], '[' + link[2] + link[3] + '](' + wiki.toLink() + page + ')' );
  4400. }
  4401. }
  4402. while ( title !== '' && ( link = /\/\*\s*([^\*]+?)\s*\*\/\s*(.)?/g.exec(text) ) !== null ) {
  4403. var page = title.toTitle(true) + '#' + link[1].toSection();
  4404. text = text.replaceSave( link[0], '[→](' + wiki.toLink() + page + ')' + link[1] + ( link[2] ? ': ' + link[2] : '' ) );
  4405. }
  4406. return text.escapeFormatting(true);
  4407. };
  4408. String.prototype.toPlaintext = function() {
  4409. return this.replace( /\[\[(?:[^\|\]]+\|)?([^\]]+)\]\]/g, '$1' ).replace( /\/\*\s*([^\*]+?)\s*\*\//g, '→$1:' ).escapeFormatting();
  4410. };
  4411. String.prototype.escapeFormatting = function(isMarkdown) {
  4412. var text = this;
  4413. if ( !isMarkdown ) text = text.replace( /[\(\)\\]/g, '\\$&' );
  4414. return text.replace( /[`_\*~:<>{}@\|]|\/\//g, '\\$&' );
  4415. };
  4416. String.prototype.replaceSave = function(pattern, replacement) {
  4417. return this.replace( pattern, ( typeof replacement === 'string' ? replacement.replace( /\$/g, '$$$$' ) : replacement ) );
  4418. };
  4419. Discord.Message.prototype.reactEmoji = function(name, ignorePause = false) {
  4420. if ( this.channel.type !== 'text' || !pause[this.guild.id] || ( ignorePause && ( this.isAdmin() || this.isOwner() ) ) ) {
  4421. var emoji = '440871715938238494';
  4422. switch ( name ) {
  4423. case 'nowiki':
  4424. emoji = '505884572001763348';
  4425. break;
  4426. case 'error':
  4427. emoji = '440871715938238494';
  4428. break;
  4429. case 'support':
  4430. emoji = '448222377009086465';
  4431. break;
  4432. case 'oppose':
  4433. emoji = '448222455425794059';
  4434. break;
  4435. default:
  4436. emoji = name;
  4437. }
  4438. return this.react(emoji).catch(log_error);
  4439. } else {
  4440. console.log( '- Aborted, paused.' );
  4441. return Promise.resolve();
  4442. }
  4443. };
  4444. Discord.MessageReaction.prototype.removeEmoji = function() {
  4445. return this.remove().catch(log_error);
  4446. };
  4447. Discord.Message.prototype.sendChannel = function(content, options, ignorePause = false) {
  4448. if ( this.channel.type !== 'text' || !pause[this.guild.id] || ( ignorePause && ( this.isAdmin() || this.isOwner() ) ) ) {
  4449. return this.channel.send(content, options).then( msg => {
  4450. if ( msg.length ) msg.forEach( message => message.allowDelete(this.author.id) );
  4451. else msg.allowDelete(this.author.id);
  4452. return msg;
  4453. }, log_error );
  4454. } else {
  4455. console.log( '- Aborted, paused.' );
  4456. return Promise.resolve();
  4457. }
  4458. };
  4459. Discord.Message.prototype.sendChannelError = function(content, options) {
  4460. return this.channel.send(content, options).then( msg => {
  4461. if ( msg.length ) msg.forEach( message => {
  4462. message.reactEmoji('error');
  4463. message.allowDelete(this.author.id);
  4464. } );
  4465. else {
  4466. msg.reactEmoji('error');
  4467. msg.allowDelete(this.author.id);
  4468. }
  4469. return msg;
  4470. }, log_error );
  4471. };
  4472. Discord.Message.prototype.replyMsg = function(content, options, ignorePause = false) {
  4473. if ( this.channel.type !== 'text' || !pause[this.guild.id] || ( ignorePause && ( this.isAdmin() || this.isOwner() ) ) ) {
  4474. return this.reply(content, options).then( msg => {
  4475. if ( msg.length ) msg.forEach( message => message.allowDelete(this.author.id) );
  4476. else msg.allowDelete(this.author.id);
  4477. return msg;
  4478. }, log_error );
  4479. } else {
  4480. console.log( '- Aborted, paused.' );
  4481. return Promise.resolve();
  4482. }
  4483. };
  4484. Discord.Message.prototype.deleteMsg = function(timeout = 0) {
  4485. return this.delete(timeout).catch(log_error);
  4486. };
  4487. Discord.Message.prototype.allowDelete = function(author) {
  4488. return this.awaitReactions( (reaction, user) => reaction.emoji.name === '🗑️' && user.id === author, {max:1,time:60000} ).then( reaction => {
  4489. if ( reaction.size ) {
  4490. this.deleteMsg();
  4491. }
  4492. } );
  4493. };
  4494. String.prototype.hasPrefix = function(prefix, flags = '') {
  4495. return new RegExp( '^' + prefix.replace( /\W/g, '\\$&' ) + '(?: |$)', flags ).test(this.replace( /\u200b/g, '' ).toLowerCase());
  4496. };
  4497. client.on( 'message', msg => {
  4498. if ( stop || msg.type !== 'DEFAULT' || msg.webhookID || msg.author.id === client.user.id ) return;
  4499. if ( !msg.content.hasPrefix(( msg.channel.type === 'text' && patreons[msg.guild.id] || process.env.prefix ), 'm') ) {
  4500. if ( msg.content === process.env.prefix + ' help' && ( msg.isAdmin() || msg.isOwner() ) ) {
  4501. if ( msg.channel.permissionsFor(client.user).has('SEND_MESSAGES') ) {
  4502. console.log( msg.guild.name + ': ' + msg.content );
  4503. db.get( 'SELECT lang FROM discord WHERE guild = ? AND (channel = ? OR channel IS NULL) ORDER BY channel DESC', [msg.guild.id, msg.channel.id], (dberror, row) => {
  4504. if ( dberror ) console.log( '- Error while getting the lang: ' + dberror );
  4505. msg.replyMsg( i18n[( row || defaultSettings ).lang].prefix.replaceSave( /%s/g, patreons[msg.guild.id] ), {}, true );
  4506. } );
  4507. }
  4508. }
  4509. if ( !( msg.content.includes( '[[' ) && msg.content.includes( ']]' ) ) && !( msg.content.includes( '{{' ) && msg.content.includes( '}}' ) ) ) return;
  4510. }
  4511. if ( !ready.allSites && !allSites.length ) getAllSites();
  4512. if ( msg.channel.type === 'text' ) {
  4513. var permissions = msg.channel.permissionsFor(client.user);
  4514. var missing = permissions.missing(['SEND_MESSAGES','ADD_REACTIONS','USE_EXTERNAL_EMOJIS','READ_MESSAGE_HISTORY']);
  4515. if ( missing.length ) {
  4516. if ( msg.isAdmin() || msg.isOwner() ) {
  4517. console.log( msg.guild.name + ': Missing permissions - ' + missing.join(', ') );
  4518. if ( !missing.includes( 'SEND_MESSAGES' ) ) {
  4519. db.get( 'SELECT lang FROM discord WHERE guild = ? AND (channel = ? OR channel IS NULL) ORDER BY channel DESC', [msg.guild.id, msg.channel.id], (dberror, row) => {
  4520. if ( dberror ) console.log( '- Error while getting the lang: ' + dberror );
  4521. if ( msg.content.hasPrefix(( patreons[msg.guild.id] || process.env.prefix ), 'm') ) {
  4522. msg.replyMsg( i18n[( row || defaultSettings ).lang].missingperm + ' `' + missing.join('`, `') + '`', {}, true );
  4523. }
  4524. } );
  4525. }
  4526. }
  4527. return;
  4528. }
  4529. db.get( 'SELECT wiki, lang, inline FROM discord WHERE guild = ? AND (channel = ? OR channel IS NULL) ORDER BY channel DESC', [msg.guild.id, msg.channel.id], (dberror, row) => {
  4530. if ( dberror ) {
  4531. console.log( '- Error while getting the wiki: ' + dberror );
  4532. if ( permissions.has('SEND_MESSAGES') ) {
  4533. msg.sendChannel( '⚠️ **Limited Functionality** ⚠️\nNo settings found, please contact the bot owner!\n' + process.env.invite, {}, true );
  4534. newMessage(msg);
  4535. }
  4536. return dberror;
  4537. }
  4538. if ( row ) newMessage(msg, row.wiki, i18n[row.lang], patreons[msg.guild.id], row.inline);
  4539. else {
  4540. msg.defaultSettings = true;
  4541. newMessage(msg);
  4542. }
  4543. } );
  4544. }
  4545. else newMessage(msg);
  4546. } );
  4547. function newMessage(msg, wiki = defaultSettings.wiki, lang = i18n[defaultSettings.lang], prefix = process.env.prefix, noInline = null, content) {
  4548. msg.noInline = noInline;
  4549. var cont = ( content || msg.content );
  4550. var cleanCont = ( content || msg.cleanContent );
  4551. var author = msg.author;
  4552. var channel = msg.channel;
  4553. var invoke = ( cont.split(' ')[1] ? cont.split(' ')[1].split('\n')[0].toLowerCase() : '' );
  4554. var aliasInvoke = ( lang.aliase[invoke] || invoke );
  4555. var ownercmd = ( msg.isOwner() && aliasInvoke in ownercmdmap );
  4556. if ( cont.hasPrefix(prefix) && ( ( msg.isAdmin() && aliasInvoke in multilinecmdmap ) || ownercmd ) ) {
  4557. if ( ownercmd || channel.permissionsFor(client.user).has('MANAGE_MESSAGES') ) {
  4558. var args = cont.split(' ').slice(2);
  4559. if ( cont.split(' ')[1].split('\n')[1] ) args.unshift( '', cont.split(' ')[1].split('\n')[1] );
  4560. if ( !( ownercmd || aliasInvoke in pausecmdmap ) && pause[msg.guild.id] ) console.log( msg.guild.name + ': Paused' );
  4561. else console.log( ( channel.type === 'text' ? msg.guild.name : '@' + author.username ) + ': ' + cont );
  4562. if ( ownercmd ) ownercmdmap[aliasInvoke](lang, msg, args, cont, wiki);
  4563. else if ( !pause[msg.guild.id] || aliasInvoke in pausecmdmap ) multilinecmdmap[aliasInvoke](lang, msg, args, cont, wiki);
  4564. } else {
  4565. console.log( msg.guild.name + ': Missing permissions - MANAGE_MESSAGES' );
  4566. msg.replyMsg( lang.missingperm + ' `MANAGE_MESSAGES`' );
  4567. }
  4568. } else {
  4569. var count = 0;
  4570. var maxcount = ( channel.type === 'text' && msg.guild.id in patreons ? 15 : 10 );
  4571. cleanCont.replace( /\u200b/g, '' ).split('\n').forEach( line => {
  4572. if ( line.hasPrefix(prefix) && count < maxcount ) {
  4573. count++;
  4574. invoke = ( line.split(' ')[1] ? line.split(' ')[1].toLowerCase() : '' );
  4575. var args = line.split(' ').slice(2);
  4576. aliasInvoke = ( lang.aliase[invoke] || invoke );
  4577. ownercmd = ( msg.isOwner() && aliasInvoke in ownercmdmap );
  4578. if ( channel.type === 'text' && pause[msg.guild.id] && !( ( msg.isAdmin() && aliasInvoke in pausecmdmap ) || ownercmd ) ) console.log( msg.guild.name + ': Paused' );
  4579. else console.log( ( channel.type === 'text' ? msg.guild.name : '@' + author.username ) + ': ' + line );
  4580. if ( ownercmd ) ownercmdmap[aliasInvoke](lang, msg, args, line, wiki);
  4581. else if ( channel.type !== 'text' || !pause[msg.guild.id] || ( msg.isAdmin() && aliasInvoke in pausecmdmap ) ) {
  4582. if ( aliasInvoke in cmdmap ) cmdmap[aliasInvoke](lang, msg, args, line, wiki);
  4583. else if ( /^![a-z\d-]{1,50}$/.test(invoke) ) {
  4584. cmd_link(lang, msg, args.join(' '), 'https://' + invoke.substring(1) + '.gamepedia.com/', ' ' + invoke + ' ');
  4585. }
  4586. else if ( /^\?(?:[a-z-]{1,8}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
  4587. if ( invoke.includes( '.' ) ) wiki = 'https://' + invoke.split('.')[1] + '.fandom.com/' + invoke.substring(1).split('.')[0] + '/';
  4588. else wiki = 'https://' + invoke.substring(1) + '.fandom.com/';
  4589. cmd_link(lang, msg, args.join(' '), wiki, ' ' + invoke + ' ');
  4590. }
  4591. else if ( /^\?\?(?:[a-z-]{1,8}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
  4592. if ( invoke.includes( '.' ) ) wiki = 'https://' + invoke.split('.')[1] + '.wikia.org/' + invoke.substring(2).split('.')[0] + '/';
  4593. else wiki = 'https://' + invoke.substring(2) + '.wikia.org/';
  4594. cmd_link(lang, msg, args.join(' '), wiki, ' ' + invoke + ' ');
  4595. }
  4596. else cmd_link(lang, msg, line.split(' ').slice(1).join(' '), wiki);
  4597. }
  4598. } else if ( line.hasPrefix(prefix) && count === maxcount ) {
  4599. count++;
  4600. console.log( '- Message contains too many commands!' );
  4601. msg.reactEmoji('⚠️');
  4602. msg.sendChannelError( lang.limit.replaceSave( '%s', author ) );
  4603. }
  4604. } );
  4605. if ( ( channel.type !== 'text' || !pause[msg.guild.id] ) && !noInline && ( cont.includes( '[[' ) || cont.includes( '{{' ) ) ) {
  4606. var links = [];
  4607. var embeds = [];
  4608. var linkcount = 0;
  4609. var linkmaxcount = maxcount + 5;
  4610. msg.cleanContent.replace( /\u200b/g, '' ).replace( /(?<!\\)```.+?```/gs, '<codeblock>' ).replace( /(?<!\\)`.+?`/gs, '<code>' ).split('\n').forEach( line => {
  4611. if ( line.hasPrefix(prefix) || !( line.includes( '[[' ) || line.includes( '{{' ) ) ) return;
  4612. if ( line.includes( '[[' ) && line.includes( ']]' ) && linkcount <= linkmaxcount ) {
  4613. let regex = new RegExp( '(?<!\\\\)(|\\|\\|)\\[\\[([^' + "<>\\[\\]\\|{}\\x01-\\x1F\\x7F" + ']+)(?<!\\\\)\\]\\]\\1', 'g' );
  4614. let entry = null;
  4615. while ( ( entry = regex.exec(line) ) !== null ) {
  4616. if ( linkcount < linkmaxcount ) {
  4617. linkcount++;
  4618. console.log( ( channel.type === 'text' ? msg.guild.name : '@' + author.username ) + ': ' + entry[0] );
  4619. let title = entry[2].split('#')[0];
  4620. let section = ( entry[2].includes( '#' ) ? entry[2].split('#').slice(1).join('#') : '' )
  4621. links.push({title,section,spoiler:entry[1]});
  4622. }
  4623. else if ( linkcount === linkmaxcount ) {
  4624. linkcount++;
  4625. console.log( '- Message contains too many links!' );
  4626. msg.reactEmoji('⚠️');
  4627. break;
  4628. }
  4629. }
  4630. }
  4631. if ( line.includes( '{{' ) && line.includes( '}}' ) && count <= maxcount ) {
  4632. let regex = new RegExp( '(?<!\\\\)(|\\|\\|)(?<!\\{)\\{\\{([^' + "<>\\[\\]\\|{}\\x01-\\x1F\\x7F" + ']+)(?<!\\\\)\\}\\}\\1', 'g' );
  4633. let entry = null;
  4634. while ( ( entry = regex.exec(line) ) !== null ) {
  4635. if ( count < maxcount ) {
  4636. count++;
  4637. console.log( ( channel.type === 'text' ? msg.guild.name : '@' + author.username ) + ': ' + entry[0] );
  4638. let title = entry[2].split('#')[0];
  4639. let section = ( entry[2].includes( '#' ) ? entry[2].split('#').slice(1).join('#') : '' )
  4640. embeds.push({title,section,spoiler:entry[1]});
  4641. }
  4642. else if ( count === maxcount ) {
  4643. count++;
  4644. console.log( '- Message contains too many links!' );
  4645. msg.reactEmoji('⚠️');
  4646. break;
  4647. }
  4648. }
  4649. }
  4650. } );
  4651. if ( links.length ) request( {
  4652. uri: wiki + 'api.php?action=query&iwurl=true&titles=' + encodeURIComponent( links.map( link => link.title ).join('|') ) + '&format=json',
  4653. json: true
  4654. }, function( error, response, body ) {
  4655. if ( error || !response || response.statusCode !== 200 || !body || !body.query ) {
  4656. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  4657. console.log( '- This wiki doesn\'t exist!' );
  4658. msg.reactEmoji('nowiki');
  4659. return;
  4660. }
  4661. console.log( '- ' + ( response && response.statusCode ) + ': Error while following the links: ' + ( error || body && body.error && body.error.info ) );
  4662. return;
  4663. }
  4664. if ( body.query.normalized ) {
  4665. body.query.normalized.forEach( title => links.filter( link => link.title === title.from ).forEach( link => link.title = title.to ) );
  4666. }
  4667. if ( body.query.interwiki ) {
  4668. body.query.interwiki.forEach( interwiki => links.filter( link => link.title === interwiki.title ).forEach( link => {
  4669. link.url = interwiki.url + ( link.section ? '#' + link.section.toSection() : '' );
  4670. } ) );
  4671. }
  4672. if ( body.query.pages ) {
  4673. var querypages = Object.values(body.query.pages);
  4674. querypages.filter( page => page.invalid !== undefined ).forEach( page => links.filter( link => link.title === page.title ).forEach( link => {
  4675. links.splice(links.indexOf(link), 1);
  4676. } ) );
  4677. querypages.filter( page => page.missing !== undefined && page.known === undefined ).forEach( page => links.filter( link => link.title === page.title ).forEach( link => {
  4678. if ( ( page.ns === 2 || page.ns === 202 ) && !page.title.includes( '/' ) ) return;
  4679. link.url = wiki.toLink() + link.title.toTitle() + '?action=edit&redlink=1';
  4680. } ) );
  4681. }
  4682. if ( links.length ) msg.sendChannel( links.map( link => link.spoiler + '<' + ( link.url || wiki.toLink() + link.title.toTitle() + ( link.section ? '#' + link.section.toSection() : '' ) ) + '>' + link.spoiler ).join('\n'), {split:true} );
  4683. } );
  4684. if ( embeds.length ) request( {
  4685. uri: wiki + 'api.php?action=query' + ( wiki.isFandom() ? '' : '&meta=siteinfo&siprop=variables' ) + '&titles=' + encodeURIComponent( embeds.map( embed => embed.title + '|Template:' + embed.title ).join('|') ) + '&format=json',
  4686. json: true
  4687. }, function( error, response, body ) {
  4688. if ( error || !response || response.statusCode !== 200 || !body || !body.query ) {
  4689. if ( response && ( response.request && response.request.uri && wiki.noWiki(response.request.uri.href) || response.statusCode === 410 ) ) {
  4690. console.log( '- This wiki doesn\'t exist!' );
  4691. msg.reactEmoji('nowiki');
  4692. return;
  4693. }
  4694. console.log( '- ' + ( response && response.statusCode ) + ': Error while following the links: ' + ( error || body && body.error && body.error.info ) );
  4695. return;
  4696. }
  4697. if ( body.query.normalized ) {
  4698. body.query.normalized.forEach( title => embeds.filter( embed => embed.title === title.from ).forEach( embed => embed.title = title.to ) );
  4699. }
  4700. if ( body.query.pages ) {
  4701. var querypages = Object.values(body.query.pages);
  4702. querypages.filter( page => page.invalid !== undefined ).forEach( page => embeds.filter( embed => embed.title === page.title ).forEach( embed => {
  4703. embeds.splice(embeds.indexOf(embed), 1);
  4704. } ) );
  4705. var missing = [];
  4706. querypages.filter( page => page.missing !== undefined && page.known === undefined ).forEach( page => embeds.filter( embed => embed.title === page.title ).forEach( embed => {
  4707. if ( ( page.ns === 2 || page.ns === 202 ) && !page.title.includes( '/' ) ) return;
  4708. embeds.splice(embeds.indexOf(embed), 1);
  4709. if ( page.ns === 0 && !embed.section ) {
  4710. var template = querypages.find( template => template.ns === 10 && template.title.split(':').slice(1).join(':') === embed.title );
  4711. if ( template && template.missing === undefined ) embed.template = template.title.toTitle();
  4712. }
  4713. if ( embed.template || !body.query.variables || !body.query.variables.some( variable => variable.toUpperCase() === embed.title ) ) missing.push(embed);
  4714. } ) );
  4715. if ( missing.length ) {
  4716. msg.sendChannel( missing.map( embed => embed.spoiler + '<' + wiki.toLink() + ( embed.template || embed.title.toTitle() + '?action=edit&redlink=1' ) + '>' + embed.spoiler ).join('\n'), {split:true} );
  4717. }
  4718. }
  4719. if ( embeds.length ) {
  4720. if ( wiki.isFandom() ) embeds.forEach( embed => msg.reactEmoji('⏳').then( reaction => {
  4721. fandom_check_wiki(lang, msg, embed.title, wiki, ' ', reaction, embed.spoiler, '', embed.section);
  4722. } ) );
  4723. else embeds.forEach( embed => msg.reactEmoji('⏳').then( reaction => {
  4724. gamepedia_check_wiki(lang, msg, embed.title, wiki, ' ', reaction, embed.spoiler, '', embed.section);
  4725. } ) );
  4726. }
  4727. } );
  4728. }
  4729. }
  4730. }
  4731. client.on( 'voiceStateUpdate', (oldm, newm) => {
  4732. if ( stop || !( oldm.guild.id in voice ) || !oldm.guild.me.permissions.has('MANAGE_ROLES') || oldm.voiceChannelID === newm.voiceChannelID ) return;
  4733. if ( !ready.allSites && !allSites.length ) getAllSites();
  4734. var lang = i18n[voice[oldm.guild.id]].voice;
  4735. if ( oldm.voiceChannel ) {
  4736. var oldrole = oldm.roles.find( role => role.name === lang.channel + ' – ' + oldm.voiceChannel.name );
  4737. if ( oldrole && oldrole.comparePositionTo(oldm.guild.me.highestRole) < 0 ) {
  4738. console.log( oldm.guild.name + ': ' + oldm.displayName + ' left the voice channel "' + oldm.voiceChannel.name + '".' );
  4739. oldm.removeRole( oldrole, lang.left.replaceSave( '%1$s', oldm.displayName ).replaceSave( '%2$s', oldm.voiceChannel.name ) ).catch(log_error);
  4740. }
  4741. }
  4742. if ( newm.voiceChannel ) {
  4743. var newrole = newm.guild.roles.find( role => role.name === lang.channel + ' – ' + newm.voiceChannel.name );
  4744. if ( newrole && newrole.comparePositionTo(newm.guild.me.highestRole) < 0 ) {
  4745. console.log( newm.guild.name + ': ' + newm.displayName + ' joined the voice channel "' + newm.voiceChannel.name + '".' );
  4746. newm.addRole( newrole, lang.join.replaceSave( '%1$s', newm.displayName ).replaceSave( '%2$s', newm.voiceChannel.name ) ).catch(log_error);
  4747. }
  4748. }
  4749. } );
  4750. client.on( 'guildCreate', guild => {
  4751. console.log( '- I\'ve been added to a server.' );
  4752. } );
  4753. client.on( 'guildDelete', guild => {
  4754. if ( !guild.available ) {
  4755. console.log( '- ' + guild.name + ': This server isn\'t responding.' );
  4756. return;
  4757. }
  4758. console.log( '- I\'ve been removed from a server.' );
  4759. db.run( 'DELETE FROM discord WHERE guild = ?', [guild.id], function (dberror) {
  4760. if ( dberror ) {
  4761. console.log( '- Error while removing the settings: ' + dberror );
  4762. return dberror;
  4763. }
  4764. if ( guild.id in patreons ) delete patreons[guild.id];
  4765. if ( guild.id in voice ) delete voice[guild.id];
  4766. console.log( '- Settings successfully removed.' );
  4767. } );
  4768. } );
  4769. function removePatreons(guild, msg) {
  4770. try {
  4771. if ( !guild ) return 'removePatreons(guild, msg) – No guild provided!';
  4772. db.get( 'SELECT lang, inline FROM discord WHERE guild = ? AND channel IS NULL', [guild], (dberror, row) => {
  4773. try {
  4774. if ( dberror ) {
  4775. console.log( '- Error while getting the guild: ' + dberror );
  4776. if ( msg ) msg.replyMsg( 'I got an error while searching for the guild!', {}, true );
  4777. return dberror;
  4778. }
  4779. if ( !row ) {
  4780. if ( msg ) msg.replyMsg( 'that guild doesn\'t exist!', {}, true );
  4781. return;
  4782. }
  4783. db.run( 'UPDATE discord SET lang = ?, inline = ?, prefix = ?, patreon = NULL WHERE guild = ?', [row.lang, row.inline, process.env.prefix, guild], function (error) {
  4784. try {
  4785. if ( error ) {
  4786. console.log( '- Error while updating the guild: ' + error );
  4787. if ( msg ) msg.replyMsg( 'I got an error while updating the guild!', {}, true );
  4788. return error;
  4789. }
  4790. console.log( '- Guild successfully updated.' );
  4791. delete patreons[guild];
  4792. if ( msg ) msg.replyMsg( 'the patreon features are now disabled on that guild.', {}, true );
  4793. }
  4794. catch ( tryerror ) {
  4795. console.log( '- Error while removing the patreon features: ' + tryerror );
  4796. }
  4797. } );
  4798. }
  4799. catch ( tryerror ) {
  4800. console.log( '- Error while removing the patreon features: ' + tryerror );
  4801. }
  4802. } );
  4803. }
  4804. catch ( tryerror ) {
  4805. console.log( '- Error while removing the patreon features: ' + tryerror );
  4806. return 'removePatreons(guild, msg) – Error while removing the patreon features: ' + tryerror;
  4807. }
  4808. }
  4809. function removeSettings() {
  4810. var guilds = [];
  4811. var channels = [];
  4812. db.each( 'SELECT guild, channel FROM discord', [], (dberror, row) => {
  4813. if ( dberror ) {
  4814. console.log( '- Error while getting the setting: ' + dberror );
  4815. return dberror;
  4816. }
  4817. if ( !row.channel && !client.guilds.has(row.guild) ) {
  4818. if ( row.guild in patreons ) delete patreons[row.guild];
  4819. if ( row.guild in voice ) delete voice[row.guild];
  4820. return guilds.push(row.guild);
  4821. }
  4822. if ( row.channel && client.guilds.has(row.guild) && !client.channels.filter( channel => channel.type === 'text' ).has(row.channel) ) return channels.push(row.channel);
  4823. }, (error) => {
  4824. if ( error ) {
  4825. console.log( '- Error while getting the settings: ' + error );
  4826. return error;
  4827. }
  4828. if ( guilds.length ) db.run( 'DELETE FROM discord WHERE guild IN (' + guilds.map( guild => '?' ).join(', ') + ')', guilds, function (dberror) {
  4829. if ( dberror ) {
  4830. console.log( '- Error while removing the guilds: ' + dberror );
  4831. return dberror;
  4832. }
  4833. console.log( '- Guilds successfully removed.' );
  4834. } );
  4835. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( channel => '?' ).join(', ') + ')', channels, function (dberror) {
  4836. if ( dberror ) {
  4837. console.log( '- Error while removing the channels: ' + dberror );
  4838. return dberror;
  4839. }
  4840. console.log( '- Channels successfully removed.' );
  4841. } );
  4842. if ( !guilds.length && !channels.length ) console.log( '- Settings successfully removed.' );
  4843. } );
  4844. }
  4845. client.login(process.env.token).catch( error => {
  4846. log_error(error, true, 'LOGIN-');
  4847. client.login(process.env.token).catch( error => {
  4848. log_error(error, true, 'LOGIN-');
  4849. client.login(process.env.token).catch( error => {
  4850. log_error(error, true, 'LOGIN-');
  4851. process.exit(1);
  4852. } );
  4853. } );
  4854. } );
  4855. client.on( 'error', error => log_error(error, true) );
  4856. client.on( 'warn', warning => log_warn(warning, false) );
  4857. if ( isDebug ) client.on( 'debug', debug => {
  4858. if ( isDebug ) console.log( '- Debug: ' + debug );
  4859. } );
  4860. function log_error(error, isBig = false, type = '') {
  4861. var time = new Date(Date.now()).toLocaleTimeString('de-DE', { timeZone: 'Europe/Berlin' });
  4862. if ( isDebug ) {
  4863. console.error( '--- ' + type + 'ERROR START ' + time + ' ---\n', error, '\n--- ' + type + 'ERROR END ' + time + ' ---' );
  4864. } else {
  4865. if ( isBig ) console.log( '--- ' + type + 'ERROR: ' + time + ' ---\n-', error );
  4866. else console.log( '- ' + error.name + ': ' + error.message );
  4867. }
  4868. }
  4869. function log_warn(warning, api = true) {
  4870. if ( isDebug ) {
  4871. console.warn( '--- Warning start ---\n' + util.inspect( warning ) + '\n--- Warning end ---' );
  4872. } else {
  4873. if ( api ) console.warn( '- Warning: ' + Object.keys(warning).join(', ') );
  4874. else console.warn( '--- Warning ---\n' + util.inspect( warning ) );
  4875. }
  4876. }
  4877. async function graceful(code = 0) {
  4878. stop = true;
  4879. console.log( '- SIGTERM: Preparing to close...' );
  4880. setTimeout( async () => {
  4881. console.log( '- SIGTERM: Destroying client...' );
  4882. await client.destroy();
  4883. await db.close( dberror => {
  4884. if ( dberror ) {
  4885. console.log( '- SIGTERM: Error while closing the database connection: ' + dberror );
  4886. return dberror;
  4887. }
  4888. console.log( '- SIGTERM: Closed the database connection.' );
  4889. } );
  4890. setTimeout( async () => {
  4891. console.log( '- SIGTERM: Closing takes too long, terminating!' );
  4892. process.exit(code);
  4893. }, 2000 ).unref();
  4894. }, 2000 ).unref();
  4895. }
  4896. process.once( 'SIGINT', graceful );
  4897. process.once( 'SIGTERM', graceful );