865 lines
26 KiB
Markdown
865 lines
26 KiB
Markdown
# 🍄 Funguy Bot
|
||
|
||
A modular, security-focused Matrix chat bot built with [`simplematrixbotlib`](https://simple-matrix-bot-lib.readthedocs.io/), 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](#-features)
|
||
- [Requirements](#-requirements)
|
||
- [Installation](#-installation)
|
||
- [Configuration](#-configuration)
|
||
- [Running the Bot](#-running-the-bot)
|
||
- [Core Admin Commands](#-core-admin-commands)
|
||
- [Plugin Reference](#-plugin-reference)
|
||
- [Rate Limiting](#-rate-limiting)
|
||
- [Security Notes](#-security-notes)
|
||
- [Support](#-support)
|
||
- [Credits](#-credits)
|
||
|
||
---
|
||
|
||
## ✨ 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](#-configuration))
|
||
|
||
---
|
||
|
||
## 🚀 Installation
|
||
|
||
**1. Clone the repository**
|
||
|
||
```bash
|
||
git clone https://gitlab.com/Eggzy/funguybot.git
|
||
cd funguybot
|
||
```
|
||
|
||
**2. Create and activate a Python virtual environment**
|
||
|
||
```bash
|
||
python3 -m venv venv
|
||
source venv/bin/activate
|
||
```
|
||
|
||
**3. Install dependencies**
|
||
|
||
```bash
|
||
pip3 install -r requirements.txt
|
||
```
|
||
|
||
**4. (Optional) Install Playwright for the Goodreads quote plugin (`quote.py`)**
|
||
|
||
```bash
|
||
pip3 install playwright beautifulsoup4 lxml
|
||
playwright install chromium
|
||
```
|
||
|
||
**5. Configure the bot** — create your `.env` and `funguy.conf` files (see [Configuration](#-configuration))
|
||
|
||
**6. Set up and start the systemd service** (see [Running the Bot](#-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.
|
||
|
||
```env
|
||
# ── 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.
|
||
|
||
```toml
|
||
[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](#configpy--live-configuration-admin-only)).
|
||
|
||
---
|
||
|
||
## 🤖 Running the Bot
|
||
|
||
### Systemd Service (Recommended)
|
||
|
||
Create `/etc/systemd/system/funguybot.service`, replacing the `$variables` with your actual paths and user:
|
||
|
||
```ini
|
||
[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
|
||
```
|
||
|
||
```bash
|
||
systemctl daemon-reload
|
||
systemctl enable funguybot
|
||
systemctl start funguybot
|
||
```
|
||
|
||
### Direct Launch
|
||
|
||
```bash
|
||
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.
|
||
|
||
---
|
||
|
||
### 📄 arxiv.py – arXiv Academic Search
|
||
|
||
| 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.
|
||
|
||
---
|
||
|
||
### 🔍 ddg.py – DuckDuckGo Search
|
||
|
||
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**
|
||
|
||
| Operation | Example |
|
||
|-----------|---------|
|
||
| `gzip compress\|decompress` | `!encode gzip compress "Hello World"` |
|
||
| `zlib compress\|decompress` | `!encode zlib compress "Hello World"` |
|
||
| `bzip2 compress\|decompress` | `!encode bzip2 compress "Hello World"` |
|
||
| `lzma compress\|decompress` | `!encode lzma compress "Hello World"` |
|
||
| `deflate compress\|decompress` | `!encode deflate compress "Hello World"` |
|
||
| `zstd compress\|decompress` | `!encode zstd compress "Hello World"` |
|
||
|
||
**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"
|
||
!encode recipe run 'base64 encode | hex encode :: hello world'
|
||
```
|
||
|
||
**Recipe Commands Details:**
|
||
- `!encode recipe list` - Lists all available operations that can be chained together
|
||
- `!encode recipe run '<json>' <data>` - Execute a JSON recipe on input data
|
||
- `!encode recipe run '<op> arg | <op> arg :: <data>'` - Execute a pipe-style recipe with data
|
||
|
||
JSON recipes allow complex operation chaining with the format:
|
||
```json
|
||
{
|
||
"steps": [
|
||
{"op": "base64", "args": ["encode"]},
|
||
{"op": "hex", "args": ["encode"]}
|
||
]
|
||
}
|
||
```
|
||
|
||
Pipe-style recipes provide a simpler syntax:
|
||
```
|
||
base64 encode | hex encode :: hello world
|
||
```
|
||
|
||
---
|
||
|
||
### 💣 exploitdb.py – Exploit-DB Search
|
||
|
||
```
|
||
!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 (0–100) 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.0–1.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](#-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 (0–100) 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.
|
||
|
||
---
|
||
|
||
### 🎥 youtube-search.py – YouTube Search
|
||
|
||
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](https://matrix.to/#/#selfhosting:mozilla.org)**
|
||
|
||
---
|
||
|
||
## 🧙 Credits
|
||
|
||
**Creator & Developer:** HB ([@hashborgir:mozilla.org](https://matrix.to/#/@hashborgir:mozilla.org))
|
||
|
||
🍄 *Funguy Bot — built during recovery from cervical spinal surgery. Bugs are features in disguise.*
|