# How HoshinoBot Handles Group Events: Join and Leave Notifications Explained

> Discover how HoshinoBot manages group events like join and leave notifications using OneBot notice events and the Service.on_notice decorator. Learn about permission checks for welcome and farewell messages.

- Repository: [ice9coffee/hoshinobot](https://github.com/ice9coffee/hoshinobot)
- Tags: how-to-guide
- Published: 2026-03-03

---

**HoshinoBot processes group join and leave events through OneBot notice events, using the `Service.on_notice` decorator in [`hoshino/service.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/service.py) to register handlers that check service permissions before executing welcome or farewell messages.**

HoshinoBot, an open-source QQ bot framework built on NoneBot, provides a structured approach to handling group member changes. When users join or leave a group, the bot intercepts these **group events** through OneBot protocol notices and executes configurable responses. This article examines the exact code paths, configuration methods, and extension points for these notifications.

## Core Architecture: The Service.on_notice Decorator

All group event handlers in HoshinoBot rely on the `Service.on_notice` decorator defined in **[`hoshino/service.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/service.py)**. This method wraps NoneBot's native event dispatcher with service-level permission checks.

```python

# hoshino/service.py

def on_notice(self, *events):
    def deco(func):
        @wraps(func)
        async def wrapper(session):
            # ① Service enabled for the group?

            if not self.check_enabled(session.event.group_id):
                return
            # ② Call the user‑defined coroutine

            return await func(session)
        # ③ Register with nonebot’s notice dispatcher

        return nonebot.on_notice(*events)(wrapper)
    return deco

```

The wrapper performs three critical functions: it verifies the service is enabled for the specific group using `self.check_enabled`, executes the handler coroutine if permitted, and registers the wrapped function with NoneBot's underlying `on_notice` dispatcher. Event strings such as `'group_increase'` or `'group_decrease.leave'` map directly to OneBot notice payloads.

## Handling Group Join Events (Member Welcome)

New member announcements are handled by the built-in `group-welcome` service in **[`hoshino/modules/groupmaster/group_notice.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/groupmaster/group_notice.py)**.

### The group_increase Event Handler

```python

# hoshino/modules/groupmaster/group_notice.py

sv2 = Service('group-welcome', help_='入群欢迎')

@sv2.on_notice('group_increase')
async def increace_welcome(session: NoticeSession):
    if session.event.user_id == session.event.self_id:
        return                     # ignore the bot itself

    welcomes = hoshino.config.groupmaster.increase_welcome
    gid = session.event.group_id
    if gid in welcomes:
        await session.send(welcomes[gid], at_sender=True)

```

When OneBot emits a `group_increase` notice, the handler first filters out events triggered by the bot itself. It then retrieves the welcome message dictionary from `hoshino.config.groupmaster.increase_welcome` and checks for a group-specific entry. If found, it sends the message while mentioning the new member via `at_sender=True`.

### Configuration Options

Welcome messages are configured in **[`hoshino/config_example/groupmaster.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config_example/groupmaster.py)**:

```python

# hoshino/config_example/groupmaster.py

increase_welcome = {
    "default": "欢迎入群！",
    1000000: "欢迎新群员",
}

```

The configuration supports a `"default"` key for global fallback messages and integer keys representing specific group IDs for customized greetings.

## Handling Group Leave Events (Member Departure)

Member departure notifications use the `group-leave-notice` service, defined in the same file as the welcome handler.

### The group_decrease.leave Event Handler

```python

# hoshino/modules/groupmaster/group_notice.py

sv1 = Service('group-leave-notice', help_='退群通知')

@sv1.on_notice('group_decrease.leave')
async def leave_notice(session: NoticeSession):
    ev = session.event
    if ev.user_id == ev.self_id:
        return                     # ignore the bot itself

    try:
        info = await session.bot.get_stranger_info(self_id=ev.self_id,
                                                   user_id=ev.user_id)
        name = info['nickname'] or ev.user_id
        name = util.filt_message(name)
    except CQHttpError as e:
        sv1.logger.exception(e)
        name = ev.user_id
    await session.send(f"{name}({ev.user_id})退群了。")

```

This handler listens for `group_decrease.leave` notices. It attempts to resolve the departing user's nickname via the `get_stranger_info` API, filters the name for safety using `util.filt_message`, and broadcasts a formatted departure message. Error handling ensures the notification still fires even if the API call fails.

## Auto-Approving Join Requests

Join requests differ from notices—they are **request** events. The `join_approve` module in **[`hoshino/modules/groupmaster/join_approve.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/groupmaster/join_approve.py)** handles these using `nonebot.on_request` directly rather than `Service.on_notice`.

```python

# hoshino/modules/groupmaster/join_approve.py

@on_request('group.add')
async def join_approve(session: RequestSession):
    cfg = hoshino.config.groupmaster.join_approve
    gid = session.event.group_id
    if gid not in cfg:
        return
    for k in cfg[gid].get('keywords', []):
        if k in session.event.comment:
            await session.approve()
            return
    if cfg[gid].get('reject_when_not_match', False):
        await session.reject()

```

This handler checks join request comments against configured keywords. If a match exists, it auto-approves the request; otherwise, it can optionally reject the request based on the `reject_when_not_match` setting.

## Customizing Group Event Handlers

Developers can extend HoshinoBot's group event handling by creating custom services. Below is a complete example demonstrating custom welcome and leave logic:

```python

# my_custom_group_events.py

from hoshino import Service, util

# 1️⃣ Define a new service (optional – can reuse the built‑in one)

sv = Service('my-group-events', help_='自定义入群/退群通知')

# 2️⃣ Welcome new members with a random quote

@sv.on_notice('group_increase')
async def welcome(session):
    quotes = ["欢迎加入！", "欢迎新人~", "大家好，新朋友！"]
    await session.send(util.random.choice(quotes), at_sender=True)

# 3️⃣ Leave notice that includes the time the user spent (requires extra DB logic)

@sv.on_notice('group_decrease.leave')
async def goodbye(session):
    uid = session.event.user_id
    # pretend we fetched join_time from a DB

    join_time = await get_user_join_timestamp(session.event.group_id, uid)
    duration = datetime.utcnow() - join_time
    msg = f"用户 {uid} 已离开本群，已在本群 {duration.days} 天。"
    await session.send(msg)

```

Register this module in your bot's initialization sequence to activate the custom handlers. The `Service` wrapper automatically handles enable/disable commands and group-specific permissions.

## Summary

- **HoshinoBot group events** are handled via OneBot notice events (`group_increase`, `group_decrease.leave`) and request events (`group.add`).
- The **`Service.on_notice`** decorator in [`hoshino/service.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/service.py) provides a unified registration mechanism with built-in permission checks.
- **Welcome messages** are configured via `increase_welcome` in [`hoshino/config_example/groupmaster.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config_example/groupmaster.py) and processed in [`hoshino/modules/groupmaster/group_notice.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/groupmaster/group_notice.py).
- **Leave notifications** fetch user nicknames via `get_stranger_info` and broadcast formatted messages through the same module.
- **Auto-approval** of join requests uses `nonebot.on_request` directly in [`hoshino/modules/groupmaster/join_approve.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/groupmaster/join_approve.py) to match keywords against request comments.

## Frequently Asked Questions

### What is the difference between group_increase and group.add events in HoshinoBot?

The `group_increase` event is a **notice** event triggered after a member has already joined the group, used for welcome messages. The `group.add` event is a **request** event triggered when someone applies to join but requires admin approval, used for auto-approval logic in [`join_approve.py`](https://github.com/ice9coffee/hoshinobot/blob/main/join_approve.py).

### How do I disable group welcome messages for specific groups?

Use the bot's command interface to disable the `group-welcome` service for specific groups. Since `Service.on_notice` checks `self.check_enabled(session.event.group_id)` before executing handlers, disabling the service via the management commands will prevent welcome messages in those groups without modifying code.

### Can HoshinoBot fetch user nicknames when someone leaves the group?

Yes. The built-in `group-leave-notice` handler in [`hoshino/modules/groupmaster/group_notice.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/groupmaster/group_notice.py) calls `session.bot.get_stranger_info(self_id=ev.self_id, user_id=ev.user_id)` to retrieve the user's nickname. If the API call fails, it falls back to displaying the raw user ID.

### Where are the group event configurations stored in HoshinoBot?

Configuration templates are located in **[`hoshino/config_example/groupmaster.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config_example/groupmaster.py)**, which defines `increase_welcome` for welcome messages and `join_approve` for auto-approval keywords. You should copy this file to your actual configuration directory and modify the dictionaries to map group IDs to specific messages or approval rules.