guilds.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. const cheerio = require('cheerio');
  2. const {defaultPermissions} = require('../util/default.json');
  3. const Lang = require('./i18n.js');
  4. const allLangs = Lang.allLangs().names;
  5. const {oauth, enabledOAuth2, settingsData, addWidgets, createNotice} = require('./util.js');
  6. const forms = {
  7. user: require('./user.js').get,
  8. settings: require('./settings.js').get,
  9. verification: require('./verification.js').get,
  10. rcscript: require('./rcscript.js').get,
  11. slash: require('./slash.js').get
  12. };
  13. const file = require('fs').readFileSync('./dashboard/index.html');
  14. /**
  15. * Let a user view settings
  16. * @param {import('http').ServerResponse} res - The server response
  17. * @param {import('./i18n.js')} dashboardLang - The user language.
  18. * @param {String} theme - The display theme
  19. * @param {import('./util.js').UserSession} userSession - The user session
  20. * @param {URL} reqURL - The used url
  21. * @param {String} [action] - The action the user made
  22. * @param {String[]} [actionArgs] - The arguments for the action
  23. */
  24. function dashboard_guilds(res, dashboardLang, theme, userSession, reqURL, action, actionArgs) {
  25. reqURL.pathname = reqURL.pathname.replace( /^(\/(?:user|guild\/\d+(?:\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?)?)?)(?:\/.*)?$/, '$1' );
  26. var args = reqURL.pathname.split('/');
  27. var settings = settingsData.get(userSession.user_id);
  28. if ( reqURL.searchParams.get('owner') && process.env.owner.split('|').includes(userSession.user_id) ) {
  29. args[0] = 'owner';
  30. }
  31. dashboardLang = new Lang(...dashboardLang.fromCookie, settings.user.locale, dashboardLang.lang);
  32. res.setHeader('Content-Language', [dashboardLang.lang]);
  33. var $ = cheerio.load(file);
  34. $('html').attr('lang', dashboardLang.lang);
  35. if ( theme === 'light' ) $('html').addClass('theme-light');
  36. $('<script>').text(`
  37. const selectLanguage = '${dashboardLang.get('general.language').replace( /'/g, '\\$&' )}';
  38. const allLangs = ${JSON.stringify(allLangs)};
  39. `).insertBefore('script#langjs');
  40. $('head title').text(dashboardLang.get('general.title'));
  41. $('.channel#settings div').text(dashboardLang.get('general.settings'));
  42. $('.channel#settings').attr('title', dashboardLang.get('general.settings'));
  43. $('.channel#verification div').text(dashboardLang.get('general.verification'));
  44. $('.channel#verification').attr('title', dashboardLang.get('general.verification'));
  45. $('.channel#rcscript div').text(dashboardLang.get('general.rcscript'));
  46. $('.channel#rcscript').attr('title', dashboardLang.get('general.rcscript'));
  47. $('.channel#slash div').text(dashboardLang.get('general.slash'));
  48. $('.channel#slash').attr('title', dashboardLang.get('general.slash'));
  49. $('.guild#invite a').attr('alt', dashboardLang.get('general.invite'));
  50. $('.guild#refresh a').attr('alt', dashboardLang.get('general.refresh'));
  51. $('.guild#theme-dark a').attr('alt', dashboardLang.get('general.theme-dark'));
  52. $('.guild#theme-light a').attr('alt', dashboardLang.get('general.theme-light'));
  53. $('#selector span').text(dashboardLang.get('general.selector'));
  54. $('#support span').text(dashboardLang.get('general.support'));
  55. $('#logout').attr('alt', dashboardLang.get('general.logout'));
  56. if ( process.env.READONLY ) createNotice($, 'readonly', dashboardLang);
  57. if ( action ) {
  58. if ( action === 'oauthother' && !actionArgs ) actionArgs = [
  59. oauth.generateAuthUrl( {
  60. scope: ['identify', 'guilds'],
  61. prompt: 'consent', state: userSession.state
  62. } )
  63. ];
  64. createNotice($, action, dashboardLang, actionArgs);
  65. }
  66. $('head').append(
  67. $('<script>').text(`history.replaceState(null, null, '${reqURL.pathname}');`)
  68. );
  69. $('#logout img').attr('src', settings.user.avatar);
  70. $('#logout span').text(`${settings.user.username} #${settings.user.discriminator}`);
  71. $('.guild#invite a').attr('href', oauth.generateAuthUrl( {
  72. scope: ['identify', 'guilds', 'bot', 'applications.commands'],
  73. permissions: defaultPermissions, state: userSession.state
  74. } ));
  75. $('.guild#refresh a').attr('href', '/refresh?return=' + reqURL.pathname);
  76. if ( settings.guilds.isMember.size ) {
  77. $('<div class="guild">').append(
  78. $('<div class="separator">')
  79. ).insertBefore('.guild#last-separator');
  80. settings.guilds.isMember.forEach( guild => {
  81. $('<div class="guild">').attr('id', guild.id).append(
  82. $('<div class="bar">'),
  83. $('<a>').attr('href', `/guild/${guild.id}/settings`).attr('alt', guild.name).append(
  84. ( guild.icon ?
  85. $('<img class="avatar">').attr('src', `${guild.icon}?size=64`).attr('alt', guild.name)
  86. : $('<div class="avatar noicon">').text(guild.acronym) )
  87. )
  88. ).insertBefore('.guild#last-separator');
  89. } );
  90. }
  91. if ( settings.guilds.notMember.size ) {
  92. $('<div class="guild">').append(
  93. $('<div class="separator">')
  94. ).insertBefore('.guild#last-separator');
  95. settings.guilds.notMember.forEach( guild => {
  96. $('<div class="guild">').attr('id', guild.id).append(
  97. $('<div class="bar">'),
  98. $('<a>').attr('href', `/guild/${guild.id}`).attr('alt', guild.name).append(
  99. ( guild.icon ?
  100. $('<img class="avatar">').attr('src', `${guild.icon}?size=64`).attr('alt', guild.name)
  101. : $('<div class="avatar noicon">').text(guild.acronym) )
  102. )
  103. ).insertBefore('.guild#last-separator');
  104. } );
  105. }
  106. if ( args[1] === 'user' && enabledOAuth2.length ) {
  107. $('head title').text(`${settings.user.username} #${settings.user.discriminator} – ` + $('head title').text());
  108. $('#channellist').empty();
  109. $('#channellist').append(
  110. $('<a class="channel channel-header">').attr('href', '/').append(
  111. $('<img>').attr('src', '/src/settings.svg'),
  112. $('<div>').text(dashboardLang.get('selector.title'))
  113. ).attr('title', dashboardLang.get('selector.title')),
  114. $('<a class="channel channel-header selected">').attr('href', '/user').append(
  115. $('<img>').attr('src', '/src/settings.svg'),
  116. $('<div>').text(dashboardLang.get('selector.user'))
  117. ).attr('title', dashboardLang.get('selector.user')),
  118. ...enabledOAuth2.map( oauthSite => {
  119. return $('<a class="channel">').attr('href', '#oauth-' + oauthSite.id).append(
  120. $('<img>').attr('src', '/src/channel.svg'),
  121. $('<div>').text(oauthSite.name)
  122. ).attr('title', oauthSite.name);
  123. } )
  124. )
  125. return forms.user(res, $, settings.user, dashboardLang);
  126. }
  127. let id = args[2];
  128. if ( id ) $(`.guild#${id}`).addClass('selected');
  129. if ( settings.guilds.isMember.has(id) ) {
  130. let guild = settings.guilds.isMember.get(id);
  131. $('head title').text(`${guild.name} – ` + $('head title').text());
  132. $('<script>').text(`
  133. const isPatreon = ${guild.patreon};
  134. const i18n = ${JSON.stringify(dashboardLang.getWithFallback('indexjs'))};
  135. `).insertBefore('script#indexjs');
  136. $('.channel#settings').attr('href', `/guild/${guild.id}/settings`);
  137. $('.channel#verification').attr('href', `/guild/${guild.id}/verification`);
  138. $('.channel#rcscript').attr('href', `/guild/${guild.id}/rcscript`);
  139. $('.channel#slash').attr('href', `/guild/${guild.id}/slash`);
  140. if ( args[3] === 'settings' ) return forms.settings(res, $, guild, args, dashboardLang);
  141. if ( args[3] === 'verification' ) return forms.verification(res, $, guild, args, dashboardLang);
  142. if ( args[3] === 'rcscript' ) return forms.rcscript(res, $, guild, args, dashboardLang);
  143. if ( args[3] === 'slash' ) return forms.slash(res, $, guild, args, dashboardLang);
  144. return forms.settings(res, $, guild, args, dashboardLang);
  145. }
  146. else if ( settings.guilds.notMember.has(id) ) {
  147. let guild = settings.guilds.notMember.get(id);
  148. $('head title').text(`${guild.name} – ` + $('head title').text());
  149. res.setHeader('Set-Cookie', [`guild="${guild.id}/settings"; SameSite=Lax; Path=/`]);
  150. let url = oauth.generateAuthUrl( {
  151. scope: ['identify', 'guilds', 'bot', 'applications.commands'],
  152. permissions: defaultPermissions, guildId: guild.id,
  153. disableGuildSelect: true, state: userSession.state
  154. } );
  155. $('#channellist').empty();
  156. $('<a class="channel channel-header">').attr('href', url).append(
  157. $('<img>').attr('src', '/src/settings.svg'),
  158. $('<div>').text(dashboardLang.get('general.invite'))
  159. ).attr('title', dashboardLang.get('general.invite')).appendTo('#channellist');
  160. $('#text .description').append(
  161. $('<p>').html(dashboardLang.get('selector.invite', true, $('<code>').text(guild.name), $('<a>').attr('href', url))),
  162. $('<a id="login-button">').attr('href', url).text(dashboardLang.get('general.invite')).prepend(
  163. $('<img alt="Discord">').attr('src', 'https://discord.com/assets/f8389ca1a741a115313bede9ac02e2c0.svg')
  164. )
  165. );
  166. addWidgets($, dashboardLang);
  167. }
  168. else if ( args[0] === 'owner' ) {
  169. let guild = {
  170. id, name: 'OWNER ACCESS',
  171. acronym: '', userPermissions: 1 << 3,
  172. patreon: true, botPermissions: 1 << 3,
  173. channels: [], roles: []
  174. };
  175. $('head title').text(`${guild.name} – ` + $('head title').text());
  176. $('<script>').text(`
  177. const isPatreon = ${guild.patreon};
  178. const i18n = ${JSON.stringify(dashboardLang.getWithFallback('indexjs'))};
  179. `).insertBefore('script#indexjs');
  180. $('.channel#settings').attr('href', `/guild/${guild.id}/settings?owner=true`);
  181. $('.channel#verification').attr('href', `/guild/${guild.id}/verification?owner=true`);
  182. $('.channel#rcscript').attr('href', `/guild/${guild.id}/rcscript?owner=true`);
  183. $('.channel#slash').attr('href', `/guild/${guild.id}/slash?owner=true`);
  184. if ( args[3] === 'settings' ) return forms.settings(res, $, guild, args, dashboardLang);
  185. if ( args[3] === 'verification' ) return forms.verification(res, $, guild, args, dashboardLang);
  186. if ( args[3] === 'rcscript' ) return forms.rcscript(res, $, guild, args, dashboardLang);
  187. if ( args[3] === 'slash' ) return forms.slash(res, $, guild, args, dashboardLang);
  188. return forms.settings(res, $, guild, args, dashboardLang);
  189. }
  190. else {
  191. $('head title').text(dashboardLang.get('selector.title') + ' – ' + $('head title').text());
  192. $('#channellist').empty();
  193. $('<a class="channel channel-header selected">').attr('href', '/').append(
  194. $('<img>').attr('src', '/src/settings.svg'),
  195. $('<div>').text(dashboardLang.get('selector.title'))
  196. ).attr('title', dashboardLang.get('selector.title')).appendTo('#channellist');
  197. $('<p>').html(dashboardLang.get('selector.desc', true, $('<code>'))).appendTo('#text .description');
  198. if ( settings.guilds.isMember.size ) {
  199. $('<h2 id="with-wikibot">').text(dashboardLang.get('selector.with')).appendTo('#text');
  200. $('<a class="channel">').attr('href', '#with-wikibot').append(
  201. $('<img>').attr('src', '/src/channel.svg'),
  202. $('<div>').text(dashboardLang.get('selector.with'))
  203. ).attr('title', dashboardLang.get('selector.with')).appendTo('#channellist');
  204. $('<div class="server-selector" id="isMember">').appendTo('#text');
  205. settings.guilds.isMember.forEach( guild => {
  206. $('<a class="server">').attr('href', `/guild/${guild.id}/settings`).append(
  207. ( guild.icon ?
  208. $('<img class="avatar">').attr('src', `${guild.icon}?size=256`).attr('alt', guild.name)
  209. : $('<div class="avatar noicon">').text(guild.acronym) ),
  210. $('<div class="server-name description">').text(guild.name)
  211. ).appendTo('.server-selector#isMember');
  212. } );
  213. }
  214. if ( settings.guilds.notMember.size ) {
  215. $('<h2 id="without-wikibot">').text(dashboardLang.get('selector.without')).appendTo('#text');
  216. $('<a class="channel">').attr('href', '#without-wikibot').append(
  217. $('<img>').attr('src', '/src/channel.svg'),
  218. $('<div>').text(dashboardLang.get('selector.without'))
  219. ).attr('title', dashboardLang.get('selector.without')).appendTo('#channellist');
  220. $('<div class="server-selector" id="notMember">').appendTo('#text');
  221. settings.guilds.notMember.forEach( guild => {
  222. $('<a class="server">').attr('href', `/guild/${guild.id}`).append(
  223. ( guild.icon ?
  224. $('<img class="avatar">').attr('src', `${guild.icon}?size=256`).attr('alt', guild.name)
  225. : $('<div class="avatar noicon">').text(guild.acronym) ),
  226. $('<div class="server-name description">').text(guild.name)
  227. ).appendTo('.server-selector#notMember');
  228. } );
  229. }
  230. if ( !settings.guilds.count ) {
  231. let url = oauth.generateAuthUrl( {
  232. scope: ['identify', 'guilds'],
  233. prompt: 'consent', state: userSession.state
  234. } );
  235. $('<a class="channel">').attr('href', url).append(
  236. $('<img>').attr('src', '/src/channel.svg'),
  237. $('<div>').text(dashboardLang.get('selector.switch'))
  238. ).attr('title', dashboardLang.get('selector.switch')).appendTo('#channellist');
  239. $('#text .description').append(
  240. $('<p>').html(dashboardLang.get('selector.none', true, $('<code>'))),
  241. $('<a id="login-button">').attr('href', url).text(dashboardLang.get('selector.switch')).prepend(
  242. $('<img alt="Discord">').attr('src', 'https://discord.com/assets/f8389ca1a741a115313bede9ac02e2c0.svg')
  243. )
  244. );
  245. }
  246. if ( enabledOAuth2.length ) $('<a class="channel channel-header">').attr('href', '/user').append(
  247. $('<img>').attr('src', '/src/settings.svg'),
  248. $('<div>').text(dashboardLang.get('selector.user'))
  249. ).attr('title', dashboardLang.get('selector.user')).appendTo('#channellist');
  250. addWidgets($, dashboardLang);
  251. }
  252. let body = $.html();
  253. res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
  254. res.write( body );
  255. return res.end();
  256. }
  257. module.exports = dashboard_guilds;