Alisa V Zadzerkalli
C++

Alisa V Zadzerkalli

Alisa v Zadzerkalli - a Discord bot written in C++

GitHub|
image

Alisa v Zadzerkalli (Ukrainian) - Alice Through the Looking-Glass. The world
on the far side of the mirror, where you go on purpose and everything is
reflected, backwards, and wrong.

«Ніколи не вгадаєш, ким станеш за хвилину.»
"You can never guess who you'll become in a minute." - Alice, in the
Аліса в Країні Див, trans. Valentyn Korniienko, ed. Ivan Malkovych p. 53.

A Discord bot written in C++.

Capabilities

Slash commands

  • /ping - round-trip latency against the gateway
  • /leaderboard - the Clube de Programação (UFFS) standings, scraped from the
    club's vjudge group and ranked by problems solved

Conversation

Mention her, or say alisa or obliterator in a message, and she answers. The
reply is not generated locally. Alisa assembles the context and Providence
does the thinking.

What she puts in front of the model on every message:

  • every club member, in leaderboard order, with their handle, solved count
    and last-active time
  • the full list of problems each member has solved, per judge
  • the ten problems solved most often across the whole club

That context is what lets her answer questions nobody wrote a command for.
Who is in the top three, what problems they have in common, who has gone
quiet. The model supplies the reasoning, the scrape supplies the facts.

Club data

Member state is pulled from vjudge at startup by a bounded pool of eight
workers, each holding one connection for its share of the group. Every member
costs two requests, the profile page for counts and rank, and the solve detail
endpoint for the problem list.

Layout

  • main.cpp - entry point
  • src/commands/ - slash commands and the dispatcher
  • src/members/ - club roster, profile and solve-list scraping
  • src/parser/ - HTML querying over lexbor
  • src/network/ - HTTP with connection reuse
  • src/speech/ - the Providence link and prompt assembly
  • src/onmessage/, src/onready/ - gateway event handlers
  • src/embeds/ - the house embed
  • src/config/ - cluster setup and shared bot state

How a reply happens

The message handler wakes on every message in every channel the bot can see.
It drops bots, drops anything that does not mention her by ping or by the words
alisa or obliterator, and right now it also drops every author but one, an id
hardcoded in src/onmessage. Whatever survives gets an immediate embed reading
Thinking..., posted before the model is asked anything, so the channel sees her
take the question even when the answer is slow.

The answer itself is a POST to Providence at http://127.0.0.1:8931 on
/speech/deepthink/, carrying the message, the whole club context, and the
Discord username as the caller identity. It waits up to 200 seconds. The reply
is trimmed to 1990 bytes on a UTF-8 character boundary so a cut never lands
mid-codepoint, and then the placeholder embed is edited in place. A timeout, a
refusal or an unparseable body all end as text in the same embed rather than
silence.

Providence is a separate service and is not in this repository. Without it
running on that port the bot still starts, still answers slash commands, and
still scrapes the club, it only stops thinking.

What Providence adds

Alisa sends light true, which is the cheap path, one reasoning node and no
branching. Drop that flag and the same endpoint runs a bounded tree of thoughts
instead, depth two, at least two labelled branches per node, each one scored for
probability of success and for how much potential it adds or destroys, and each
node generated by a native thinker spawned as a subprocess. That machinery is
sitting there unused on every message she sends.

It also decides for itself whether the question needs more than talk. If it
does, a classifier maps the message to one command and its argument, runs it,
and folds the result into the final answer. What is on that list right now:

  • plotting, a 2D curve or a 3D surface from a function you describe in words
  • algebra, simplify, expand, factor, solve for a variable
  • calculus, derivatives, definite and indefinite integrals, limits
  • matrices, determinant, inverse, eigenvalues
  • rendering any expression as LaTeX
  • the UFFS SIGAA portal, current disciplines, schedule, upcoming assignments
  • the UFFS restaurant, which meals are open for reservation and how past ones
    went, driven through a real headless browser with its own credentials

Every one of those answers carries structured fields beside the prose. A plot or
a LaTeX render comes back as a base64 PNG under image, a portal read comes back
as JSON under data, and the model's own commentary sits alongside under
response. Alisa reads response and throws the rest away, so today a question
that produces a graph reaches Discord as a sentence about a graph nobody can
see. Sending the image as an attachment is the obvious next thing this bot
should learn.

Access is per caller, not per bot. She identifies herself as discord and the
author's username, and Providence looks that pair up and refuses anyone it does
not know, which is the refusal she prints verbatim. Adding a club member means
adding them there, not here.

Building

Needs a C++20 compiler, CMake 3.16 or newer, and three libraries, DPP for the
gateway, cpr for HTTP, lexbor for HTML. On Arch the Makefile installs whichever
are missing through yay or paru. Anywhere else, install them yourself first.

make deps      fetch DPP, cpr and lexbor if absent
make           configure and build into build/
make run       load .env, then run the binary
make rebuild   wipe build/ and start over

Two things in the build are deliberate. The default build type is
RelWithDebInfo, and parallel compile jobs are capped at two. DPP's headers are
template-heavy enough that an unoptimised build costs several gigabytes per
compiler process, and one process per core will take the machine down with it.
Give it more when you have the memory, make JOBS=8.

Sources are globbed with CONFIGURE_DEPENDS, so a new file under src/ is picked
up on the next build without touching CMakeLists.

Configuration

The bot reads DISCORD_TOKEN from the environment and nothing else. Put it in
.env at the repository root and make run will export it for you, or export it
yourself and run build/alisa-v-zadzerkalli_core directly. She connects with the
default intents plus message content, which is a privileged intent, so enable
it on the application in the Discord developer portal or the message handler
will only ever see empty strings.

Providence keys live alongside it in .env for the service on the other end of
the socket. This binary never reads them.