Files
FunguyBot/README.md
T

25 KiB
Raw Blame History

🍄 Funguy Bot

A modular, security-focused Matrix chat bot built with simplematrixbotlib, written in Python. Features a plugin architecture, in-process cron scheduling, per-room plugin management, rate limiting, and a full suite of recon, encoding, and utility commands.

Written during recovery from cervical spinal surgery (CDA Cervical Discectomy and Disc Arthroplasty). Expect bugs — please report them!


📋 Table of Contents


Features

  • Modular plugin system each command lives in its own plugins/*.py file; add or remove features without touching core code
  • Runtime plugin management load, unload, enable, or disable plugins per-room with no restart required
  • In-process cron scheduler schedule any bot command to fire automatically (APScheduler + SQLite, no system crontab needed)
  • Rate limiting non-admin users are capped at 3 commands per 5-second window; admins are always exempt
  • SSRF protection all outbound network plugins validate that targets resolve to public IPs only
  • Admin/moderator access control a dedicated admin_user is set in config; moderation commands require power level ≥ 50
  • Live configuration settings can be changed at runtime with !set/!saveconf without restarting

📦 Requirements

  • Python 3.9+
  • A Matrix homeserver account for the bot
  • fortune package installed on the host (sudo apt install fortune) if using fortune.py
  • dict / WordNet installed on the host (sudo apt install dict dict-wn) if using dictionary.py
  • Optional API keys for certain plugins (see Configuration)

🚀 Installation

1. Clone the repository

git clone https://gitlab.com/Eggzy/funguybot.git
cd funguybot

2. Create and activate a Python virtual environment

python3 -m venv venv
source venv/bin/activate

3. Install dependencies

pip3 install -r requirements.txt

4. (Optional) Install Playwright for the Goodreads quote plugin (quote.py)

pip3 install playwright beautifulsoup4 lxml
playwright install chromium

5. Configure the bot — create your .env and funguy.conf files (see Configuration)

6. Set up and start the systemd service (see Running the Bot)


⚙️ Configuration

Environment Variables (.env)

Create a .env file in the bot's root directory. Only the three Matrix variables are required; all API keys are optional.

# ── Required ──────────────────────────────────────────────────────────
MATRIX_URL="https://matrix.org"        # Your homeserver URL
MATRIX_USER="@yourbot:matrix.org"      # Bot's Matrix ID
MATRIX_PASS="your_password"            # Bot's password

# ── Logging (optional, default: INFO) ─────────────────────────────────
LOG_LEVEL=INFO    # DEBUG | INFO | WARNING | ERROR | CRITICAL

# ── Plugin API Keys (all optional) ────────────────────────────────────
OPENWEATHER_API_KEY=        # weather.py
SHODAN_KEY=                 # shodan.py
DNSDUMPSTER_KEY=            # dnsdumpster.py
LASTFM_API_KEY=             # lastfm.py
YOUTUBE_API_KEY=            # youtube-search.py
INFERMATIC_API=             # infermatic-text.py
INFERMATIC_MODEL=           # infermatic-text.py  model name string
OMDB_API_KEY=               # imdb.py
GNEWS_API_KEY=              # news.py

# ── SMTP (optional, for notification plugins) ─────────────────────────
SMTP_SERVER=mail.example.com
SMTP_PORT=465
SMTP_USER=bot@example.com
SMTP_PASSWORD=yourpassword

Bot Configuration (funguy.conf)

Create funguy.conf in the bot's root directory. This is a TOML file consumed by simplematrixbotlib and the config.py plugin.

[simplematrixbotlib.config]
admin_user                = "@youradmin:matrix.org"   # Full Matrix ID of the admin
prefix                    = "!"                        # Single-character command prefix
join_on_invite            = true
encryption_enabled        = false
emoji_verify              = false
ignore_unverified_devices = true
store_path                = "./store/"

# Pre-disable plugins per room (optional)
[plugins.disabled]
# "!yourroom:matrix.org" = ["pluginname1", "pluginname2"]

All writable options can also be changed live by the admin using !set (see config.py).


🤖 Running the Bot

Create /etc/systemd/system/funguybot.service, replacing the $variables with your actual paths and user:

[Unit]
Description=Funguy Bot Service
After=network.target

[Service]
Type=simple
User=$user
Group=$group
WorkingDirectory=$working_directory
ExecStart=$working_directory/start-funguy.sh
Restart=on-failure
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=funguybot

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable funguybot
systemctl start funguybot

Direct Launch

source venv/bin/activate
python3 funguy.py

🔑 Core Admin Commands

These commands are handled by funguy.py itself and require admin_user privileges.

Command Description
!reload Reload all plugins from disk (no restart needed)
!load <plugin> Dynamically load a single plugin by filename (without .py)
!unload <plugin> Dynamically unload a single plugin
!enable <plugin> Re-enable a plugin in the current room
!disable <plugin> Disable a plugin in the current room (persisted to funguy.conf)
!restart Gracefully stop the bot process (systemd will restart it automatically)
!rehash Reload funguy.conf and the disabled-plugin list without restarting

🧩 Plugin Reference

All commands use the prefix defined in funguy.conf (default !). Plugin filenames correspond to the names used with !load / !unload / !disable / !enable.


🛡️ admin.py Room Moderation

Full moderator toolkit with multi-word display name resolution. Requires power level ≥ 50 or admin_user. The bot automatically resolves multi-word display names and prompts with a numbered disambiguation list when names are ambiguous.

Command Description
!kick <name|@user> [reason] Kick a user from the room
!ban <name|@user> [reason] Ban a user
!unban <@user:domain> Unban a user (full MXID required)
!invite <name|@user> Invite a user to the room
!op <name|@user> [power_level] Promote a user (max 50 / moderator level)
!deop <name|@user> Demote a user to power level 0
!userinfo <name|@user> Show display name and power level
!topic [new topic] View or set the room topic
!roomname [new name] View or set the room name
!avatar [mxc://…] View or set the room avatar (must be an mxc:// URL)
!members List all joined members with power levels
!bans List all banned users
!modhelp Show moderator command reference

!admin <action> also works as an explicit parent command for all of the above.


Command Description
!arxiv <query> Search papers with abstracts
!arxiv list <query> Search titles only (no abstracts)
!arxiv category <cat> Browse recent papers by category
!arxiv recent [category] Papers from the last 7 days
!arxiv random Random paper
!arxiv <id> Fetch paper by arXiv ID (e.g. 2101.00101)

Categories: ai, ml, security, crypto, cv, nlp, math, physics, quantum, bio, software


₿ bitcoin.py Bitcoin Price

!btc

Fetches the latest BTC/USD price from bitcointicker.co (Bitstamp feed, 60-second intervals). No API key required.


⚙️ config.py Live Configuration (Admin Only)

Manage bot settings at runtime. Changes are in-memory until you run !saveconf.

Command Description
!set <option> <value> Change a configuration option
!get <option> View the current value of one option
!show Display all current configuration values
!saveconf Write current settings to funguy.conf
!loadconf Reload settings from funguy.conf
!reset Reset all options to defaults (preserves admin_user)
!config help Show config command help

Configurable options: prefix, timeout, join_on_invite, encryption_enabled, emoji_verify, ignore_unverified_devices, store_path, allowlist, blocklist

admin_user is read-only via !set — edit funguy.conf directly to change it.


🕐 cron.py Scheduled Commands (Admin Only)

In-process cron scheduler backed by APScheduler and SQLite (cron_jobs.db). Room context is automatically derived from where the command is issued — no room ID needed.

Command Description
!cron add <cron_expr> <command> [tz=IANA] Schedule a command in the current room
!cron remove <job_id> Delete a job
!cron list List jobs in the current room
!cron list * List all jobs across all rooms
!cron enable <job_id> Re-enable a paused job
!cron disable <job_id> Pause a job without deleting it
!cron clear Remove all jobs from the current room

Cron expression format: 5 fields — minute hour day-of-month month day-of-week

!cron add 0 8 * * * !weather London tz=Europe/London

Timezone defaults to UTC. Use any IANA zone name (e.g. tz=America/New_York).


📅 date.py Date & Time

!date

Displays the current day of the week, ordinal date (e.g. "the 9th"), and 12-hour time. No API key required.


No API key required. Uses the ddgs library.

Command Description
!ddg <query> Top web result snippet (collapsible)
!ddg search <query> 5 web results
!ddg image <query> 3 image results
!ddg news <query> 3 news articles
!ddg video <query> 3 video results
!ddg bang <!bang query> DuckDuckGo bang redirect
!ddg define <word> Word definition
!ddg calc <expression> Calculator
!ddg weather [location] Weather snippet

📖 dictionary.py Word Definitions

!define <word>

Looks up definitions using the system dict command against WordNet (with fallback to all installed dictionaries). Requires the dict and dict-wn packages on the host (sudo apt install dict dict-wn).


🌐 dns.py DNS Reconnaissance (SSRF-safe)

!dns <domain>

Queries A, AAAA, MX, NS, TXT, CNAME, SOA, SRV, and PTR records and outputs a clean, emoji-aligned table. Private/internal IP targets are blocked.


🗺️ dnsdumpster.py DNSDumpster Recon

Requires DNSDUMPSTER_KEY in .env.

Command Description
!dnsdumpster <domain> Full DNS mapping via DNSDumpster API
!dnsdumpster test Test API connection against google.com

🔧 encode.py CyberChef-Style Toolkit

A fully offline, CyberChef-like data manipulation plugin with dozens of operations across encoding, cryptography, compression, data processing, forensics, and networking.

!encode <operation> [arguments] <data>
!encode help <op>        # Detailed help for a specific operation

Encoding

Operation Example
base64 encode|decode !encode base64 encode Hello World
base32 encode|decode !encode base32 encode Hello
hex encode|decode !encode hex encode Secret
url encode|decode !encode url encode https://example.com/a b
html encode|decode !encode html encode "<script>"
unicode encode|decode !encode unicode encode café
binary encode|decode !encode binary encode Hi
rot13 !encode rot13 Uryyb Jbeyq
morse encode|decode !encode morse encode SOS

Cryptography

Operation Example
xor <key_hex> !encode xor 41 Hello
aes encrypt|decrypt <key_hex> <iv_hex> AES-CBC
chacha20 encrypt|decrypt <key_hex> <nonce_hex> ChaCha20
rsa encrypt|decrypt <PEM_key> RSA-OAEP
md5 / sha1 / sha256 / sha512 !encode sha256 hello
sha3-256 / sha3-512 !encode sha3-256 hello
hmac <algo> <key_hex> !encode hmac sha256 6b6579 message
bcrypt hash <rounds> / bcrypt verify !encode bcrypt hash 12 mypassword
argon2 hash <params> / argon2 verify PHC format
pbkdf2 <salt_hex> <iters> <keylen> <algo> !encode pbkdf2 aabbccdd 100000 32 sha256 pass

Compression

gzip, zlib, bzip2, lzma — each supports compress and decompress subcommands.

Data Processing

Operation Example
json format|validate !encode json format '{"key":"value"}'
xml format !encode xml format "<root><a>1</a></root>"
yaml format|tojson !encode yaml format "key: value"
csv Parse CSV (first row as header)
asn1 Parse ASN.1 DER (base64 input)
pemder topem|toder PEM ↔ DER conversion

Forensics

Operation Description
entropy Shannon entropy of input
ioc Extract IPs, domains, URLs, emails, hashes
strings Extract ASCII strings (≥4 chars) from hex data
filemagic Detect file type by magic bytes
base64blob Find embedded Base64 blobs in text
xbrute XOR single-byte brute force
yara Scan with a YARA rule (rule as base64)
peinfo PE header analysis (hex input)

Networking

Operation Example
cidr !encode cidr 192.168.1.0/24
ipconv hex|dec|binary !encode ipconv hex 192.168.1.1
urlparse !encode urlparse https://user:pass@example.com:8080/path?q=1
dns !encode dns example.com

Recipes — chain multiple operations:

!encode recipe list
!encode recipe run '{"steps":[{"op":"base64","args":["encode"]},{"op":"hex","args":["encode"]}]}' "hello world"

!exploitdb <search term> [max_results]

Searches the Exploit-DB CSV export from GitLab. Returns up to 10 results (default 5) with title, EDB-ID, type, platform, author, and a direct link. No API key required.


🃏 fortune.py Random Fortune

!fortune

Runs /usr/games/fortune on the host and sends the result to the room. Requires the fortune package (sudo apt install fortune).


📍 geo.py IP / Domain Geolocation

!geo <ip_address or domain>

Geolocates an IP or domain using ip-api.com (primary) with ipapi.co as fallback. Shows country, city, region, postal code, coordinates, timezone, ISP, organization, and ASN. Private IP targets are blocked.


📰 hackernews.py Hacker News

No API key required. Uses the official Firebase HN API and Algolia search.

Command Description
!hn Top 5 stories (default)
!hn top|new|best|ask|show|job Stories by type
!hn story <id> Full story details
!hn comments <id> Top comments for a story
!hn search <query> Full-text search via Algolia

Append a number to set the result count: !hn new 10


🔐 hashid.py Hash Type Identifier

!hashid <hash>

Identifies 100+ hash formats including MD5, SHA family, bcrypt, Argon2, yescrypt, scrypt, PBKDF2, NTLM, NetNTLMv2, LM, LDAP, Oracle, MSSQL, MySQL, phpBB3, WordPress, Drupal, and more. Displays Hashcat mode (-m) and John the Ripper format (--format) for each match, sorted by confidence.


🔒 headers.py HTTP Security Header Analysis

!headers <url>

Fetches a URL and analyzes its HTTP security headers. Outputs a security score (0100) plus a structured breakdown covering HSTS, CSP, X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy, SSL certificate details, and actionable recommendations. Private/internal addresses are blocked.


help.py Dynamic Help

!help

Aggregates and displays the __help__ metadata from every currently loaded plugin in a single collapsible message.


🎬 imdb.py IMDb / OMDb Lookup

Requires OMDB_API_KEY in .env.

Command Description
!imdb <title> Full details + poster image
!imdb id <tt1234567> Lookup by IMDb ID
!imdb search <query> Search titles
!imdb episode <series> -s N -e N Episode info

Optional flags: -y <year>, -t movie|series|episode, --short-plot


🤖 infermatic-text.py AI Text Generation

Requires INFERMATIC_API (and optionally INFERMATIC_MODEL) in .env.

Command Description
!text <prompt> Generate text with the default model
!text --list-models List available models
!text --use-model <model> <prompt> Use a specific model

Optional flags: --temperature <0.01.0> (default 0.9), --max-tokens <N> (default 512)


🌐 isup.py Site Availability Check

!isup <domain or IP>

Performs DNS resolution then checks HTTP/HTTPS availability, reporting the status code and response time.


😂 joke.py Random Jokes

!joke              # General joke
!joke programming  # Programming joke

Fetches jokes from the Official Joke API. No API key required.


karma.py Room Karma Tracking

Karma is tracked by display name — Matrix IDs (@user:server) are not accepted.

Command Description
!karma <user> Show karma points for a user
!karma++ <user> / !-- <user> Give or remove karma
!karma top [n] / !karma bottom [n] Leaderboard (top or bottom N)
!karma rank <user> Show a user's rank
!karma stats Room-wide statistics
!karma history <user> Recent votes for a user

Shortcuts: !++ user, !-- user, or inline username++ / username-- anywhere in a message.


🎵 lastfm.py Last.fm Music Stats

Requires LASTFM_API_KEY in .env. Run !lastfm for the full command list. Outputs neatly aligned code blocks.


📡 news.py News Headlines

Requires GNEWS_API_KEY in .env.

Command Description
!news Top headlines
!news top|world|tech|business|science|health|sports|crypto Category headlines
!news search <query> Search news

Append a number to set result count: !news tech 8


🔌 plugins.py List Loaded Plugins

!plugins

Displays the total count of loaded plugins and a collapsible list with each plugin's name and description.


🔗 proxy.py SOCKS5 Proxy Finder

!proxy

Fetches a public proxy list, tests each candidate for connectivity, and returns a random working SOCKS5 proxy with its measured latency.


💬 quote.py Goodreads Quotes

!quote random
!quote <author>

Scrapes Goodreads using Playwright (headless Chromium). Requires playwright, beautifulsoup4, and lxml to be installed (see Installation).


📊 roomstats.py Per-User Room Statistics

Command Description
!roomstats Aggregate room stats + top 10 users
!rank <stat> Top 10 users by a specific statistic
!stats [name] Show statistics for a specific user

🔎 shodan.py Shodan Reconnaissance

Requires SHODAN_KEY in .env.

Command Description
!shodan ip <ip> IP info with open ports and services
!shodan search <query> Search internet-connected devices
!shodan host <domain> Host and subdomain enumeration
!shodan count <query> Result count for a query

🔐 sslscan.py SSL/TLS Security Scanner

!sslscan <domain[:port]>

Tests SSL/TLS protocol support, cipher suites, certificate validity, and known vulnerabilities. Provides a security score (0100) and actionable recommendations. Defaults to port 443.


🎨 stable-diffusion.py Image Generation

Requires a locally running Stable Diffusion API (e.g. AUTOMATIC1111 or ComfyUI).

!sd [options] <prompt>
Flag Description
--steps N Sampling steps (default 4)
--cfg <scale> CFG scale (default 2)
--h H --w W Image height / width in pixels (default 512×512)
--neg <prompt> Negative prompt
--sampler <name> Sampler name (default DPM++ SDE)
--seed <N> Deterministic seed

LORAs can be embedded directly in the prompt: <lora:filename:weight>


🌍 subdomains.py Subdomain Enumeration

!subdomains <domain>

Discovers subdomains using SSL certificate transparency logs via the CertSpotter API. No API key required.


🧮 subnet.py Subnet Calculator

Command Description
!subnet info <CIDR> Detailed network info (network, broadcast, hosts, mask, etc.)
!subnet split <CIDR> --prefix <N> Split into smaller subnets by new prefix length
!subnet split <CIDR> --diff <N> Split by prefix delta
!subnet adjacent <CIDR> <count> Show current and adjacent networks

Supports both IPv4 and IPv6. RFC 3021 /31 and /32 networks are handled correctly (both addresses listed as usable).


💻 sysinfo.py System Information

!sysinfo

Displays CPU usage and model, RAM, disk, network I/O, GPU info, temperature sensors, and top processes in a clean, emoji-aligned code block.


🕒 timezone.py World Clock

!time <city>              # Any city worldwide (geocoded)
!time <IANA zone>         # e.g. Europe/London, Asia/Karachi
!time help                # Show help

Examples: !time Lahore, !time New York, !time America/Chicago

No city names are hardcoded. IANA zones resolve completely offline; city name lookup uses free geocoding.


📚 urbandictionary.py Urban Dictionary

!ud <term>

Fetches top definitions from Urban Dictionary. No API key required.


☁️ weather.py Current Weather

!weather <location>

Shows temperature, feels-like, conditions, humidity, wind speed, and more in a clean aligned table. Uses OpenWeatherMap as the primary source (requires OPENWEATHER_API_KEY) with Open-Meteo as a free fallback.


👋 welcome.py Room Welcome Message

!welcome

Manually triggers the room's configured welcome message for the requesting user.


🔍 whois.py WHOIS Lookup

!whois <domain or IP>

Returns registrar, creation/expiry dates, nameservers, and registrant info in a clean aligned table.


📖 wikipedia.py Wikipedia Summary

!wp <search term>

Returns the lead section and main image of a Wikipedia article using the MediaWiki API. No scraping, no API key required.


🗂️ xkcd.py xkcd Comics

!xkcd             # Random comic
!xkcd <number>    # Specific comic (e.g. !xkcd 538)

Fetches comics directly from the xkcd JSON API. No API key required.


Requires YOUTUBE_API_KEY in .env.

!yt <search query>

Returns the top video results from YouTube with titles, channel names, and direct links.


Rate Limiting

Non-admin users are limited to 3 commands per 5 seconds. Exceeding this limit returns:

You're sending commands too quickly. Please wait a few seconds.

The admin_user defined in funguy.conf is always exempt from rate limiting.


🔐 Security Notes

  • All plugins that make outbound HTTP requests (geo, dns, headers, sslscan, etc.) validate that the target resolves to a public IP address before connecting. Private, loopback, link-local, and reserved ranges are blocked.
  • The admin_user config field is read-only via !set — it can only be changed by editing funguy.conf directly, preventing privilege escalation through chat commands.
  • Moderator commands (!kick, !ban, !op, etc.) require Matrix power level ≥ 50 in the room, independent of the global admin setting.
  • The encode.py plugin operates entirely offline — no data is ever sent to external services.

💬 Support

Join the community Matrix room for help, bug reports, and discussion:

Self-hosting | Security | Sysadmin | Homelab | Programming


🧙 Credits

Creator & Developer: HB (@hashborgir:mozilla.org)

🍄 Funguy Bot — built during recovery from cervical spinal surgery. Bugs are features in disguise.