-
v5.4.0
Stablereleased this
2026-10-11 16:54:10 -05:00 | 0 commits to main since this releaseThe
debug:Truefooter names the model and settings that answered, and
Make Public posts it with the answer.Added
- The footer ends with the model, effort and thinking setting:
-# Model: claude-sonnet-5-5 · effort high · thinking on. The model is
the one the API says answered. Effort and thinking are what the request
sent. Thinking is adaptive, so "on" means the model was free to think, not
that it did. Sonnet 5.5 returns its thinking blocks empty and also uses
them for progress notes between tool calls, so a block in the response
does not show that it reasoned.
Changed
- Make Public posts the debug footer under the answer. The public post
now matches what the asker saw. Before, the footer was dropped on publish.
The footer is still kept out of the saved conversation turn and out of the
shared context that later questions read, so turning debug on changes
nothing about the next answer.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- The footer ends with the model, effort and thinking setting:
-
v5.3.7
Stablereleased this
2026-10-10 13:04:51 -05:00 | 1 commits to main since this releaseTwo small fixes found while fixing 5.3.6, and the dependencies brought up to
date.Fixed
- A fetched page that shows an HTML entity reached Claude with it decoded
twice.web_fetchdecoded&first and then the other entities, so
the&the first step produced started a second one: a page writing about
HTML that showed<p>reached Claude as the tag<p>. Each entity is
now decoded once. - The migration rollback logged the migrations it undid as fields named
0,1, …. They now go in onemigrationsfield, as they already did
when migrating.
Changed
@anthropic-ai/sdk0.131 → 0.133,@modelcontextprotocol/sdk1.32.1,
dotenv 18.0.7, and on the dev side Prettier 3.9.10, typescript-eslint
8.71.1,@types/node24.19.2 and thejs-yamloverride 5.4.3. None of the
SDK changes touch what the bot uses. TypeScript stays on 6.0: TypeScript 7
is out, but the newest typescript-eslint (8.71.1) still accepts only
<6.1.0and the newest ts-jest (29.4.14) only<7.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- A fetched page that shows an HTML entity reached Claude with it decoded
-
v5.3.6
Stablereleased this
2026-10-10 12:48:38 -05:00 | 2 commits to main since this releaseThe last 22 findings from the whole-repo review, each minor or rare. LoL
results survive a restart and a watchlist change, registering checks the
region and Riot ID first, failed buttons say so, a broken mappings file no
longer wipes names, and the logs keep their details and never show the
database password. A setting that did nothing,D2EMU_POLL_INTERVAL_MS, is
gone.Fixed
- A LoL result still pending when the bot restarted was never posted.
Riot can take a few minutes to publish a finished match, and the bot keeps
checking for it in the background. A restart in that window, such as a
deploy, stopped the checks and nothing started them again, so the post said
"results unavailable" for good. After a restart the bot now picks up any
result from the last half hour that never arrived. - A restart could drop a LoL game's result over one failed Riot call. On
startup the bot checks whether each game it was following is still on. One
timeout or busy answer from Riot at that moment wrote the game off, so if it
had ended, its result never appeared. The game is now checked again on the
next poll, as it already was outside a restart. - Removing a player from the watchlist mid-game could cost that game its
result. A game is followed through the first watched player found in it.
If that player was removed while it was on, nobody noticed it end, and it
was written off hours later with no result. Another watched player in the
game now takes it over, and the result shown is theirs. - A LoL game that came back from a false end could lose its ranks and
result after the notification channel changed. Riot sometimes briefly
reports a live game as over, and when it reappears the bot edits its
original post back. Ifmonitor-channelhad moved notifications meanwhile,
the bot then looked for that post in the new channel, so the ranks and the
result never arrived. It now follows the post in the channel it is in. - The cleanup of stuck LoL games always reported none. Its count was
always 0, so its log line never appeared, and the games it closed had no
end time. It now reports how many it closed and records when. - A LoL player registered under a region they don't play on was accepted,
and their games were never posted. Riot finds an account whatever region
is typed, so the wrong region was stored and their games were looked for on
the wrong server. Register and monitor-add now ask Riot where the player
plays and register them there, and say so when it differs from what was
typed. If Riot can't be asked, the typed region is used as before. - A mistyped LoL region or a name without its #TAG got "try again
later". Neither was checked until Riot was asked, so input that could
never work failed as if Riot were down, and was logged as an error.
Register, unregister, monitor-add and monitor-remove now reply at once with
the valid regions or theName#TAGformat. - A connection to Riot that dropped mid-answer was logged as a bug in the
bot. A cut-off response surfaced as a bareTypeErrorrather than a
network error. It is now reported, and handled, like any other network
failure. - Every
query-chatanswer said "(with context: last 1 hours)". The
default one-hour window was named on every answer, private and public,
though nobody chose it, and a follow-up reads what was said since the last
question rather than an hour anyway. The header now names a window only when
the asker typed one, and says "1 hour", not "1 hours". /aufhocker espnff helpsaid the fantasy server wasn't connected when it
was. When the server was up but none of its tools were open to the person
asking, help said "not connected" whileespnff mcpsaid connected. Help
now says the same asespnff mcp, and no longer retries a server that is
already up.- A web page with one bad character code couldn't be read. A numeric
character code past the last valid one, such as�, failed the
whole fetch, so Claude answered without the page. The code is now left as
written and the rest of the page comes through. - When chat history was too long to send in full, part of a multi-line
message could lose its author. If the cut fell inside a message several
lines long, its last lines were kept without the name and time, so Claude
read them as nobody's, or as the next person's. A message is now either
kept whole or left out. - Buttons and menus that hit an error showed only "This interaction
failed". The bot logged the error but never answered the click. The
clicker now gets a private error message, and the message the button sits
on is left alone. - The role sign-up board couldn't be posted with one role for both games,
and the bot blamed a permission. Each button needs its own role, so
Discord refused the board, and the bot told the admin to check its Send
Messages permission. The command now asks for a different role per game up
front, and a post that fails for any other reason no longer blames
permissions unless Discord said so. - Reloading name mappings from a broken file erased every real name. If
user-mappings.jsonhad a typo or was missing,/aufadmin reload-mappings
dropped every mapping, reported success, and the bot fell back to Discord
usernames everywhere. A file that can't be read now leaves the current names
in place, and the command says the reload failed. - The startup log could show part of the database password. A password
holding an@followed by/,?or#(or digits followed by#or
?), pasted intoDATABASE_URLunencoded, confused the address parser, and
pieces of it were logged as the database host, port or name on every boot.
When the address can't be read with certainty, the line now says
(unparseable)and shows nothing from it. - The startup checks said only "fetch failed" when a service couldn't be
reached. The real reason (address not found, connection refused, a bad
certificate) was dropped, so DNS, firewall and TLS problems all looked the
same. The check now names it. Any token or password is removed from the
line first, including one the error text quoted back, which could already
happen before. - A crash caused by an unusual error could skip the clean shutdown or log
no reason. When code threw something that wasn't a normal error, the crash
handler either crashed itself, exiting without closing the database or
Discord, or logged a line with no reason in it. It now logs what was thrown
and always shuts down properly, and normal errors keep their type and cause. - A log line whose text contained a % sign could lose its details,
including the error. The logging library treats%s,%o,%%and the
like as fill-in markers, and a message with one dropped everything logged
with it; a user typing50%offwas enough. The details are now always kept,
and the text is left exactly as written. - A number setting written as
10_000,1e4or30swas quietly cut
short. Only the leading digits were read, soCLAUDE_MAX_TOKENS=10_000
ran as 10 tokens andRATE_LIMIT_INTERVAL_MS=30sas 30 milliseconds, and
the startup check passed both. Number settings must now be plain digits, and
anything else stops the bot at startup with an error naming the setting.
Every shipped config file and the Swarm stack already use plain digits. - The connection that runs migrations ignored
DATABASE_SSL_REJECT_UNAUTHORIZED. WithDATABASE_SSL=true, the bot's own
connection verified the database server's certificate, but the one that
applies schema changes at startup accepted any. Both now use the same SSL
settings. The live stack runs withDATABASE_SSL=false, so it is unaffected. - CI's dependency audit passes again.
handlebars4.7.9 had newly
published critical advisories. It is dev-only, reached through
ts-jest, so it was never in the production image, and 4.7.10, within the
rangets-jestasks for, fixes it.
Changed
D2EMU_POLL_INTERVAL_MSis removed. It looked like it set how often the
D2 tracker polls, but nothing read it: Terror Zones are checked on the
5-minute marks and DClone every 10–30 seconds. A stack that still sets it
gets a startup warning saying it is ignored, so remove it from the stack.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- A LoL result still pending when the bot restarted was never posted.
-
v5.3.5
Stablereleased this
2026-10-10 08:58:37 -05:00 | 3 commits to main since this releaseNine rarer bugs, each needing bad timing or an unusual answer to show up. A
cut-off answer now says so, Make Public and the fantasy Confirm buttons hold
up under slow or failed clicks, a LoL game is posted once and keeps its
result, a long watchlist lists, a stalled Data Dragon no longer holds up
startup, and a new DClone board shows the latest grid.Fixed
- An answer cut off at its length limit was shown as if complete.
Thinking counts toward Claude's token limit, so an answer can stop half
written, or before it starts. A half answer was shown and saved for
follow-ups, a "let me look that up" before a cut-off tool call passed for
the answer, and thinking alone failed as an API error. What was written is
now shown with a note that it was cut off (or a message saying none of it
was), and it isn't saved for follow-ups. - Make Public could post an answer twice. A long post can outlast
Discord's 3 seconds to answer a click, which shows "This interaction
failed" beside a button that still works, and a second click posted the
answer again. A second click while posting now says so. A retry after a
post failed partway also posted its start a second time; it now picks up
at the part that failed. - A fantasy Confirm message could pass 2,000 characters and stop
updating. Each line was clipped, but the message wasn't: five long
changes with five long failures under them went over, and Discord refused
the edit after the change had run, so its outcome never showed. Lines are
now clipped shorter until the message fits. - A Confirm press that Discord didn't take left the change stuck. If
answering the press failed, the change stayed "running" though nothing had
been sent, and every later press said it had already been handled. It now
goes back to waiting, so pressing again runs it. - Two watched players in one LoL game could get it posted twice. Their
detections, a moment apart, both found no row for the game, and the step
meant to reserve it succeeded for both. The game is now claimed with an
insert that only one detection can win. - A game that ended while its ranked data was loading lost its result.
Ranks and recent form can take minutes behind the Riot rate limit, and a
game that ended meanwhile (a remake ends at the next poll) had its result
replaced by the in-progress embed. The late edits now check that the game
is still live first. /aufhocker lol monitor-listfailed past about 30 summoners. The whole
list went in one message, and Discord refuses one over 2,000 characters.
It now continues in follow-up messages.- A stalled Data Dragon could hold up startup for minutes. Its two
requests had no timeout, and every plugin after the LoL one, and the health
endpoint, waited on them. Each now has 10 seconds, and champion names fall
back to IDs, as they already did when Data Dragon was down. - A new DClone status message could show an old grid for hours. A poll
that landed while/aufadmin d2 dclone-boardwas posting couldn't see the
new message to update it, and later polls with the same data skipped it.
It is now brought up to date as soon as it is recorded.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- An answer cut off at its length limit was shown as if complete.
-
v5.3.4
Stablereleased this
2026-10-10 00:34:15 -05:00 | 4 commits to main since this releaseLong answers get time to finish, a deploy no longer fails a Confirm click
partway through, a staleDISCORD_GUILD_IDno longer keeps the bot down, and
a DClone spawn reported as 6/6 stays up for 12 hours.Fixed
- A long answer from Claude could be cut off, and the question failed.
Each API call had 60 seconds, set before the models thought and before
chat ran on Sonnet 5.5. An answer arrives whole once the model has
finished, thinking included, so a hard question at high effort could run
out of time, and the identical request was then sent three more times,
about four minutes, before the question failed. Each call now has 3
minutes, a request that runs out of time is sent once more rather than
three times, and no call runs past 13 minutes from the question, so the
reply or the error still reaches Discord inside its 15-minute window. The
start-up key check keeps its 60 seconds. - A Confirm click during a deploy could report a failure after the change
had gone through. Shutdown closed the fantasy server's MCP sessions
before waiting for interactions in progress, so a Confirm click mid-call
failed, and anespnff mcpquestion mid-loop lost its tools. The sessions
now close after that wait. The LoL and D2 pollers still stop before it. - A stale or mistyped
DISCORD_GUILD_IDkept the bot from starting.
After registering its commands globally, the bot clears that guild's
commands, and a refusal there (a 403 from a guild the bot has left) failed
startup, so the container restarted into the same failure. It now logs a
warning and starts..env.examplenow says what the variable is for: a
guild to clear leftover commands from, not one to register to. - A DClone spawn that the tracker reports as 6/6 was deleted at the
reset. The bot counted a spawn only when a game dropped from a step
straight back to idle. A game reported at 6/6 ("Diablo has invaded
Sanctuary") pinged spawn subscribers under "DClone progress", and the drop
to idle a poll later deleted that post instead of keeping it up for 12
hours. The first arrival at 6/6 now counts as the spawn either way.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- A long answer from Claude could be cut off, and the question failed.
-
v5.3.3
Stablereleased this
2026-10-10 00:13:38 -05:00 | 5 commits to main since this releaseA busy moment at Anthropic or a stalled web page no longer fails a question,
and a LoL game that seemed to end twice still gets its result.Fixed
- One overloaded or server-error answer from Anthropic failed the whole
question. A 529 "overloaded", or a 500, 502 or 503, got no retry. The
SDK's own retries are off because the bot runs its own, and the bot's
matched only rate limits and network errors. Inside a tool loop, the
failure also threw away every round already done. These are now retried
up to 3 times, waiting 1, 2 and 4 seconds, or as long as Anthropic's
Retry-Afterasks, up to a minute. web_fetchcould leave a question hanging with no answer. Its
15-second timeout ended once the headers arrived, so a server that sent
headers and then trickled the body (a live feed, a long-poll endpoint, a
deliberately slow page) held the question open past Discord's 15-minute
window, with no answer and no error. The 15 seconds now cover the body
too, andweb_searchreads Brave's results inside its timeout the same
way.- A LoL game that ended twice could lose its result. A spectator 404
mid-game ends a game falsely and starts a result retry, the next poll
brings the game back, and the real end starts another. The second
orphaned the first, which went on calling Riot every minute for up to half
an hour where shutdown couldn't stop it, then logged every minute until
the next restart. When its attempts ran out it stopped the second retry
instead, so a result Riot published late was never posted. The newer retry
now replaces the older one, and each retry stops only itself.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- One overloaded or server-error answer from Anthropic failed the whole
-
v5.3.2
Stablereleased this
2026-10-09 23:54:54 -05:00 | 6 commits to main since this releaseFixes a query that any user could send to crash the bot.
Fixed
- A query with
```followed by about 2,000 characters without a
space crashed the bot. The message splitter reads everything between the
backticks and the first space or line break as a code block's language
tag, and repeats the tag in every chunk of a long block. A tag close to a
message long left no room for code, so the splitter looped until it ran out
of memory. The reply header quotes the query, so any user could trigger
it, and again after each restart. A block whose tag would take more than
half of each chunk is now split as plain text. Before, a slightly shorter
tag sent one message per character or two of code, and a block that was
all tag was dropped from the reply. Both now split as text too.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- A query with
-
v5.3.1
Stablereleased this
2026-10-09 23:50:26 -05:00 | 7 commits to main since this releaseRate-limit bypasses last as long as they say, OCE and SEA players work with
the LoL monitor, andLOG_LEVELcan no longer switch the logs off.Fixed
- A temporary rate-limit bypass expired as soon as it was granted. Its
end time was stored as aTIMESTAMP, which keeps the bot's local time
(America/Chicago) and drops the zone, then compared againstNOW()in the
database's UTC session. It read as five or six hours earlier than it was,
so any bypass shorter than that was over before it started, and the
longest ran 5–6 hours short. Migration 018 (applied at boot) turns every
TIMESTAMPcolumn intoTIMESTAMPTZ, which stores the instant. - Channel history around the November clock change. Local time runs
1 AM twice that night, and the same zoneless columns sorted messages from
the second 1 AM before those from the first, so a follow-up asked in that
hour missed what was said since the question before it. Fixed by the same
migration. - OCE game results and recent form. Riot serves OCE match data from
sea, but the bot askedamericas, which answers 404, so every OCE game
ended "results unavailable" after half an hour of retries. - Players on PH, SG, TH, TW and VN couldn't be registered. Their account
lookups went tosea, which the account API doesn't serve. They now go to
asia; any of the three account clusters returns any account. LOG_LEVEL=INFO,warning, or any name winston doesn't know silenced
every log line, errors included. winston drops every record at a level it
doesn't know. Case is now ignored,warningreads aswarn, and anything
else logs atinfo, with a startup warning naming the value.- A
LOG_LEVELset in.envor.env.confignever reached the logger
undernpm run devornpm start: the logger read it before those files
were loaded, whileconfig/runtimereported the file's value. Docker, where
the variables arrive in the environment, was unaffected.
Deployment
Migration 018 runs at boot and rewrites the
messagestable once. It reads
the columns the bot writes in the bot's own time zone (the stack'sTZ),
whichMigrationRunnernow passes to every migration, so let the bot apply
it rather than runningnpm run db:migratefrom a machine in another zone.
Rows those columns got before 1.10.0, when the bot ran in UTC, come out 5–6
hours late; only spent bypasses and audit history are that old. Rolling the
image back to 5.3.0 needs no database rollback: 5.3.0 reads and writes
TIMESTAMPTZcorrectly.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- A temporary rate-limit bypass expired as soon as it was granted. Its
-
v5.3.0
Stablereleased this
2026-10-09 12:21:45 -05:00 | 8 commits to main since this releasequery-chatandespnff mcprun on Claude Sonnet 5.5, and LoL lane
detection thinks before it answers.Changed
query-chat,espnff mcpand the start-up key check run on Claude
Sonnet 5.5 instead of Haiku 5.5. Everything else they send is
unchanged: thinking on,higheffort, 10,000 max tokens, a one-hour
prompt cache.CLAUDE_MODELnow defaults toclaude-sonnet-5-5. The
live model is the stack'sCLAUDE_MODEL, so set it to
claude-sonnet-5-5there. 5.2.0 runs fine on Sonnet 5.5 too, so the
stack can go first. The cache belongs to the model, so each
conversation's first follow-up after the switch reads its history at
full price once, then caches again.- LoL lane detection thinks before it assigns lanes. It stays on Haiku
5.5 atmediumeffort. Its token cap goes from 150 to 10,000, because
thinking counts toward it, and a cap that thinking fills leaves no answer
and every lane Unknown.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v5.2.0
Stablereleased this
2026-10-07 22:47:02 -05:00 | 9 commits to main since this releaseFinished walk posts no longer get stuck in the channel.
Added
- A Clone Hunters post that is never struck through goes 24 hours after
its walk. Every post that names a time now goes on the delete list when
it arrives, with the bot's reply under it, due a day after the last time
in it. A post they forget to strike, or strike in a way the bot misreads,
still goes. A strike still wins: struck through, the post goes in 30
minutes as before. If they edit the walk time, the day counts from the
new time. A post with no time in it ("Walk NOW EU") stays until it is
struck, as before.
Fixed
- A walk post edited before its walk now goes once it is struck
through. When Clone Hunters change a post first, crossing out the old
region beside the new one (Region: ~~Asia~~ **Americas**), and strike
the whole thing later, the strike marks pair up wrongly and Discord leaves
Asiauncrossed. That one word made the bot read the post as still live,
so it was never deleted. A post now counts as struck when it opens and
closes with a strike mark, whatever is inside.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- A Clone Hunters post that is never struck through goes 24 hours after