# How the HoshinoBot Permission System Manages User Privileges in priv.py

> Discover how HoshinoBot manages user privileges using integer-based levels in priv.py. Learn about super-users, block lists, and group roles with get user priv and check priv functions.

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

---

**The HoshinoBot permission system uses integer-based privilege levels defined in [`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py) to enforce access control, evaluating super-users, temporary block lists, static configuration lists, and dynamic group roles through the `get_user_priv()` and `check_priv()` functions.**

HoshinoBot implements a lightweight yet robust permission model centered in [`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py). This module assigns every user a numeric privilege level that determines which commands they can execute, balancing transient bans, permanent blacklists, and real-time group membership roles. The system is designed to be both efficient and flexible, allowing module developers to guard commands with simple integer comparisons.

## Privilege Constants and Numeric Levels

The permission hierarchy is encoded as integer constants ranging from `-999` (blocked) to `999` (super-user). These values are declared at the top of [[`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/priv.py#L12-L21):

| Constant | Value | Meaning |
|----------|-------|---------|
| `BLACK` | `-999` | Blocked user (temporary or permanent ban) |
| `DEFAULT` | `0` | Unset or fallback level |
| `NORMAL` | `1` | Regular group member |
| `PRIVATE` | `10` | Private chat (non-group context) |
| `ADMIN` | `21` | Group administrator |
| `OWNER` | `22` | Group owner |
| `WHITE` | `51` | Whitelisted user (exempt from restrictions) |
| `SUPERUSER` / `SU` | `999` | Bot owner with full access |

Higher integers indicate greater privileges. When enforcing permissions, the system checks if the user's level is greater than or equal to the required threshold.

## Temporary and Static Block Lists

HoshinoBot employs a three-layer defense mechanism combining transient blocks, static configuration lists, and dynamic role detection.

### Temporary Blocks

Two dictionaries in [`priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/priv.py) manage time-based bans:

```python
_black_group = {}   # {group_id: expiry_datetime}

_black_user  = {}   # {user_id: expiry_datetime}

```

The functions `set_block_group(group_id, time)` and `set_block_user(user_id, time)` add entries with expiration timestamps (`datetime.now() + time`). The check functions `check_block_group` and `check_block_user` return `True` while the block is active and automatically purge expired entries. See the implementation in [[`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/priv.py#L23-L49).

### Static Configuration Lists

Permanent access control is configured via:

- **`config.BLACK_LIST`**: A list of user IDs denied all access (e.g., `BLACK_LIST = [1974906693]` in [[`config_example/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/config_example/__bot__.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/config_example/__bot__.py#L13)).
- **`config.WHITE_LIST`**: A list of user IDs granted the `WHITE` level (`51`), exempting them from most restrictions regardless of group role.

These lists are evaluated inside `get_user_priv` (lines 44-62 of [[`priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/priv.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/priv.py#L44-L62)).

## Determining User Privileges with get_user_priv

The core authorization logic resides in `get_user_priv(ev)`, which inspects a `CQEvent` and returns the user's integer privilege level. The function evaluates conditions in strict priority order:

```python
def get_user_priv(ev: CQEvent):
    uid = ev.user_id
    if uid in hoshino.config.SUPERUSERS:
        return SUPERUSER
    if check_block_user(uid):
        return BLACK
    if uid in config.WHITE_LIST:
        return WHITE
    if ev['message_type'] == 'group':
        if not ev.anonymous:
            role = ev.sender.get('role')
            if role == 'member':      return NORMAL
            elif role == 'admin':    return ADMIN
            elif role == 'owner':    return OWNER
        return NORMAL
    if ev['message_type'] == 'private':
        return PRIVATE
    return NORMAL

```

This implementation is found in [[`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/priv.py#L55-L77). The hierarchy ensures that super-user status overrides all other checks, while temporary blocks take precedence over whitelist status. For group messages, the sender's role (`member`, `admin`, or `owner`) is mapped directly to the corresponding privilege constant.

## Enforcing Permissions with check_priv

Once a user's privilege level is determined, command handlers enforce restrictions via `check_priv(ev, require)`:

```python
def check_priv(ev: CQEvent, require: int) -> bool:
    if ev['message_type'] == 'group':
        return bool(get_user_priv(ev) >= require)
    else:
        return False

```

Located in [[`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/priv.py#L80-L85), this function performs a simple integer comparison. It returns `True` only if the user's level is greater than or equal to the required threshold. Notably, it explicitly returns `False` for non-group messages, meaning privileged commands cannot be invoked via private chat by default.

## Real-World Usage in Modules

Module developers integrate privilege checks by guarding command entry points with `priv.check_priv`. The pattern is consistent across the codebase: verify the requirement and return early if insufficient.

### Super-User Restrictions

In the picfinder module, sensitive operations are restricted to super-users:

```python
if not priv.check_priv(ev, priv.SUPERUSER):
    return

```

See the implementation in [[`hoshino/modules/picfinder/__init__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/picfinder/__init__.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/modules/picfinder/__init__.py#L98).

### Admin-Only Commands

The gacha module restricts configuration commands to group administrators:

```python
if not priv.check_priv(ev, priv.ADMIN):
    return

```

This appears in [[`hoshino/modules/priconne/gacha/__init__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/modules/priconne/gacha/__init__.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/modules/priconne/gacha/__init__.py#L68).

### Service-Level Integration

The `Service` class in [[`hoshino/service.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/service.py)](https://github.com/ice9coffee/hoshinobot/blob/master/hoshino/service.py#L163) combines privilege checks with feature enablement:

```python
return self.check_enabled(gid) and not priv.check_block_group(gid) and priv.check_priv(ev, self.use_priv)

```

This ensures that a command is only executed if the service is enabled for the group, the group is not temporarily blocked, and the user meets the required privilege level.

## Summary

- **Integer-based hierarchy**: HoshinoBot assigns every user a numeric level from `-999` (`BLACK`) to `999` (`SUPERUSER`), defined in [`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py).
- **Three-layer defense**: The system evaluates temporary block lists first, then static configuration lists (`BLACK_LIST`, `WHITE_LIST`), and finally dynamic group roles.
- **Priority evaluation**: `get_user_priv` checks super-users, blocks, whitelists, and group roles in strict order, returning the highest applicable level.
- **Simple enforcement**: `check_priv` compares the user's level against a required threshold using `>=`, returning `False` for private messages by default.
- **Modular integration**: Command handlers across modules like `picfinder` and `priconne/gacha` guard execution with `if not priv.check_priv(ev, priv.LEVEL): return`.

## Frequently Asked Questions

### What is the highest privilege level in HoshinoBot?

The highest level is `SUPERUSER` (integer `999`), defined as the constant `SU` in [`hoshino/priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/priv.py). Users whose IDs are listed in `config.SUPERUSERS` receive this level, overriding all other permission checks including temporary blocks and group roles.

### How does HoshinoBot handle temporary user bans?

Temporary bans are managed through the `_black_user` and `_black_group` dictionaries in [`priv.py`](https://github.com/ice9coffee/hoshinobot/blob/main/priv.py), which map IDs to expiration timestamps. When `set_block_user()` or `set_block_group()` is called with a `timedelta`, the system stores `datetime.now() + time` as the expiry. `get_user_priv()` returns `BLACK` (`-999`) while the current time is less than the stored expiry, automatically excluding expired entries during checks.

### Can private chat users execute privileged commands?

No. By design, `check_priv()` explicitly returns `False` when `ev['message_type']` is not `'group'`. This means commands requiring `priv.NORMAL` or higher cannot be triggered via private messages, ensuring that privilege checks are strictly enforced within group contexts where roles (`member`, `admin`, `owner`) are verifiable.

### Where are static blacklists and whitelists configured?

Static lists are defined in the bot configuration, typically in [`hoshino/config/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config/__bot__.py) (based on the example in [`hoshino/config_example/__bot__.py`](https://github.com/ice9coffee/hoshinobot/blob/main/hoshino/config_example/__bot__.py)). The `BLACK_LIST` contains user IDs permanently denied access, while `WHITE_LIST` contains users granted the `WHITE` level (`51`), exempting them from most restrictions regardless of their group role. These lists are evaluated inside `get_user_priv()` after super-user checks but before group role detection.