[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-93784":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":21,"hasPages":19,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":30,"readmeContent":31,"aiSummary":32,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":33,"discoverSource":34},93784,"netmon","Role1776\u002Fnetmon","Role1776","Self-hosted network monitor — hourly speed tests, LAN device counts via ARP scan, and sarcastic AI-generated reports delivered to Telegram.","",null,"Python",228,35,3,2,0,44.67,"MIT License",false,"main",true,[23,24,25,26,27,28,29],"network-monitoring","nmap","python","self-hosted","speedtest","telegrambot","uv","2026-09-21 04:01:26","\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Flogo.png\" alt=\"netmon logo\" width=\"180\" \u002F>\n\u003C\u002Fp>\n\n\u003Ch1 align=\"center\">netmon\u003C\u002Fh1>\n\n\u003Cp align=\"center\">\n  \u003Cb>Self-hosted local network monitor with 24-hour speed charts & sarcastic AI commentary delivered straight to Telegram or Discord.\u003C\u002Fb>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"LICENSE\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-8bc34a?style=for-the-badge\" alt=\"License MIT\">\u003C\u002Fa>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FPython-3.13+-3776AB?style=for-the-badge&logo=python&logoColor=white\" alt=\"Python\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fuv-managed-DE5FE9?style=for-the-badge&logo=uv&logoColor=white\" alt=\"uv\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTelegram-Bot_API-26A5E4?style=for-the-badge&logo=telegram&logoColor=white\" alt=\"Telegram\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FDiscord-Webhook-5865F2?style=for-the-badge&logo=discord&logoColor=white\" alt=\"Discord\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSQLite-Storage-003B57?style=for-the-badge&logo=sqlite&logoColor=white\" alt=\"SQLite\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FMatplotlib-Graphs-11557c?style=for-the-badge\" alt=\"Matplotlib\">\n\u003C\u002Fp>\n\n---\n\nA lightweight local bot that runs a speed test on your network every 30 minutes, scans active devices on your LAN using `nmap`, and logs everything to a local SQLite database.\n\nEvery 4 hours, it delivers a **detailed report** complete with a 24-hour trend graph and a sarcastic, LLM-generated commentary on your network's behavior (*\"someone's hogging the bandwidth again\"*).\n\n> [!NOTE]\n> **100% Private & Self-Hosted:** No external metric servers involved — everything runs locally on your machine or Raspberry Pi. Only text reports and graph images are dispatched to your chosen notifier (Telegram or Discord).\n\n---\n\n## Features & Workflow\n\nEvery 30 minutes (`SLEEP_TIME` in `main.py`, default 1800 seconds):\n\n1. **Speed Test:** Measures download\u002Fupload speeds, ping latency, ISP, and test server details using `speedtest-cli` (see [the note on measurement mode](#a-note-on-measurement-mode)).\n2. **LAN Scan:** Scans the local subnet using `nmap` ARP scan to count active connected devices.\n3. **Local Storage:** Saves metrics & device tallies directly to a local `metrics.sql` SQLite database.\n4. **Status Alert:** Sends a concise status update to your chosen notifier (*\"all good\"* or *\"line is dying\"*).\n5. **24h AI Report:** Every 8th cycle (every 4h), generates a **24-hour trend graph** via `matplotlib` alongside a sarcastic LLM analysis of network load and speed fluctuations.\n\n---\n\n## Tech Stack\n\n| Technology | Purpose |\n| :--- | :--- |\n| **Python 3.13+** (via `uv`) | Core runtime |\n| **SQLite** | Local metrics persistence (`metrics.sql`) |\n| **`speedtest-cli`** | Network bandwidth and ping measurements |\n| **`nmap`** | Subnet ARP scanning for device discovery |\n| **`matplotlib`** | 24-hour metrics visualization |\n| **OpenAI-compatible API** | Sarcastic report & trend analysis (cloud OpenAI or a local LLM) |\n| **Telegram API \u002F Discord Webhooks** | Alert and graph report delivery |\n\n---\n\n## Requirements\n\n* **OS:** macOS or Linux (`nmap --iflist` required; Windows not supported out of the box).\n* **[uv](https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002F)** — manages the Python version, virtualenv, and locked dependencies for you. No manual `python3`\u002F`venv`\u002F`pip` juggling.\n* **System Binaries:** `nmap` and `speedtest-cli` installed system-wide.\n* **Passwordless `sudo` for `nmap`** — device counting needs a real ARP scan (raw sockets), which requires root; see one-time setup below.\n* **Tokens:** either a Telegram Bot Token + Chat ID, *or* a Discord Webhook URL (see [Notifications](#notifications-telegram-or-discord)), plus an API key for your OpenAI-compatible provider (not needed if you point `AI_BASE_URL` at a local LLM server).\n\n---\n\n## Quick Start\n\n### 1. System Dependencies\n\n**macOS (Homebrew):**\n```bash\nbrew install nmap speedtest-cli\n```\n\n**Linux (Debian\u002FUbuntu):**\n```bash\nsudo apt update && sudo apt install -y nmap speedtest-cli\n```\n\n### 2. Allow Passwordless `nmap` (one-time)\n\nDevice counting runs `nmap` as root for a real ARP scan — without it, host discovery silently falls back to ordinary TCP probing and undercounts devices that don't answer on common ports. Since the bot runs unattended, `sudo` needs to work without a password prompt on every cycle:\n\n```bash\necho \"$(whoami) ALL=(root) NOPASSWD: $(command -v nmap)\" | sudo tee \u002Fetc\u002Fsudoers.d\u002Fnetmon-nmap\nsudo chmod 440 \u002Fetc\u002Fsudoers.d\u002Fnetmon-nmap\n```\n\nThis grants passwordless `sudo` only for the `nmap` binary — not your whole account.\n\n### 3. Clone & Setup Environment\n\nInstall [`uv`](https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002F) if you don't have it yet:\n\n```bash\ncurl -LsSf https:\u002F\u002Fastral.sh\u002Fuv\u002Finstall.sh | sh\n```\n\nThen:\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002FRole1776\u002Fnetmon.git\ncd netmon\nuv sync\n```\n\n`uv sync` downloads the pinned Python version (see `.python-version`) if you don't already have it, creates `.venv`, and installs the exact locked dependency versions from `uv.lock`. No system `python3`, no manual venv activation.\n\n### 4. Configure `.env`\n\nCopy the template file and fill in your secrets:\n\n```bash\ncp .env.example .env\n```\n\n`.env` variables:\n\n| Variable | Description |\n| :--- | :--- |\n| `AI_API_KEY` | Your LLM provider API key (any string works for most local servers) |\n| `AI_MODEL` | Model name (e.g. `gpt-4o-mini`, or a local model name — see below) |\n| `AI_BASE_URL` | Base API URL (e.g., `https:\u002F\u002Fapi.openai.com\u002Fv1`, or your local server's URL) |\n| `NOTIFIER` | `telegram` (default) or `discord` — picks which service receives alerts |\n| `TG_BOT_TOKEN` | Telegram bot token from `@BotFather` — required if `NOTIFIER=telegram` |\n| `TG_CHAT_ID` | Your Telegram Chat ID — required if `NOTIFIER=telegram` |\n| `DISCORD_WEBHOOK_URL` | Discord channel webhook URL — required if `NOTIFIER=discord` |\n| `DB_PATH` | SQLite database file path (e.g. `metrics.sql`) |\n| `REQUEST_TIMEOUT` | *Optional.* HTTP timeout in seconds for Telegram\u002FDiscord requests (positive integer, default `10`) |\n\n> [!TIP]\n> **You're not locked into OpenAI.** `ai.py` talks to any OpenAI-compatible endpoint, so a local inference server (e.g. [Ollama](https:\u002F\u002Follama.com), LM Studio) works too — just point `AI_BASE_URL` at it. For report quality that holds up, use a model with **at least ~7B parameters**; a solid local pick is **Gemma 4 12B at 4-bit (QAT) quantization** (`gemma4:12b-it-qat` via Ollama), which fits comfortably on 16GB of RAM.\n\n### 5. Run the Bot\n\n```bash\nuv run main.py\n```\n\n`uv run` always uses this project's own `.venv` and pinned Python version, so it can't accidentally run against your system `python3`.\n\n> [!TIP]\n> Run the bot inside `tmux`\u002F`screen` or set it up as a system service (`systemd`\u002F`launchd`) to keep it running 24\u002F7 in the background.\n\n---\n\n## Notifications: Telegram or Discord\n\nnetmon supports two notification backends, selected via the `NOTIFIER` variable in `.env`. Only one is needed.\n\n### Telegram (default)\n\n1. Message [`@BotFather`](https:\u002F\u002Ft.me\u002Fbotfather) on Telegram and send `\u002Fnewbot`, following the prompts to get a **bot token**.\n2. Get your **Chat ID** — the simplest way is to message your new bot, then visit `https:\u002F\u002Fapi.telegram.org\u002Fbot\u003CYOUR_TOKEN>\u002FgetUpdates` in a browser and read the `chat.id` field from the JSON response.\n3. In `.env`:\n   ```\n   NOTIFIER=telegram\n   TG_BOT_TOKEN=123456789:AAHfoo...\n   TG_CHAT_ID=987654321\n   ```\n\nIf `NOTIFIER` is left unset, netmon defaults to Telegram, so existing setups keep working with no changes.\n\n### Discord\n\n1. In your target Discord channel: **Server Settings → Integrations → Webhooks → New Webhook**, then copy the webhook URL. No bot invite or permissions setup needed.\n2. In `.env`:\n   ```\n   NOTIFIER=discord\n   DISCORD_WEBHOOK_URL=https:\u002F\u002Fdiscord.com\u002Fapi\u002Fwebhooks\u002Fxxxx\u002Fyyyy\n   ```\n\nDiscord delivery reuses the same report content as Telegram — the existing HTML formatting (`\u003Cb>`, `\u003Ccode>`, `\u003Cpre>`) is automatically converted to Discord markdown, so reports render correctly in either service without any changes to the AI prompt.\n\n> [!WARNING]\n> Treat both the Telegram bot token and the Discord webhook URL as secrets — anyone with either can post messages through your bot\u002Fwebhook. Don't commit them to version control (`.env` is already git-ignored).\n\n---\n\n## A Note on Measurement Mode\n\nnetmon runs `speedtest --secure --single --json` (see `runner.py`) — the `--single` flag means the test uses **one TCP connection**. This is deliberate: a single stream approximates what one real application on your network would actually get, since it is subject to the same window-size and packet-loss limits any ordinary download faces.\n\nMulti-threaded speed tests (including Ookla's official CLI, and the speedtest.net web UI) open many parallel connections instead. That measures something different — the practical ceiling of your line — and will report noticeably higher numbers on fast connections. Neither figure is \"wrong\"; they answer different questions.\n\nTwo consequences worth knowing:\n\n* **Don't compare netmon's numbers directly against speedtest.net in a browser.** The browser test is multi-threaded and will read higher. That gap is methodology, not a fault in your line.\n* **On very fast links (roughly 500 Mbps+), expect single-stream figures to sit well below your subscribed speed.** Beyond the methodology gap, `speedtest-cli` is pure Python, so at gigabit speeds its own CPU overhead starts contributing too.\n\nSince netmon exists to track *trends*, consistency matters more than peak numbers: keep one measurement method for the lifetime of your database. Swapping the backend mid-history puts a step change in your 24-hour graph that the AI commentary will faithfully report as a real speed jump.\n\n---\n\n## Example Output\n\n### Hourly Short Status Update\n\n```text\nNetwork Status Update\nTime: 2026-07-21 14:00:00\nISP: MyISP | Server: New York\n\nDevices online: 7\nDownload: 145.2 Mbps\nUpload: 62.1 Mbps\nLatency: 14.8 ms\n\nTraffic used: 160.0 MB down \u002F 70.0 MB up\n\nCurrent status: Good speed and low latency\n```\n\n### 4-Hour Detailed Report (With Graph & AI Analysis)\n\nEvery 4 hours, the bot sends a **24-hour matplotlib graph** accompanied by a sarcastic LLM-generated report:\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Fexample_graph.png\" alt=\"24h Network Speed Test Graph\" width=\"650\" \u002F>\n\u003C\u002Fp>\n\n```html\n\u003Cb>Network Speed Test Report (24h Analysis)\u003C\u002Fb>\n\nClient: \u003Cb>MyISP\u003C\u002Fb>\nServer: \u003Cb>New York\u003C\u002Fb>\n\n\u003Cb>Latest Test Metrics\u003C\u002Fb>\n\u003Cpre>\nDownload: 178.5 Mbps\nUpload: 45.2 Mbps\nPing: 23.1 ms\nDevices Online: 9\n\u003C\u002Fpre>\n\n\u003Cb>24-Hour Dynamics Analysis\u003C\u002Fb>\nOver the last 24 hours, the download speed averaged \u003Ccode>140 Mbps\u003C\u002Fcode>, but we saw a massive drop to \u003Ccode>20 Mbps\u003C\u002Fcode> at 8:00 PM right as device count jumped from \u003Ccode>4\u003C\u002Fcode> to \u003Ccode>11 devices\u003C\u002Fcode>. Clearly, someone's hogging the bandwidth or the ISP's mice were busy chewing on the fiber line again. Latency remained stable except for a brief spike during peak hours.\n\n\u003Cb>Data Transfer (Latest Test)\u003C\u002Fb>\n\u003Cpre>\nDownloaded: 160.0 MB\nUploaded: 70.0 MB\n\u003C\u002Fpre>\n\n\u003Cb>Conclusion\u003C\u002Fb>\nExpect periodic speed drops whenever local freeloaders stream 4K movies or the ISP potato infrastructure struggles.\n```\n\n---\n\n## Project Structure\n\n```text\nnetmon\u002F\n├── assets\u002F                        # Logo & documentation media assets\n├── graphs\u002F                        # Generated 24h matplotlib graph images\n├── main.py                        # Main execution loop & orchestrator\n├── runner.py                      # Speedtest-cli and nmap scan execution & parsing\n├── sqlite.py                      # SQLite database operations & schema management\n├── models.py                      # Domain data models (NetworkMetric, SpeedTest)\n├── graphs.py                      # Matplotlib graph rendering engine\n├── ai.py                          # OpenAI API client & sarcastic text generator\n├── tg.py                          # Telegram bot dispatch helper\n├── discord_hook.py                # Discord webhook dispatch helper\n├── config.py                      # Environment variable validation & config\n├── notifier.py                    # Notifier protocol & shared chat-action enum\n├── pyproject.toml                 # Project metadata & dependencies\n├── uv.lock                        # Locked, reproducible dependency versions\n└── LICENSE                        # MIT License file\n```\n\n---\n\n## License\n\nDistributed under the **MIT License**. See [`LICENSE`](LICENSE) for more details.\n","netmon 是一个自托管的本地网络监控工具，定期执行网速测试、局域网设备扫描，并通过 Telegram 或 Discord 发送带趋势图表和 AI 生成的幽默分析报告。核心功能包括每30分钟一次的 speedtest-cli 带宽与延迟测量、nmap ARP 扫描统计在线设备数、SQLite 本地存储、matplotlib 生成24小时性能图表，以及基于 OpenAI 兼容 API 的轻量级网络行为评论。适用于家庭网络、小型办公室或 NAS\u002FRaspberry Pi 环境下的私有化网络健康监测，强调数据不出本地、无需云依赖。","2026-07-25 02:30:05","CREATED_QUERY"]