A handful of related Telegram-bot improvements that piled up in one
working session.
Slash menu: /start now brings the whole stack up (was: panel render —
duplicated by /menu) and a new /stop takes it down. /up and /down kept
as undocumented aliases. /clear wipes the current chat's rows from the
messages table — DM-self for users, admin-only in groups — so Redstone's
recentTurns history can be reset on demand.
Sticker auto-collection: every incoming sticker is upserted into a new
sticker_library table (file_id PK, plus emoji, set_name, first_seen,
last_seen, seen_count). No /setsticker needed. The original event-keyed
stickers table is untouched and still drives the bot's own event
triggers (maybeSendSticker("up") etc).
Emoji → sticker swap (replaces the AI-driven sticker tool): instead of
listing 15+ file_ids in the system prompt and exposing tg_send_sticker
as a function call, the prompt now lists just the *emoji palette* the
library knows about. After Redstone replies, pickStickerForReply()
scans the text for the first palette-emoji that appears and fires the
matching sticker as a follow-up via ctx.replyWithSticker — without
inserting a row into the messages table, so the audit log stays
text-only and Redstone's next recentTurns isn't polluted. tg_send_sticker
and tg_stickers_known are filtered out of Redstone's geminiFunctionDeclarations
(still exposed via the MCP server for Claude / external clients).
Reliability: Gemini call timeout bumped from 25s to 45s after a real
timeout in production with the previous (too-large) sticker prompt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Redstone previously only saw the 23 tg_* tools — it had no idea the
Minecraft stack existed, so questions like "is the server up?" went
unanswered. This change extracts the 15 Minecraft tools (lifecycle,
rcon, backup, players, seen) into the same shared catalog the Telegram
tools already used, so both Gemini and external MCP clients see them.
mcp/lib/types.ts (new) holds the shared shape: Tool<P>, ToolCtx,
createTgClient, createMcRuntime (a Bun.spawn-based wrapper for docker
compose / docker exec), and toolToGeminiFunction (zod → Gemini schema,
now also stripping exclusiveMinimum/Maximum since Gemini rejects them).
mcp/lib/minecraft-tools.ts (new) is the catalog itself. Eight handlers
are flagged requiresAdmin: rcon, backup, and all destructive player_*
writes plus seen_list. mcp/server.ts trusts the caller (Claude / Paul
on the host) and ignores the flag; bot/bot.ts honours it at dispatch
time, returning {error: "...requires admin role..."} to Gemini so it
can explain to the user instead of attempting the call.
mcp/server.ts shrinks from 423 lines to 70 — a single loop over both
catalogs replaces the hand-rolled registrations.
bot/bot.ts wires both catalogs into the function declarations and adds
the admin gate. It also gains a defensive re-check on every incoming
group/DM text: the Redstone handler now does its own lookup of
ctx.from.id against the users table and refuses to reply unless
status='active'. This is belt-and-braces — the auth middleware already
short-circuits unauthorized callers earlier in the chain, but with
privacy-mode-off groups now feeding every message through, a future
refactor that reorders middleware shouldn't be able to make Redstone
respond to strangers.
Drops thinkingConfig from the Gemini call (gemini-2.5-flash-lite
rejects it outright with HTTP 400).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops two new capabilities on top of the existing Telegram bot + Minecraft
stack, both built around a shared toolkit so there's one source of truth
for the actions either side can take.
mcp/ — a Bun-native MCP server exposing 38 tools to MCP clients (Claude
Code, etc.): server lifecycle, rcon, backup, player/db CRUD, plus 23
Telegram Bot API methods (messaging, reactions, polls, dice, photos,
stickers, pins, forum topics, chat config). Runs over stdio.
mcp/lib/telegram-tools.ts — the Telegram tool catalog as Zod-typed
handlers. Imported by both mcp/server.ts (registers each as an MCP tool)
and bot/bot.ts (exposes each as a Gemini function declaration), so adding
a tool in one place lights it up everywhere.
bot/bot.ts — replaces the silent-on-unknown-text behaviour with Redstone,
an in-bot persona driven by gemini-2.5-flash-lite with native function
calling. In DMs it always responds; in groups only when @-mentioned or
replied to. The tool-use loop (max 4 rounds) lets it decide to send a
poll, react with an emoji, roll dice, etc. via the shared handlers rather
than just text. Thinking budget zeroed and system prompt locked down so
the model doesn't leak its reasoning into replies.
docker-compose.yml — adds google_key as a docker secret and passes
GEMINI_API_KEY_FILE + GEMINI_MODEL to the bot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the archive is produced, tag the current HEAD as backup-<stamp>,
create a Gitea release, and attach the tarball. Token is read from
$GITEA_TOKEN or ~/.gitea_token; upload is skipped if neither is set.
When the minecraft container is not running, archive data/world
directly from the host instead of failing — the files are already
consistent on disk with no process writing to them.
Stream tar from inside the container to the host, so all server
interaction (rcon + archive) happens through docker exec. Requires
the container to be running; fails fast otherwise.
Flushes world via rcon (save-off + save-all flush) before tarring
when the server is running; skips the flush and archives as-is when
the container is down. Outputs timestamped archives to backups/,
which is gitignored.
Tracks docker-compose config, server scripts, and server properties.
Runtime data (world, libraries, versions, logs, jar), credentials, and
backup snapshots are gitignored.