mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-09-10 10:15:37 -05:00
Remove User#authAtLeast and add more sysop bypasses (#7046)
This commit is contained in:
@@ -288,12 +288,12 @@ exports.restrictLinks = false;
|
||||
exports.chatmodchat = false;
|
||||
/**
|
||||
* battle modchat - default minimum group for speaking in battles; changeable with /modchat
|
||||
* @type {false | string}
|
||||
* @type {false | AuthLevel}
|
||||
*/
|
||||
exports.battlemodchat = false;
|
||||
/**
|
||||
* pm modchat - minimum group for PMing other users, challenging other users
|
||||
* @type {false | string}
|
||||
* PM modchat - minimum group for sending private messages or challenges to other users
|
||||
* @type {false | AuthLevel}
|
||||
*/
|
||||
exports.pmmodchat = false;
|
||||
/**
|
||||
|
||||
@@ -572,8 +572,8 @@ export const commands: ChatCommands = {
|
||||
if (user.settings.blockPMs === (target || true)) {
|
||||
return this.errorReply(this.tr("You are already blocking private messages! To unblock, use /unblockpms"));
|
||||
}
|
||||
if (target in Config.groups) {
|
||||
user.settings.blockPMs = target as GroupSymbol;
|
||||
if (Users.Auth.isAuthLevel(target)) {
|
||||
user.settings.blockPMs = target;
|
||||
this.sendReply(this.tr `You are now blocking private messages, except from staff and ${target}.`);
|
||||
} else if (target === 'autoconfirmed' || target === 'trusted' || target === 'unlocked') {
|
||||
user.settings.blockPMs = target;
|
||||
@@ -728,7 +728,7 @@ export const commands: ChatCommands = {
|
||||
for (const setting in user.settings) {
|
||||
if (setting in raw) {
|
||||
if (setting === 'blockPMs' &&
|
||||
(raw[setting] in Config.groups || ['autoconfirmed', 'trusted', 'unlocked'].includes(raw[setting]))) {
|
||||
Users.Auth.isAuthLevel(raw[setting])) {
|
||||
settings[setting] = raw[setting];
|
||||
} else {
|
||||
settings[setting as keyof UserSettings] = !!raw[setting];
|
||||
@@ -1202,12 +1202,10 @@ export const commands: ChatCommands = {
|
||||
|
||||
async search(target, room, user, connection) {
|
||||
if (target) {
|
||||
if (Config.laddermodchat) {
|
||||
if (!Users.globalAuth.atLeast(user, Config.laddermodchat)) {
|
||||
const groupName = Config.groups[Config.laddermodchat].name || Config.laddermodchat;
|
||||
this.popupReply(`On this server, you must be of rank ${groupName} or higher to search for a battle.`);
|
||||
return false;
|
||||
}
|
||||
if (Config.laddermodchat && !Users.globalAuth.atLeast(user, Config.laddermodchat)) {
|
||||
const groupName = Config.groups[Config.laddermodchat].name || Config.laddermodchat;
|
||||
this.popupReply(`This server requires you to be rank ${groupName} or higher to search for a battle.`);
|
||||
return false;
|
||||
}
|
||||
const ladder = Ladders(target);
|
||||
if (!user.registered && Config.forceregisterelo && await ladder.getRating(user.id) >= Config.forceregisterelo) {
|
||||
@@ -1245,12 +1243,10 @@ export const commands: ChatCommands = {
|
||||
if (!user.named) {
|
||||
return this.popupReply(`You must choose a username before you challenge someone.`);
|
||||
}
|
||||
if (Config.pmmodchat) {
|
||||
if (Users.globalAuth.atLeast(user, Config.pmmodchat as GroupSymbol)) {
|
||||
const groupName = Config.groups[Config.pmmodchat].name || Config.pmmodchat;
|
||||
this.popupReply(`Because moderated chat is set, you must be of rank ${groupName} or higher to challenge users.`);
|
||||
return false;
|
||||
}
|
||||
if (Config.pmmodchat && !user.hasSysopAccess() && !Users.globalAuth.atLeast(user, Config.pmmodchat as GroupSymbol)) {
|
||||
const groupName = Config.groups[Config.pmmodchat].name || Config.pmmodchat;
|
||||
this.popupReply(`This server requires you to be rank ${groupName} or higher to challenge users.`);
|
||||
return false;
|
||||
}
|
||||
return Ladders(target).makeChallenge(connection, targetUser);
|
||||
},
|
||||
|
||||
@@ -93,7 +93,7 @@ export const commands: ChatCommands = {
|
||||
target = Users.PLAYER_SYMBOL;
|
||||
/* falls through */
|
||||
default:
|
||||
if (!Config.groups[target]) {
|
||||
if (!Users.Auth.isAuthLevel(target)) {
|
||||
this.errorReply(`The rank '${target}' was unrecognized as a modchat level.`);
|
||||
return this.parse('/help modchat');
|
||||
}
|
||||
@@ -203,7 +203,7 @@ export const commands: ChatCommands = {
|
||||
this.add(`|raw|<div class="broadcast-red"><strong>Moderated join is set to autoconfirmed!</strong><br />Users must be rank autoconfirmed or invited with <code>/invite</code> to join</div>`);
|
||||
this.addModAction(`${user.name} set modjoin to autoconfirmed.`);
|
||||
this.modlog('MODJOIN', null, 'autoconfirmed');
|
||||
} else if (target in Config.groups || target === 'trusted') {
|
||||
} else if (Users.Auth.isAuthLevel(target)) {
|
||||
if (room.battle && !user.can('makeroom') && !'+%'.includes(target)) {
|
||||
return this.errorReply(`/modjoin - Access denied from setting modjoin past % in battles.`);
|
||||
}
|
||||
@@ -1563,7 +1563,7 @@ export const pages: PageTable = {
|
||||
this.title = `[Permissions]`;
|
||||
const room = this.extractRoom();
|
||||
if (!room) return `<h2>This room does not exist or does not support permissions.</h2>`;
|
||||
if (!user.authAtLeast('%', room)) return `<h2>Access denied.</h2>`;
|
||||
if (!room.auth.atLeast(user, '%')) return `<h2>Access denied.</h2>`;
|
||||
|
||||
const roomGroups = ['default', ...Config.groupsranking.slice(1)];
|
||||
const permissions = room.settings.permissions || {};
|
||||
@@ -1576,7 +1576,7 @@ export const pages: PageTable = {
|
||||
const requiredRank = permissions[permission];
|
||||
atLeastOne = true;
|
||||
buf += `<tr><td><strong>${permission}</strong></td><td>`;
|
||||
if (user.authAtLeast('#', room)) {
|
||||
if (room.auth.atLeast(user, '#')) {
|
||||
buf += roomGroups.map(group => (
|
||||
requiredRank === group ?
|
||||
Utils.html`<button class="button disabled" style="font-weight:bold;color:#575757;background:#d3d3d3">${group}</button>` :
|
||||
|
||||
@@ -954,7 +954,7 @@ export class CommandContext extends MessageContext {
|
||||
this.errorReply(this.tr(`You are muted and cannot talk in this room.`));
|
||||
return null;
|
||||
}
|
||||
if (room.settings.modchat && !user.authAtLeast(room.settings.modchat, room)) {
|
||||
if (room.settings.modchat && !room.auth.atLeast(user, room.settings.modchat)) {
|
||||
if (room.settings.modchat === 'autoconfirmed') {
|
||||
this.errorReply(
|
||||
this.tr(
|
||||
@@ -996,14 +996,14 @@ export class CommandContext extends MessageContext {
|
||||
this.errorReply(`The user "${targetUser.name}" is locked and cannot be PMed.`);
|
||||
return null;
|
||||
}
|
||||
if (Config.pmmodchat && !user.authAtLeast(Config.pmmodchat) &&
|
||||
if (Config.pmmodchat && !Users.globalAuth.atLeast(user, Config.pmmodchat) &&
|
||||
!Users.Auth.hasPermission(targetUser, 'promote', Config.pmmodchat as GroupSymbol)) {
|
||||
const groupName = Config.groups[Config.pmmodchat] && Config.groups[Config.pmmodchat].name || Config.pmmodchat;
|
||||
this.errorReply(`On this server, you must be of rank ${groupName} or higher to PM users.`);
|
||||
return null;
|
||||
}
|
||||
if (targetUser.settings.blockPMs &&
|
||||
(targetUser.settings.blockPMs === true || !user.authAtLeast(targetUser.settings.blockPMs)) &&
|
||||
(targetUser.settings.blockPMs === true || !Users.globalAuth.atLeast(user, targetUser.settings.blockPMs)) &&
|
||||
!user.can('lock')) {
|
||||
Chat.maybeNotifyBlocked('pm', targetUser, user);
|
||||
if (!targetUser.can('lock')) {
|
||||
@@ -1016,7 +1016,7 @@ export class CommandContext extends MessageContext {
|
||||
}
|
||||
}
|
||||
if (user.settings.blockPMs && (user.settings.blockPMs === true ||
|
||||
!targetUser.authAtLeast(user.settings.blockPMs)) && !targetUser.can('lock')) {
|
||||
!Users.globalAuth.atLeast(targetUser, user.settings.blockPMs)) && !targetUser.can('lock')) {
|
||||
this.errorReply(`You are blocking private messages right now.`);
|
||||
return null;
|
||||
}
|
||||
@@ -1132,7 +1132,7 @@ export class CommandContext extends MessageContext {
|
||||
return false;
|
||||
}
|
||||
if (targetUser.settings.blockPMs &&
|
||||
(targetUser.settings.blockPMs === true || !this.user.authAtLeast(targetUser.settings.blockPMs)) &&
|
||||
(targetUser.settings.blockPMs === true || !Users.globalAuth.atLeast(this.user, targetUser.settings.blockPMs)) &&
|
||||
!this.user.can('lock')
|
||||
) {
|
||||
Chat.maybeNotifyBlocked('pm', targetUser, this.user);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
type Config = typeof import('../config/config-example') & AnyObject;
|
||||
|
||||
type GroupSymbol = import('./user-groups').GroupSymbol;
|
||||
type AuthLevel = import('./user-groups').AuthLevel;
|
||||
|
||||
/** not actually guaranteed to be one of these */
|
||||
type PunishType = '#chatfilter' | '#hostfilter' | '#dnsbl' | '#ipban';
|
||||
|
||||
@@ -76,8 +76,8 @@ export interface RoomSettings {
|
||||
aliases?: string[];
|
||||
banwords?: string[];
|
||||
isPrivate?: boolean | 'hidden' | 'voice';
|
||||
modjoin?: string | true | null;
|
||||
modchat?: string | null;
|
||||
modjoin?: AuthLevel | true | null;
|
||||
modchat?: AuthLevel | null;
|
||||
staffRoom?: boolean;
|
||||
language?: string | false;
|
||||
slowchat?: number | false;
|
||||
@@ -473,23 +473,13 @@ export abstract class BasicRoom {
|
||||
if (!this.settings.modjoin) return true;
|
||||
// users with a room rank can always join
|
||||
if (this.auth.has(user.id)) return true;
|
||||
const userGroup = user.can('makeroom') ? user.group : this.auth.get(user.id);
|
||||
|
||||
const modjoinSetting = this.settings.modjoin !== true ? this.settings.modjoin : this.settings.modchat;
|
||||
if (!modjoinSetting) return true;
|
||||
let modjoinGroup = modjoinSetting;
|
||||
|
||||
if (modjoinGroup === 'trusted') {
|
||||
if (user.trusted) return true;
|
||||
modjoinGroup = Config.groupsranking[1];
|
||||
if (!Users.Auth.isAuthLevel(modjoinSetting)) {
|
||||
Monitor.error(`Invalid modjoin setting in ${this.roomid}: ${modjoinSetting}`);
|
||||
}
|
||||
if (modjoinGroup === 'autoconfirmed') {
|
||||
if (user.autoconfirmed) return true;
|
||||
modjoinGroup = Config.groupsranking[1];
|
||||
}
|
||||
if (!(userGroup in Config.groups)) return false;
|
||||
if (!(modjoinGroup in Config.groups)) throw new Error(`Invalid modjoin setting in ${this.roomid}: ${modjoinGroup}`);
|
||||
return Config.groups[userGroup].rank >= Config.groups[modjoinGroup].rank;
|
||||
return Users.globalAuth.atLeast(user, modjoinSetting);
|
||||
}
|
||||
mute(user: User, setTime?: number) {
|
||||
const userid = user.id;
|
||||
@@ -596,7 +586,7 @@ export abstract class BasicRoom {
|
||||
this.destroy();
|
||||
}
|
||||
reportJoin(type: 'j' | 'l' | 'n', entry: string, user: User) {
|
||||
const canTalk = user.authAtLeast(this.settings.modchat ?? 'unlocked', this) && !this.isMuted(user);
|
||||
const canTalk = this.auth.atLeast(user, this.settings.modchat ?? 'unlocked') && !this.isMuted(user);
|
||||
if (this.reportJoins && (canTalk || this.auth.has(user.id))) {
|
||||
this.add(`|${type}|${entry}`).update();
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,7 @@ import {FS} from '../lib/fs';
|
||||
|
||||
export type GroupSymbol = '~' | '&' | '#' | '★' | '*' | '@' | '%' | '☆' | '+' | ' ' | '‽' | '!';
|
||||
export type EffectiveGroupSymbol = GroupSymbol | 'whitelist';
|
||||
export type AuthLevel = EffectiveGroupSymbol | 'unlocked' | 'trusted' | 'autoconfirmed';
|
||||
|
||||
export const PLAYER_SYMBOL: GroupSymbol = '\u2606';
|
||||
export const HOST_SYMBOL: GroupSymbol = '\u2605';
|
||||
@@ -58,9 +59,16 @@ export abstract class Auth extends Map<ID, GroupSymbol | ''> {
|
||||
isStaff(userid: ID) {
|
||||
return this.has(userid) && this.get(userid) !== '+';
|
||||
}
|
||||
atLeast(user: User, group: GroupSymbol, isPermissionCheck?: boolean) {
|
||||
if (!Config.groups[group]) return false;
|
||||
atLeast(user: User, group: AuthLevel, isPermissionCheck?: boolean) {
|
||||
if (user.hasSysopAccess()) return true;
|
||||
if (group === 'trusted' || group === 'autoconfirmed') {
|
||||
if (user.trusted && group === 'trusted') return true;
|
||||
if (user.autoconfirmed && group === 'autoconfirmed') return true;
|
||||
group = Config.groupsranking[1];
|
||||
}
|
||||
if (user.locked || user.semilocked) return false;
|
||||
if (group === 'unlocked') return true;
|
||||
if (!Config.groups[group]) return false;
|
||||
if (this.get(user.id) === ' ' && group !== ' ') return false;
|
||||
if (Auth.atLeast(this.get(user.id, true), group)) {
|
||||
return true;
|
||||
@@ -109,7 +117,9 @@ export abstract class Auth extends Map<ID, GroupSymbol | ''> {
|
||||
|
||||
const symbol = auth.getEffectiveSymbol(user, useVisualGroup);
|
||||
let targetSymbol = (typeof target === 'string' || !target) ? target : auth.get(target);
|
||||
if (targetSymbol === 'whitelist') targetSymbol = Auth.defaultSymbol();
|
||||
if (!targetSymbol || ['whitelist', 'trusted', 'autoconfirmed'].includes(targetSymbol)) {
|
||||
targetSymbol = Auth.defaultSymbol();
|
||||
}
|
||||
|
||||
const group = Auth.getGroup(symbol);
|
||||
if (group['root']) return true;
|
||||
@@ -129,7 +139,7 @@ export abstract class Auth extends Map<ID, GroupSymbol | ''> {
|
||||
}
|
||||
}
|
||||
|
||||
return Auth.hasJurisdiction(symbol, jurisdiction, targetSymbol);
|
||||
return Auth.hasJurisdiction(symbol, jurisdiction, targetSymbol as GroupSymbol);
|
||||
}
|
||||
static atLeast(symbol: EffectiveGroupSymbol, symbol2: EffectiveGroupSymbol) {
|
||||
return Auth.getGroup(symbol).rank >= Auth.getGroup(symbol2).rank;
|
||||
@@ -183,6 +193,10 @@ export abstract class Auth extends Map<ID, GroupSymbol | ''> {
|
||||
if (symbol.length !== 1) return false;
|
||||
return !/[A-Za-z0-9|,]/.test(symbol);
|
||||
}
|
||||
static isAuthLevel(level: string): level is AuthLevel {
|
||||
if (this.isValidSymbol(level)) return true;
|
||||
return ['unlocked', 'trusted', 'autoconfirmed', 'whitelist'].includes(level);
|
||||
}
|
||||
static ROOM_PERMISSIONS = ROOM_PERMISSIONS;
|
||||
static GLOBAL_PERMISSIONS = GLOBAL_PERMISSIONS;
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ export class User extends Chat.MessageContext {
|
||||
|
||||
settings: {
|
||||
blockChallenges: boolean,
|
||||
blockPMs: boolean | GroupSymbol | 'autoconfirmed' | 'trusted' | 'unlocked',
|
||||
blockPMs: boolean | AuthLevel,
|
||||
ignoreTickets: boolean,
|
||||
hideBattlesFromTrainerCard: boolean,
|
||||
};
|
||||
@@ -531,20 +531,6 @@ export class User extends Chat.MessageContext {
|
||||
const status = statusMessage + (this.userMessage || '');
|
||||
return status;
|
||||
}
|
||||
authAtLeast(minAuth: string, room: BasicRoom | null = null) {
|
||||
if (!minAuth || minAuth === ' ') return true;
|
||||
if (this.locked || this.semilocked) return false;
|
||||
if (minAuth === 'unlocked') return true;
|
||||
if (minAuth === 'trusted' && this.trusted) return true;
|
||||
if (minAuth === 'autoconfirmed' && this.autoconfirmed) return true;
|
||||
|
||||
if (minAuth === 'trusted' || minAuth === 'autoconfirmed') {
|
||||
minAuth = Config.groupsranking[1];
|
||||
}
|
||||
if (!(minAuth in Config.groups)) return false;
|
||||
const auth = (room && !this.can('makeroom') ? room.auth.get(this.id) : this.group);
|
||||
return auth in Config.groups && Config.groups[auth].rank >= Config.groups[minAuth].rank;
|
||||
}
|
||||
can(permission: RoomPermission, target: User | null, room: BasicRoom, cmd?: string, useVisualGroup?: boolean): boolean;
|
||||
can(
|
||||
permission: GlobalPermission,
|
||||
@@ -1360,12 +1346,12 @@ export class User extends Chat.MessageContext {
|
||||
chat(message: string, room: Room | null, connection: Connection) {
|
||||
const now = Date.now();
|
||||
|
||||
if (message.startsWith('/cmd userdetails') || message.startsWith('>> ') || this.isSysop) {
|
||||
if (message.startsWith('/cmd userdetails') || message.startsWith('>> ') || this.hasSysopAccess()) {
|
||||
// certain commands are exempt from the queue
|
||||
Monitor.activeIp = connection.ip;
|
||||
Chat.parse(message, room, this, connection);
|
||||
Monitor.activeIp = null;
|
||||
if (this.isSysop) return;
|
||||
if (this.hasSysopAccess()) return;
|
||||
return false; // but end the loop here
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user