This will actually destroy groupchats when a user is locked/globally banned, which to my understanding is the intended behavior.
`continue`ing when a room isn't persistent makes no sense, since groupchats are by definition not persistent.
The nullable room refactor made it so there was no longer an easy way
to tell if a command could be used in PMs. This adds it back, as a
property on the handler function itself.
Now that we have properties on handler functions, I also added
`broadcastable` for whether or not the command is broadcastable.
It uses `Function#toString`, which is generally frowned-upon, but it's
well-specified and only happens during startup, so it shouldn't affect
performance after startup.
This also allows us not to need to repeat ourselves or give up
flexibility to have metadata about commands, which I think is worth the
tradeoff of reflection generally.
Half the permissions checks were previously in `user.can`, which is
unintuitive. It's now completely self-contained and should be pretty
readable, now, with `getEffectiveSymbol` and `hasJurisdiction` as
separate functions.
This command should never be used by users.
This is for scripting, like buttons, like:
<button name="send" value="/nofeedback /ionext">
so they don't spam your PMs.
Global punishments like /lock will notify Staff, and admin commands
like /updateserver will log to Staff, but this has been done ad-hoc
in the past.
To handle these cases, this commit introduces new chat-context
functions:
- `this.addGlobalModAction`,
- `this.privateGlobalModAction`
- `this.stafflog`
Other updates:
- the issue where Staff notifications didn't show up until someone talks
has been fixed
- `privateModAction`s in Staff will now be visible in the scrollback
log
- a bunch of commands that should notify Staff but didn't now do
- some typos in modlogs have been fixed
NOTE: This is changes the semantics of `hidenext`/`ionext` from
applying to the next created *battle* to applying to the next
created *search*/*challenge*.
The idea is that throwing `ErrorMessage` will replace needing to pass
`context` variables around (which make it hard to unit test a lot of
chat functions).
I recognize the drawback is that it makes it harder to tell where
chat commands might return from. This might be somewhat alleviated by
a convention such as prefixing everything with `check`
this.checkBroadcastable();
this.checkCan('lock');
I honestly didn't like the old approach of `if (!this.can(...)) return`,
though. It didn't seem very obvious which commands would show error
messages and which needed you to write your own error messages. I think
the new system would at least be clearer about that.
We can also consider things such as some sort of sigil, such as:
!this.checkCan('lock');
There's no other reason to use `!` at the beginning of a line, so I
think this is reasonably unambiguous, although it might take some
time to learn. Also we'd have to screw with eslint.
Another alternative is something all-caps?
this.CHECK_can('lock');
In the end, I still think `this.checkCan('lock')` would be enough, and
I still think it's already an improvement in many ways.
Permissions have gotten out-of-date, so this commit syncs them.
Default permissions are now matched with Main, in particular including
the new & rank as admin (removing the old Leader rank and ~ symbol).
Relevant changes:
- Admin (~) and Leader (&) have been merged into Admin (&)
- The 'ban' permission was split into 'globalban' and 'ban'
- The 'broadcast' permission was renamed 'show' (going forward,
"broadcast" should only refer to the big red/blue/green
announcement bars.)
- Bots no longer have global moderation abilities, making it
easier to give untrustworthy bots the "bot" rank.
They were only ever kept separate because of GlobalRoom. It might be
useful to support rooms that aren't ChatRooms, but we've been chucking
properties into either BasicChatRoom or BasicRoom essentially at
random, so I think it makes sense to wait until we actually have a
use-case for a non-Chat room before carefully deciding which properties
belong where.
I couldn't completely remove the global room in one commit, but this
solves basically every problem with it by making it no longer a `Room`.
In particular, this means:
- It's no longer of type `Room`
- It's no longer in the `Rooms.rooms` table
- Its class name is now `GlobalRoomState` rather than `GlobalRoom`
- It no longer tracks its own user list (online user count is now
provided by `Users.onlineCount`)
- It's no longer a socket channel (there's new syntax for "send this
message to every user")
String#split with an empty string as an argument butchers Unicode
codepoints that are more than one character long.
String#[Symbol.iterator] doesn't, and is also faster.
- Chat.getImageDimensions and Chat.fitImage now throw if passed things
that aren't image URLs.
- Fix help message if you use /show by itself
- Link to full aize image if image is shrunk
This replaces the old approach with a new "clear everything except a
whitelist" approach, which should overall involve much less code and
lead to fewer bugs of the "the path changed for a module and I forgot
to update the uncache paths" variety.
I considered a lot of other approaches, but they seem to have more
flaws without any advantages in exchange for them. (We moved away
from `uncacheTree` because it only tracks the first require: there's
no way to get a full list of dependents for a module, only its first
dependent.)
Previously, ending a read stream was `stream.push(null)`, and ending a
write stream was `stream.end()`. This was often confusing, and so now,
these are consistently `stream.pushEnd()` and `stream.writeEnd()`.
This refactor already found a bug in which `stream.end()` was used
where `stream.push(null)` should have been.
Also in this refactor: By default, `pushError` ends the stream. You can
pass `true` as the second parameter if the error is recoverable (the
stream shouldn't end).
This implements two big changes:
- All settings shared between `room.chatRoomData` and `room` have been
merged into `room.settings` (so, for instance, `room.slowchat` is now
only `room.settings.slowchat`).
This makes it so we never have to worry about them getting "out of
sync".
- Checking to see if a room is persistent is now `if (room.persist)`
instead of `if (room.chatRoomData)`
- `Rooms.global.writeChatRoomData()` is now rarely called directly;
there's a new `room.saveSettings()` which will handle it for you.
- All properties of `room.settings` are now optional (except
`title`).
- There's a new file `user-groups.ts` which handles authority.
- `room.auth` and `Users.globalAuth` are now
`Auth extends Map<ID, GroupSymbol>` objects.
- `room.auth` is now always defined, removing the need for
`room.auth?.[userid]` workarounds.
- A lot of code relating to usergroups and permission checks have
been refactored.
Co-authored-by: Guangcong Luo <guangcongluo@gmail.com>
A lot of people had been asking about whether it was possible to search for moves with a high critical-hit ratio (relevant for building with things like Sniper or Focus Energy).
(Also includes a refactor to cut down on the number of type assertions
necessary in movesearch code, by Zarel)
Co-authored-by: Guangcong Luo <guangcongluo@gmail.com>
Currently, 'hotpatch' and 'lockdown' are admin permissions, and
'console' is a console admin permission. In preparation for the
admin/leader unification, I'm removing the hotpatch permission:
- /memoryusage, /loadbanlist, /adddatacenters, /refreshpage,
/loadbanlist have been moved from 'hotpatch' to 'lockdown'. This
doesn't change anything, I'm just unifying the permission name.
- /updateserver has been moved from 'hotpatch' to 'console'.
/updateserver in combination with either hotpatch or lockdown can be
used for arbitrary code execution, and is approximately as dangerous
as any other console command.
- /hotpatch, /savelearnsets have been moved from 'hotpatch' to
'console'. They're reasonably harmless, but they don't do anything
without console access, so a non-console admin using them is nearly
definitely some sort of mistake.
`user.hasConsoleAccess` now also has a CommandContext function
`this.canUseConsole` to handle its error message.
PR #6781 only fixed one of the crashes; the real problem was that the
code expects aliases to be an empty array if rooms have no aliases,
which have multiple flaws including unnecessarily taking up JSON
space.