# Silence timers, an unanswered counter, and a bot that speaks first in AstrBot

> An AstrBot plugin that schedules its own outgoing messages per chat session, counts how long you have been quiet, restores pending tasks from files after a restart, and ships its own console. Its central weakness is the one every proactive plugin shares: it sends a simulated user message, so the prompt decides everything.

**Pancakes-Labs/astrbot_plugin_proactive_chat** — 一个能让 Bot 在私聊和群聊中发起主动消息的插件，拥有上下文感知、持久化数据、动态情绪、免打扰时段和 TTS 集成。还有独立 WebUI，可进行个性化配置。  An AstrBot plugin that enables Bot to send proactive messages in private and group chats, featuring context awareness, persistent data, dynamic emotions, do-not-disturb periods, and TTS integration. It also boasts an independent WebUI for personalized.

- Repository: https://github.com/Pancakes-Labs/astrbot_plugin_proactive_chat
- Stars: 398 · Forks: 21
- Language: Python
- License: AGPL-3.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/pancakes-labs-astrbot-plugin-proactive-chat

## A silence timer per session, jittered by a random window

The trigger is not a cron entry. It is how long a given chat has been quiet, measured per session, plus a random delay inside a range you configure, so two bots set up the same way will not fire in lockstep. On each reload the plugin can start creating proactive message tasks by itself, which means nothing you type is needed to arm it. You fill in the session list, save, and the scheduler takes over from there.

Behind that sits APScheduler. `requirements.txt` pins it to `apscheduler>=3.10,<4.0`, the one hard upper bound in that file, and the plugin leans on it to hold pending tasks, resume them, and fire them when the countdown runs out. `aiofiles>=23.2` sits next to it for the async file writes that make resumption possible at all.

What greets you after a fresh install is status rather than a conversation. Inside that console sit plugin state, scheduler state, how much session data is loaded, how many WebSocket connections are open, and separate countdown cards for the group silence timer and the auto trigger timer. That last pair answers the only question worth asking on day one: is this chat counting down, or is it sitting idle because nobody has said anything yet?

## Each session keeps its own counter, persona and quiet hours

Sessions do not share state. Every one carries its own state, counters and triggers, and each can be given its own configuration and a nickname, so a busy group and a one-to-one chat can run different personalities on the same bot. That isolation is what gives the session-level override in the console something to override, and it means a short silence window in a lively room does not follow you into a private chat.

One hook matters more than the rest, and it is the unanswered counter. Each time you fail to reply it goes up, and the plugin makes the value readable inside your own prompt, so the bot can grow warmer, colder, or sulk as the silence extends. You can also set a ceiling on the counter, which is the line between a mood and a complaint.

Three smaller mechanisms sit alongside it. A do-not-disturb window stops the bot initiating anything during a period you pick. Text to speech calls whatever TTS service you have already configured instead of shipping an engine. Long replies are split into several short messages with typing intervals between them, which is what keeps an eight-paragraph answer from landing as one wall of text in a chat app.

## The UMO string is the only address the bot has for a chat

Configuring a session begins with asking the bot to name itself. Send `/sid` in the chat you want messages in, and the reply hands back a UMO and a UID. If you changed the wake word under AstrBot's platform configuration, the slash leaves with it, so `/sid` becomes your custom prefix instead.

Reply format is fixed:

```text
UMO: 「default:GroupMessage:123456789」
UID: 「987654321」
*Use UMO to set whitelist and configure routing, use UID to set admin list(UMO 可用于设置白名单和配置文件路由，UID 可用于设置管理员列表)

Your session information:
Bot ID: 「default」
Message Type: 「GroupMessage」
Session ID: 「123456789」
```

Take the text between the corner brackets and nothing else. The documentation warns about exactly this twice, because pasting the whole reply into the config is the most common way to end up with a bot that never speaks at all.

People trip on the official QQ bot case. That reply adds a line naming the group, and for a group you substitute the group ID where the UID would go, giving `default:GroupMessage:7E933A67F5C0AD0A128A199EFCE140B4`. For a private chat, the UID value is the UMO you want.

## Pending tasks are restored from files, not memory

A reload cancels nothing. Whether you restart AstrBot or reload only the plugin, pending proactive message tasks are restored from files on disk, so a countdown you set up before lunch is still waiting in the afternoon. That behaviour is the reason a file-backed design matters here: the scheduler's state survives the process that owned it.

Most of what lives here is plumbing, and the layout shows it. `main.py` is the entry point, `core/` holds the scheduler and message path, `utils/` the helpers, `assets/` the static files the console serves, and `_conf_schema.json` is the schema that drives the configuration forms. `metadata.yaml` sits alongside for the plugin market listing, and the three parallel READMEs in Chinese, English and Japanese mean documentation upkeep recurs on every release rather than happening once.

That schema file also explains why you never learn a plugin command. One point the documentation makes twice: every core setting is reachable from AstrBot's own WebUI and from the plugin's console, with no code editing and nothing to memorize. What that costs is visibility into the configuration surface itself, which is exactly what the schema declares. A setting you expect is either rendered or it does not exist, and the per-session override is the only partial escape.

## Five console views, and the config form is rendered from the schema

Since v1.2.0 the plugin has shipped its own console, and the reason is visibility into a job that runs when nobody is watching. Runtime status shows plugin and scheduler state, session data volume, WebSocket connection count, and countdown cards for the group silence timer and the auto trigger timer. Task management lists every pending task with its next execution time, remaining countdown, scheduling progress and unanswered count, and lets you trigger one immediately or cancel it outright.

Notification center receives update notes, fix notices and security reminders from the plugin's own side, with unread counts, per-item marking and manual sync. Doc browsing renders the repository's Markdown inside the frontend, so the README, the changelog and whatever lives in `docs/` are readable while you configure rather than after you break something. Config management builds its form from the schema and layers session overrides on top.

Both timers and both task types are shown the same way: a countdown, a status label and a progress bar, with the session nickname, UMO and unanswered count highlighted so you can locate the right chat. Operations past manual refresh and trigger-now are not settled by the documentation that ships with the repository, which stops partway down that list.

## The message it sends is a fabricated user turn, and that is the ceiling

One limit is stated plainly by the developer: almost every plugin that sends proactive messages does it by emitting a simulated user message, and this one is no different. So the model receives a fabricated turn from your side of the conversation, and whether the reply stays in character depends on prompt quality rather than on the plugin's scheduling. Fixes offered are the obvious ones: rewrite the proactive message prompt, adjust the persona, move to a model with more capability, or supply richer context.

That is a ceiling rather than a footnote. A bot that hears nothing for six hours and then replies as though you had just typed something is the failure mode, and no timer setting removes it.

An unusual note about how this was written sits in the repository. In it the developer states that the plugin's files and documentation were produced by AI, with him supplying the architecture and the prose, and asks readers to judge the content carefully. Cost figures follow in the same note: 70 days elapsed and roughly 327 hours on the main plugin, with 731,817,728 tokens used. Read that as a statement about how much review the code received, not as a quality claim in either direction.

## Four pinned requirements, and tomli only below Python 3.11

Requirements come to four lines plus one conditional, and they mostly repeat what AstrBot already ships. The file's own comment says the core dependencies are included in AstrBot's defaults and installed automatically when the plugin is downloaded, so what is left exists for the environments that need it spelled out:

```
apscheduler>=3.10,<4.0
aiofiles>=23.2
fastapi>=0.110
uvicorn>=0.29
tomli>=2.0,<3.0 ; python_version < "3.11"
```

Two install paths exist. AstrBot's plugin market handles it, or you take the `.zip` of `astrbot_plugin_proactive_chat` from the Releases page and install it from file using the plus button at the bottom right of AstrBot's plugin page. If something is genuinely missing, the manual command is short:

```bash
pip install fastapi uvicorn
# Python < 3.11 额外安装
pip install "tomli>=2.0; python_version < '3.11'"
```

`fastapi` and `uvicorn` appear because of the plugin's own console. A plugin that is otherwise pure Python carries a web server purely so the five views above can be rendered, which is also why a host that blocks local ports gets a plugin that installs fine and then looks dead.

## Three tags since May, under AGPL-3.0

Change is recent enough to read in the tags: v1.2.6 on 2026-09-28, v1.2.5 on 2026-08-30 and v1.2.4 on 2026-05-29, with the last push to the default branch landing on 2026-09-28. Tag names carry no detail beyond the version number, so `CHANGELOG.md` at the repository root is the file to read, and the console's notification center surfaces the same notes where you will actually see them.

Licensing is AGPL-3.0, with `LICENSE` at the root next to `CODE_OF_CONDUCT.md` and `CONTRIBUTING.md`. Two housekeeping artifacts are worth knowing about because they tell you how the project is worked on: `run_ruff.bat`, a Windows batch wrapper for the Python linter, and a `docs/` directory that the console indexes.

Structurally, this is a plugin rather than a bot. Nothing happens until AstrBot is running with a model behind it, and everything the plugin offers is a modifier on sessions that already exist. If your requirement is a chat product rather than a personality layer on someone else's bot, the size of this repository is the first warning sign.

## Conclusion

Take it if you already run AstrBot and want one specific group or friend to hear from the bot without being asked first. Skip it if the chat has to sound like a spontaneous human, because the simulated user message is a limit no amount of scheduling removes, and skip it if you need it to be a standalone bot rather than a plugin. Before installing, confirm your AstrBot version is recent enough for the plugin market path and read CHANGELOG.md, since the release tags number v1.2.6 on 2026-09-28 with no per-tag detail attached.

## FAQ

### What is AstrBot, and where does this plugin sit?

AstrBot is the chat bot this plugin extends. The plugin is fetched from AstrBot's plugin market or installed from a release zip in its WebUI, configured from its plugin page, and it fires inside chat sessions of that bot. It cannot run on its own, and it needs a model configured in AstrBot before any of it happens.

### How do I find the UMO for a chat that should receive proactive messages?

Send `/sid` in that chat and copy only what sits between the corner brackets in the reply. If you changed the wake word under platform configuration, use your custom prefix instead of the slash. For a group on the official QQ bot, substitute the group ID shown in the reply where the UID would normally go.

### What happens to pending proactive messages if I restart AstrBot?

They survive. Pending tasks are restored from files on disk, so restarting AstrBot or reloading the plugin leaves every countdown running. The console's task view shows the next execution time and remaining countdown for each one, and lets you trigger a session immediately or cancel it.

### Do I have to install fastapi and uvicorn myself?

Usually not. The core dependencies are already part of AstrBot's defaults and are installed automatically when the plugin is downloaded. They are there for environments that need the manual step, and `tomli` is only required when you run Python below 3.11.

### Will the bot message me in the middle of the night?

Not if you set a do-not-disturb period. You define a time window during which the plugin will not initiate anything, and the silence counters keep running underneath it. Long replies are also split into several messages with typing intervals, and TTS uses whatever speech service you have already configured.

## Sources

- [Issues](https://github.com/Pancakes-Labs/astrbot_plugin_proactive_chat/issues)
- [License: AGPL-3.0](https://github.com/Pancakes-Labs/astrbot_plugin_proactive_chat/blob/main/LICENSE)
- [Pancakes-Labs/astrbot_plugin_proactive_chat on GitHub](https://github.com/Pancakes-Labs/astrbot_plugin_proactive_chat)
- [README](https://github.com/Pancakes-Labs/astrbot_plugin_proactive_chat/blob/main/README.md)
- [Releases](https://github.com/Pancakes-Labs/astrbot_plugin_proactive_chat/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pancakes-labs-astrbot-plugin-proactive-chat
