guilds.js 12 KB

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