← Back to Marketplace
Disculate banner
Disculate icon

Disculate

Discord plugin
by Paul
v0.0.15 1 install

A discord friendly calculator tool.

Disculate screenshot 1
Disculate screenshot 2
Disculate screenshot 3
Disculate screenshot 4

Commands this plugin adds

/calc /calc-config /calc-help

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 010 decimal places 6
angle_mode rad / deg rad
scientific_threshold 120 (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 / compile on user input. The parser uses stdlib ast.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 through math.pow (so bignums can't OOM the sandbox), and a 200 ms wall-clock budget.
  • Mention injection defeated at two layers: safe_text inserts zero-width spaces between @ and everyone/here, and every response sets allowed_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 →
Screenshot preview