Disculate
A discord friendly calculator tool.
Commands this plugin adds
About this plugin
Disculate
An in-Discord calculator plugin for YourBot. Arithmetic, percentages, common math functions, and constants — without leaving the channel.
You:
/calc expression: (3 + 4) * sqrt(16)Disculate:
(3 + 4) * sqrt(16)= 28
Built for the YourBot platform's sandboxed plugin runtime (yourbot_sdk; the platform was formerly branded "MMO Maid"). Safe-tier capabilities only (interaction:respond, storage:kv). No outbound HTTP, no disk writes, no eval/exec/compile on user input.
Slash commands
| Command | Who | Purpose |
|---|---|---|
/calc expression:<text> [ephemeral:<bool>] |
Anyone | Evaluate a math expression. ephemeral:true hides the result from the channel. |
/calc-config [precision:<int>] [angle_mode:<rad|deg>] [scientific_threshold:<int>] |
Server admin (MANAGE_GUILD) |
Set per-server defaults. Omitted options keep their current value. |
/calc-help |
Anyone | Quick reference for syntax, functions, and constants. |
Supported syntax
Operators: + - * / // ** unary +/- parentheses
Percent: trailing % divides by 100 (e.g. 50% → 0.5, 200 * 5% → 10)
Constants (case-sensitive): pi e tau
Functions:
| Category | Names |
|---|---|
| Basic | abs(x) round(x[, n]) floor(x) ceil(x) min(a, b, ...) max(a, b, ...) mod(a, b) pow(a, b) gcd(a, b) lcm(a, b) trunc(x) hypot(x, y) sign(x) |
| Roots / exp / log | sqrt(x) cbrt(x) exp(x) log(x[, base]) log10(x) log2(x) ln(x) |
Trig (honours angle_mode) |
sin cos tan asin acos atan atan2(y, x) |
| Hyperbolic | sinh(x) cosh(x) tanh(x) asinh(x) acosh(x) atanh(x) |
Examples
| Input | Output |
|---|---|
2 + 2 |
= 4 |
1/3 |
= 0.333333 (precision-dependent) |
200 * 5% |
= 10 |
100 + 10% |
= 100.1 (10% = 0.1, not "10% of 100"; see ARCHITECTURE.md §B) |
sqrt(2) |
= 1.414214 |
sin(30) with angle_mode:deg |
= 0.5 |
mod(-7, 3) |
= 2 (sign-of-divisor) |
(2+1)*7-8 |
= 13 with a Steps field showing the three intermediate computations |
1e308 * 10 |
error: Result is too large to represent. |
2^3 |
error: Use ** for power, not ^ (which would be bitwise XOR). |
5 % 3 |
error: Use mod(a, b) for modulo. Trailing % means percent (e.g. 50% = 0.5). |
sqirt(2) |
error: Unknown function sqirt. Did you mean sqrt? |
Pi + 1 |
error: Unknown name Pi. Constants are case-sensitive. Did you mean pi? |
((1+2) |
error: Unclosed parenthesis. 1 more ( than ). Add 1 ) to balance the expression. |
sqrt(-1) |
error: sqrt of a negative value isn't a real number. Try sqrt(abs(x)) if you want the magnitude. |
Errors aren't generic. Disculate identifies the specific problem in your input — close-typo suggestions for unknown names and functions, paren-balance counts, function-specific domain explanations, operator-mistake hints. Every error embed shows what's wrong on top, how to fix it next, a reason: <code> footer for tracking, and a Show help button that swaps the error for the full syntax reference in place — one message, not two.
/calc-help leads with a Commands overview and adapts to the server: a fresh install (nothing configured yet) sees a numbered Getting started quick-start; a configured server sees its live Server settings. Running /calc-config with no options shows the current settings plus a one-click Switch to degrees / radians button so an admin can fix the most common trig surprise without learning the option syntax.
Configuration
/calc-config adjusts three per-server settings, persisted in plugin KV:
| Setting | Range | Default |
|---|---|---|
precision |
0–10 decimal places |
6 |
angle_mode |
rad / deg |
rad |
scientific_threshold |
1–20 (switch to scientific notation when |result| ≥ 10^N) |
12 |
A per-user cooldown of 2 seconds prevents accidental spam. Errors are always shown ephemerally to the invoker only.
Safety model
- No
eval/exec/compileon user input. The parser uses stdlibast.parse(mode="eval")and a manual node-allowlist walker. The audit gate (tools/run_audit.py) scans for these calls and blocks any from being committed. - Defense-in-depth DoS guards: 120-char input cap, 32-deep AST cap, 200-node AST cap,
**operand guard that routes large operands throughmath.pow(so bignums can't OOM the sandbox), and a 200 ms wall-clock budget. - Mention injection defeated at two layers:
safe_textinserts zero-width spaces between@andeveryone/here, and every response setsallowed_mentions: {"parse": []}. - NFKC normalisation + bidi/control-char rejection on every input before parsing.
See AUDIT-REPORT.md for the full audit trail.
Local development
# Requirements: Python 3.11+ (tested on 3.11–3.14)
# Dev deps (test runner + the SDK whose vendored validator the audit uses):
py -m pip install -r requirements-dev.txt
# Run the test suite (333 tests, ~0.2s)
py -m pytest tests/ -q
# Build the deterministic production bundle (build/disculate.zip)
py tools/build_bundle.py
# Run all 9 audit gates (manifest, imports, blocked_substrings, no_eval_ast,
# todo_markers, plugin_run, pytest, bundle,
# platform_validator)
py tools/run_audit.py
The bundle excludes everything outside an explicit allowlist (see tools/build_bundle.py:INCLUDED_FILES). Tests, tools, documentation, dotfiles, and assets/ never ship — Discord fetches the brand image directly from the GitHub raw URL.
Project layout
disculate/
├── manifest.json ← plugin id, capabilities, slash commands
├── __main__.py ← entry point the platform requires; just imports plugin
├── plugin.py ← handler entry points (must end with plugin.run())
├── assets/
│ └── disculate.webp ← brand thumbnail, referenced by Discord via GitHub raw URL
├── lib/
│ ├── parser.py ← cleaning, percent preprocessing, ast.parse + allowlist walker
│ ├── walker.py ← AST walk, DoS guards, wall-clock budget, step trace
│ ├── functions.py ← FunctionSpec registry (one entry per math function)
│ ├── format.py ← result formatting (precision, scientific, int preservation)
│ ├── config.py ← per-server KV config + schema versioning
│ ├── embed.py ← response builders + safe_text/clip/help generation
│ ├── diagnostics.py ← per-reason error explainer + did-you-mean
│ ├── reasons.py ← reason codes + user-facing hints
│ └── logctx.py ← request_id ContextVar for log correlation
├── tests/ ← 333 tests: smoke, unit, handler, failure-injection, adversarial,
│ diagnostics, bundle/platform contract, drift guards, components
├── tools/
│ ├── build_bundle.py ← deterministic zip with allowlist guard
│ ├── run_audit.py ← 9 audit gates (incl. marketplace-substring mirror)
│ └── validate_artifact.py ← runs the platform's vendored upload validator on the zip
└── docs (CLAUDE.md, AUDIT-REPORT.md, ARCHITECTURE.md, RUNBOOK.md, SDK-ASSUMPTIONS.md, CHANGELOG.md)
Documentation
| Document | Audience |
|---|---|
| README.md | Users + first-time contributors. You are here. |
| CLAUDE.md | The next maintainer (or LLM). TL;DR, recipes, things-that-look-wrong-but-aren't. |
| ARCHITECTURE.md | Why each design decision beat its alternatives. |
| AUDIT-REPORT.md | Full audit trail across R1, R2, R3, and the math-shape Tailored round. |
| SDK-ASSUMPTIONS.md | Every defensive try/except that exists because the SDK doc was incomplete — the post-deploy probe list. |
| RUNBOOK.md | Operational scenarios: OOM, KV quota, schema migration, rollback. |
| CHANGELOG.md | Per-version Added / Changed / Fixed. |
Contributing
The single-paragraph recipe for "add a math function" is in CLAUDE.md → Common task recipes. The audit gates in tools/run_audit.py are the merge bar: if py tools/run_audit.py exits non-zero, the change isn't shippable.
Before opening a PR:
py -m pytest tests/ -q # all green
py tools/run_audit.py # all 9 gates pass
License
This repository ships without a LICENSE file. Under default GitHub / copyright terms that means all rights reserved to the author — the source is publicly readable, but redistribution, derivative works, and commercial use are not granted. A formal open-source license may be added later; until then, please open an issue if you'd like to use any of this code outside the context of forking + contributing back.
Have a question?
Ask the developer of Disculate directly. Sign in with Discord to send your question. You'll be notified when they reply.
Sign in to ask the developer →Ready to install Disculate?
Sign in with Discord, set up your server and add it in minutes.
Install on your server →