- `writeUpdate` state is now stored in a global variable, so hotpatching doesn't crash it
- throttling now writes on the tail (so two throttled `writeUpdate` calls will write one update, not two)
- room settings, punishments, and helptickets are now throttled
`checkLearnset` has been renamed `checkCanLearn`.
`reconcileLearnset` has been replaced with `validateMoves`, which
bundles the actual `checkCanLearn` calls with the old
`reconcileLearnset`, making for a better name.
* Add multi-hit as a parameter to /ms
* Revert /help ds, add multihit to /help ms, fix TS
Code linted, let's go
'Tis coding at midnight
Save what had been tossed...
Bring back the older line...
The older line.
* ;-;
1465 passing (25s)
53 pending
> pokemon-showdown@0.11.4 posttest /home/partman/Documents/Code/pokemon-showdown
> npm run tsc
> pokemon-showdown@0.11.4 tsc /home/partman/Documents/Code/pokemon-showdown
> tsc
No idea why this wasn't caught.
* Fingers crossed
I wrote a `forceWrap` method to support break-word wrapping in table
cells for scavengers, but apparently code blocks need it too, so I'm
moving it to Utils.
Fixes#7854
- Polls were always starting as quizzes even when they weren't
- Hotpatching chat would reset poll/announcement timers
- A lot of bugs relating to minute/millisecond confusion in timer code
- Poll answers were called `questions` in the source code for some
reason; they've been renamed to consistently be `answer`.
- Refactor out an extremely unnecessary manual iteration
* Fix Autoresponder Refresh button bug
Currently if you go into ``/autoresponder view`` and choose to view autoresponder Stats, it opens a menu where no date is specified and you can choose a date from there. If, however, you press the Refresh button (where no date is specified), it opens a new page called "view-autoresponder-room-stats-" (note the extra hyphen at the end). This is because the "refresh" function is called (on line 458) with [date], which would equal [""]. This is a truthy value, unlike [] and unlike "". This patch changes the conditional statement to look at the first item in the array's truthfulness - it is either an empty string (falsey) or a non-empty string (truthy) - instead of always being truthy.
* Fix failing test
* Fix failing test
Sorry if this isn't ideal, it's certainly not as elegant as it originally was.
* Update responder.ts
* Update responder.ts
* Update responder.ts
* Chat monitor: Improve the handling of usernames
This PR ignores word boundaries in evasion regexes when filtering usernames, preventing the use of usernames with evasions of slurs in them.
* properly fix merge conflicts
* Allows players that subbed out or stopped cohosting to be forceadded into the game.
* Upon subbing, players' pages update after the subbed player is removed from the playertable.
* Allows /mafia dt for ideas
* Shift plurality to the subbed in player.
* Fix clear button
* Adds spaces between roles in role list.
* Syntax fixes
* Add info to /mafia dt <idea>
Co-authored-by: Student <Student@MacBook-Air-5.hsd1.il.comcast.net>
Unlike /effectiveness and /coverage, /dexsearch doesn't need to support
arbitrary mods, so Thousand Arrows can be hard-coded as the only move to
ignore any immunities.
* Split up dex-data over individual files
This commit introduces:
- `dex-abilities.ts`
- `dex-conditions.ts`
- `dex-formats.ts`
- `dex-items.ts`
- `dex-moves.ts`
- `dex-species.ts`
These files centralize definitions from `dex-data` and `global-types`.
* Inherit ItemData from Item etc
Previously, Condition inherited from ConditionData. Now, ConditionData
inherits from Condition. The advantage of the new approach is that now,
Condition and DataCondition no longer need to be separate types, and
there should be much less duplication of type definitions in general.
This has also been done for
- ItemData/Item/DataItem
- AbilityData/Ability/DataAbility
- FormatData/Format/DataFormat
Species and DataSpecies was already merged, but this also reverses
their inheritance (saving a lot of duplicated definitions in the
process!)
The only one left is MoveData, which is just super complicated and
will need its own commit.
The sim no longer uses any global variables, so this can and should be
removed.
`server/global-variables` has also been cleaned up, since I'm working
on this.
* Lint arrow-body-style
* Lint prefer-object-spread
Object spread is faster _and_ more readable.
This also fixes a few unnecessary object clones.
* Enable no-parameter-properties
This isn't currently used, but this makes clear that it shouldn't be.
* Refactor more Promises to async/await
* Remove unnecessary code from getDataMoveHTML etc
* Lint prefer-string-starts-ends-with
* Stop using no-undef
According to the typescript-eslint FAQ, this is redundant with
TypeScript, and they're not wrong. This will save us from needing to
specify globals in two different places which will be nice.
Two minor changes for the Scavengers plugin:
- Change the ``/`` for alts to a ``;`` to allow easier copying
- Change the edithunt method to allow commas in the answers
yes another modlog commit from me!
this time, it's making sure that note searches based on user IDs actually get toID() called on them, and fixing some HTML escaping issues
This provides a new way to persist chat plugin data across hotpatches:
`Chat.oldPlugins`.
In a plugin, you can now do:
```
export const cache = Chat.oldPlugins.pluginname?.cache || {};
```
to create a cache that will persist across hotpatches.
We're skipping two major typescript-eslint versions, so there are a
bunch of changes here, including:
- it's catching a lot of things it didn't catch in the past, for
reasons unclear to me
- no-unused-vars has to be explicitly disabled in global-types now
- a lot of `ts-ignore`s were never necessary and have been fixed
- Crashlogger can now handle being thrown things that aren't errors.
This has never been a problem in the past, but to satisfy TypeScript
we might as well not die in a fire on the off chance someone tries to
`throw null` or something.
This adds new functions `stream.byChunk(bytes)`, `stream.byLine()` etc
which parse a `ReadStream` into an `ObjectReadStream<string>` which
can then be consumed with for-await.
Fixes#7195
This doesn't really have any nice-to-have automatic restoration
features but it should be all the important parts.
`user.group` no longer exists, and has been replaced with
`user.tempGroup`, which now applies both to temporary promotions of
unregistered users, as well as temporary hidden ranks of auth.
The crashes in the daily spotlight plugin were caused by `/queuedailyat` leaving an empty daily that can't be parsed before failing. This should solve it, but some rooms' spotlight data may still cause crashes. (These rooms should use `/removedaily`.)
`this.dex.deepClone` still exists as an alias to `Utils.deepClone` for
use in `data/`. I'll need to spend more time figuring out the correct
solution there.
After a Policy decision within Wi-Fi Staff, it's been decided to update the
Lottery Giveaway to still pick winners when there are less than maxWinners,
but people entered. This will allow low entry GA's to overestimate and still
have winners.
- Add new command to directly stuff a hunt back into the queue
- Better queue response
- Fix extra spaces in teamscav team names
- Get rid of some old code tidbits that were neccessary when roomlogs were still bork
onChatMessage previously took `string | false`, where `false` means
"let the message through". This is a reversal of what `false` usually
means, so this is now updated to `string | void`, which should be
much clearer.
- `ip` and `ipself` are now separate permissions. This means that `ip`
is now a generalized permission for viewing IPs (no more need to use
`globalban`, since `ipself` now controls the ability to see your own
IP address)
- `alts` and `altsself` are now also separate (all users used to be
able to `altsself`).
- `modchat`, `modchatall`, and `manageroom` are now just one `modchat`
permission whose jurisdiction controls how high you can set modchat.
`BattlePokedex` is now `Pokedex`, `BattleItems` is now `Items`, etc.
I also renamed `Movedex` to `Moves` and `Statuses` to `Conditions`.
`TypeChart` isn't `Types` yet, because unlike the others, it's not
indexed by ID. That should probably be fixed one day.
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.
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 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>
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.
- Long answers now wrap (I tried so many different ways to do this
in CSS, involving nested divs with max-width, and ended up giving
up and just inserting `<wbr />`s into the code.
- Hints are now in boxes, making them easier to see
- There's a "to answer, use /scavenge ANSWER" prompt next to the
hunt start message, to help new players.
- Fixed a bug with help permissions
This is a really minor thing, but fewer words tend to make
sentences more readable, and crash messages should focus more
on why something crashed than the crash itself.
uhtmlchange messages, which should never have been in the chatlog
in the first place, are now always hidden.
uhtml messages are now hidden by default (can be shown with the `all`
option), because they lead to huge message sizes which cause problems
with the server, in gamecorner and a few other rooms which use it a
lot.
This mostly serves to provide cleaner and more consistent field
naming. maxMove currently doesn't have boosts or effects to group
together but who knows what will be thrown at us via DLC, and being
symmetrical with zMoves is a nice.
For historical reasons, move property definitions have been very blurry
across `EffectData`. Fortunately, recent refactors have made it
possible to put them all where they're supposed to be.
Not having prefer-const on the JS side makes JS -> TS refactors really
unreadable. This commit just auto-fixes it so we're using
`prefer-const` everywhere.
* Attribute non-host mod commands
* Fix crash in queue
* Add command for listing all data entries
* Update permissions in help box
credits to @Claire238
Pokemon that are not available in-game are now consolidated into `isNonstandard`. 'Past' indicates that the Pokemon no longer works in the current generation. 'Unobtainable' indicates that the Pokemon works but can only be obtained through hacking.
- apply them as "formats"/"mods" instead of a large parent game with another room game nested inside
- the parent game is attached to ``room.scavgame`` as an independent object instead.
- add a command to set the twist (/scav settwist) and reset the twist (/scav resettwist) for scripted formats
- this means that scavenger-games.js might be updated once per month as new twists are created.
- add a command to start a regular hunt with twists
More than one twist can be loaded into a single scavenger hunt (from scavenger game mode + /scav createtwist)
- global auth should now be able to see chat logs of rooms they can't
moderate
- secret and personal rooms that a staff member is either currently in,
or has auth in, will now show up in the main logs list (although
you should just use /chatlog, it's much easier)
`user.can` and `user.authAtLeast` now take `Room | BasicChatRoom`
instead of `BasicChatRoom`. It's now significantly less necessary to
cast things to `BasicChatRoom`.
This is mostly just a follow up to #6342.
`prefer-optional-chaining` was turned on and fixed in every location it
complained in. The transformed function [0] looks expensive from a
glance but from skimming through the replaced sites it doesn't appear
to be ran in any important place, so it should be OK.
The linter improvements are:
- Increase linter performance
- Make `full-lint` and `lint` write to different caches so we
avoid overwriting their caches since they're different configs
- Change husky's hook to `npm run lint` so as to write to the
same cache
- Remove `@typescript-eslint/eslint-plugin-tslint` which is
essentially a wrapper to TSLint because the rules aren't worth
running another linter
- Convert `.eslintrc.json` and `.eslintrc-syntax.json` to two spaces
rather than four tabs to respect PS' `.editorconfig`
- Rename `fulllint` to `full-lint` to ease spelling it
[0] - https://pastie.io/mmtxpf.js (prettified)
There is no lottery editmarkup command. The proper use is to use /lottery edit, which allows you to edit the number of max winners, the name of the lottery, and the HTML, but also requires all three parameters be included.
By adding a `getGame` function of type:
```
// null is returned if the gameids don't match
// or the game doesn't exist
getGame<T extends RoomGame>(constructor: new (...args: any[]) => T) => T | null
```
(Credits @urkerab and @whalemer for the function signature.)
It allows refactoring previous code of:
```
if (room.game && room.game.gameid !== 'hangman') return;
const game = room.game as Hangman;
```
to:
```
const game = room.getGame(Hangman);
if (!game) return;
```
This has a couple of advantages:
- TypeScript will throw an error if the if condition is not present.
- In the new code, the template must extends `RoomGame` and be assignable to the same ID, so it's 100% typesafe
Date strings can have inconsitensies between browsers and apparently node versions too.
sim3 uses a newer node version and as a result date calculations are off by one.
I have swapped to using individual date componets eg `new Date(2020, 0)` to fix this.
This commit adds a `challengeType` property to `RoomBattle` of
type `rated | unrated | challenge | tour`.
Previously, there was no way to programatically differ an unrated
battle from a challenge, which is useful in places like filters.
Right now the /mnm command results in warnings that the mega stone is a CAP stone, even when it is not. This is because isNonstandard is now set to "Past" instead of undefined.
`server/chat-commands.js` is now a directory. It's been split into
`core`, `moderation`, and `admin`. `info` and `roomsettings` from
`chat-plugins` have also moved to `chat-commands`.
Some cleanup:
- Bot commands for inserting HTML into rooms like `/adduhtml` have been
moved from `info` into `admin`.
- `/a` has been renamed `/addline`, for clarity (and also moved from
`info` into `admin`).
- Room management commands like `/createroom` and `/roomintro` were
moved to `room-settings`
- `chat-commands/admin` has been TypeScripted
Command consoles previously would silently fail if you used a
broadcastable command (like `/dt` or `/learn`). They are now let
through, although actually trying to broadcast (like `!dt`) will
still show an error message.
- Automatically request PM logs from users when possible in PM harrasment tickets.
- Add a button to check a reported user's global modlog to all tickets.
- Add buttons for force-renaming/clearing the status of a user in inappropriate name/status tickets.
- Add a button for checking shared battles between a the reporter and reported user for battle harrasment tickets.
- Other minor improvements.
* Make /events view visually match /events
Lack of consistency was bothering me
* This is why I shouldn't code when I'm sick
* Generalize formatEvent to function
I'm just going to pretend this code is good
* Update room-events.js
Closing an inactive ticket indicates that there was no communication
between the user and staff, do not credit a ticket to the staff member.
Also fixes an issue with a error message for closing tickets with a
negative result that were already closed.
- Remove ticket escalation because its not used.
- Simpliy report category to hopefully reduce the number of tickets opened in the wrong category.
- Improve ticket form interaction with namelocked users.
- Add a button to PM Harrasment ticket staffintros to allow staff to quickly request pm logs.
- - Only works when the reporter reports a user through their usercard.
* Refactor filters to always use regex
* Update server/chat-plugins/chat-monitor.js
Co-Authored-By: Kirk Scheibelhut <scheibo@users.noreply.github.com>
* Simplify evasion detection regex storage after peach rightfully pointed out how fucking stupid i am
* Put this garbage in adminlog if that room exists
* fix tslint
Previously, we split gen 2-5 egg move validation into two phases,
`checkLearnset` where we searched for a valid father, and
`reconcileLearnset` where we made sure the father could learn the move
combination.
Egg move validation has now been completely moved out of these
functions and into `validateSource`, which calls `findEggMoveFathers`.
The new algorithm no longer requires `C` moves to be hardcoded into
`learnsets.js`, now doing a more thorough check validation for the
father's move combination. This should be slower than before, but
net performance should be massively improved due to two other
optimizations in this refactor:
- We no longer do any father-searching if the moveset can be obtained
any other way - in particular, this means no more father validation
in gen 6+ where all egg move combinations are legal anyway.
- We only check fully-evolved pokemon as fathers (because anything a
prevo can learn, its evos can learn, too - yes, we remember to make
exceptions for Salazzle, Combee, and future-gen evos)
In addition, `/learn` should now provide significantly better
information for egg move breeding, since it uses a more thorough check
instead of the validator's short-circuiting the moment it finds a valid
father.
This also improves some baby-only move validation, specifically fixing
the issue with Seismic Toss Charm Chansey.
* Update chat monitor to include a battle filter, as well as evasion detection
* Forgot about the roomid rename thing
* Mitigate the first problem hopefully
* Mitigate the second problem in an extremely elegant way
* Precompile the substitutions
* Make this new filter not racist towards nigerians
* Totally not ghostwritten code because i'm really smart
* Small edit
- Repealing rules now always works, regardless of rule order
(Fixes AAA validation)
- Fix a check for egg move hidden ability validation
(Fixes a Gen 4 Dragon Dance Charizard set)
- Always ban AG when banning Uber
(Fixes allowing Rayquaza-Mega in lower tiers)
- Fix ability validation in Let's Go
- Fix valid-move-combo dexsearch
* Refactor validator
This is a major refactor intended to make the default rules easier to
understand, and also easier for OMs to bypass.
Removed rules:
- `Pokemon`: This is now `-Nonexistent`. Its previous name was intended
to be interpreted as "simulate the Pokémon games exactly, and only
allow what they allow". The new name should make it clearer that it
mainly bans CAPs and other nonexistent Pokémon and functionality.
- `-Illegal`: This is now `Obtainable` (see below).
- `Allow CAP`: This is now `+CAP`. Instead of having a hardcoded rule,
OMs can now be manually whitelist any pokemon/item/etc or group of
them, overriding `-Nonexistent`.
- `Ignore Illegal Abilities`: This is now `!Obtainable Abilities` (see
below).
`Obtainable` was previously `-Illegal`, and does the same thing: Makes
sure you have a regular Pokémon game with only Pokémon that can be
obtained without hacking.
But instead of being a ban, it's now a rule that does nothing by
itself, except contain more rules:
- `Obtainable Moves`
- `Obtainable Abilities`
- `Obtainable Formes`
- `Obtainable Misc`
- `-Nonexistent`
- `-Unreleased`
This allows OMs to piecemeal repeal and unban any of these individual
rules, instead of the previous approach of unbanning them all and
manually reimplementing every single validation you wanted to keep.
* Refactor PokemonSources into a class
This mostly just makes a lot of the weirder checks in the validator
substantially more readable.
This also renames `lsetData` to `setSources`, which should also help
readability.
* Validate Bottle Cap HP types
Fixes an issue reported here:
https://github.com/Zarel/Pokemon-Showdown/issues/5742#issuecomment-533850288
* Fix several move validation issues
Fixes#5742
We have a new MoveSource type: R for Restricted. R moves work like
level-up/tutor/TM moves, except you're limited to one R move.
- Shedinja move stolen from Ninjask in Gen 3-4 are now R moves instead
of event moves. This allows them to coexist with Nincada egg moves.
- Necrozma-DW/DM now inherit moves/events from Necrozma (like Rotom,
but with event validation). This allows them to be shiny.
- Pokemon can now get egg moves from their own evolutions. This fixes
some Tyrogue, Charmander, and Treecko sets mentioned in #5742
- Some more C moves were added, fixing some Hitmontop and Chatot sets
mentioned in #5742
* Improve ability/move compatibility validator
eslint:recommended seems tohave gotten more sensitive.
require-atomic-updates was disabled because "I think this is
intentional behavior because that someObj object was read before
await expression and written after await expression." is wayyy
to aggressive: https://github.com/eslint/eslint/issues/11899
* Add more hunt management options
- Hints: add a command to display hints that can be recalled using /scav hint and /viewhunt for hunts that last several hours. This allows previously given hints to still be visible even after it scrolls out of chat, or the user uses /clear after completing some difficult param question. This command cannot be used until after 10 minutes of the hunt.
- /scav status is enhanced to show users who have tried to guess in the last 5 minutes in bold (<strong> for you HTML purists)
This has been approved by all 4 roomowners.
* Improve username punishments
Track how many times a name has been forcerenamed, note differently for offline warns and forcerenames
* Hyperlink names, show trusted/ac/registered
* rebase
* pseudorank -> accountstatus
- Anyone can now see the amount of participants
- Max winners can't go above `Number.MAX_SAFE_INTEGER`
- `/lottery edit` can be used to edit the lottery
- "Page unavailable" is not shown to guest users, instead the not "allowing guests to join" is deferred to `/lottery join`.
- Punished users are dropped before generating winners
This seemed desirable in tested, but probably because I hadn't
refreshed. Also, the creator can wrap their content in a div and
give it a margin-bottom.
Created distinctions between:
Role Cop and Role Cop (Classic)
Red Goo and Red Goo (Cracking Idea)
Neapolitan and Vanilla Cop
Also clarified some roles and standardized how Idea Variant roles are listed.
Added a distinction between Silencer and Silencer (Greater Idea) and between Grey Goo and Grey Goo (Cracking Idea). Also added the (xxx Idea) tag to the roles in their respective ideas.
- Ensure the first card of the dealer is on the top of the turnlog
- Don't allow people to use the command to start the game if the game has already started
- Use user names instead of user ids in the initial playerlist for join messages
- Modlog conditionally if the game was ended forcibly or not
This command was originally restricted because users would complain
about lag when the uptime hit about a week. I'm removing this because
it makes it alot harder to verify a server's uptime meets the
requirements when registering servers, and its been a year or so
since lag complaints related to uptime were an issue.
- Modchat permission code should be simpler now
- Mods now have 'modchatall' (can set modchat up to their own rank)
- 'modchatall' (ROs) can no longer remove modchat higher than their own
rank
- 'makeroom' (Leaders) still can remove higher modchat
Namefilter is a complete mismatch for status messages, since names
have very specific requirements that don't apply to status messages.
Nicknames have no such restriction, and work basically the same as
status messages.
By default, PS doesn't have very many tools for dealing with ban
evaders on proxies - most of the main server's protection is in private
code.
This commit adds a basic IP evaluator to PS. It categorizes IPs into
residential, mobile, and proxy (and a few other determinations), and
locks proxy IPs by default. DNSBL entries remain semilocked.
This behavior can, as always, be customized via hostfilter. Detect
`user.locked === '#hostfilter'` for the proxy IP lock, which you can
just set `user.locked = null` to disable.
Fixes#5239
* Scavengers: Viewhunt/Queue improvements/fixes
- Add "unrated" hunts to queue system
- fix /forcestarthunt, an add an unrated option for this.
- allow /viewhunt to show the current question while leaving the unknown answer blank
* Update scavengers.js
`import =` and `export =` are really only intended for backwards
compatibility with CommonJS. While I really don't like the new module
system TC39 has designed for us, it's what we should be using, and
consistency is important.
This is mostly a TypeScript refactor, but it does come with several
renames:
Dnsbl -> IPTools
Dnsbl.query -> IPTools.queryDnsbl
Dnsbl.reverse -> IPTools.getHost