Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# ==========================================
# AstrBot Environment Configuration Example
# ==========================================
# Copy this file to .env and adjust the values as needed.
# Note: Variables set here will override default configurations.

# ------------------------------------------
# Core Configuration (核心配置)
# ------------------------------------------

# AstrBot root directory path. Defaults to current working directory or ~/.astrbot for desktop client.
# ASTRBOT_ROOT=/path/to/astrbot

# Log level. Options: DEBUG, INFO, WARNING, ERROR, CRITICAL. Default: INFO.
# ASTRBOT_LOG_LEVEL=INFO

# Enable plugin auto-reload. Set to "1" to enable. Useful for development.
# ASTRBOT_RELOAD=0

# Disable metrics upload. Set to "1" to disable anonymous usage statistics.
# ASTRBOT_DISABLE_METRICS=0

# Python executable path override (used for local code execution feature).
# PYTHON=/usr/bin/python3

# Enable demo mode (might restrict some features).
# DEMO_MODE=False

# Enable testing mode (affects logging and some behaviors).
# TESTING=False

# Flag indicating execution via desktop client (Internal use mostly).
# ASTRBOT_DESKTOP_CLIENT=0

# Flag indicating execution via systemd service.
# ASTRBOT_SYSTEMD=0

# ------------------------------------------
# Dashboard Configuration (管理面板配置)
# ------------------------------------------

# Enable or disable the WebUI Dashboard. Default: True.
# ASTRBOT_DASHBOARD_ENABLE=True

# Dashboard bind host. Default: 0.0.0.0 (listen on all interfaces).
# ASTRBOT_DASHBOARD_HOST=0.0.0.0

# Dashboard bind port. Default: 6185.
# ASTRBOT_DASHBOARD_PORT=6185

# Enable SSL (HTTPS) for the dashboard.
# ASTRBOT_DASHBOARD_SSL_ENABLE=False

# SSL Certificate path (required if SSL is enabled).
# ASTRBOT_DASHBOARD_SSL_CERT=/path/to/cert.pem

# SSL Key path (required if SSL is enabled).
# ASTRBOT_DASHBOARD_SSL_KEY=/path/to/key.pem

# SSL CA Certificates path (optional).
# ASTRBOT_DASHBOARD_SSL_CA_CERTS=/path/to/ca.pem

# ------------------------------------------
# Network Configuration (网络配置)
# ------------------------------------------

# HTTP/HTTPS Proxy URL (e.g., http://127.0.0.1:7890).
# http_proxy=
# https_proxy=

# No proxy list (comma-separated domains/IPs to bypass proxy).
# no_proxy=localhost,127.0.0.1

# ------------------------------------------
# Integrations (第三方集成)
# ------------------------------------------

# Alibaba DashScope API Key (used for Rerank service).
# DASHSCOPE_API_KEY=sk-xxxxxxxxxxxx

# Coze Integration
# COZE_API_KEY=
# COZE_BOT_ID=

# Computer Use data directory (for screenshot/file storage related to computer control).
# BAY_DATA_DIR=

# ------------------------------------------
# Platform Specific (平台特定配置)
# ------------------------------------------

# Test mode for QQ Official Bot.
# TEST_MODE=off
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ Runs on `http://localhost:3000` by default.
5. Use English for all new comments.
6. For path handling, use `pathlib.Path` instead of string paths, and use `astrbot.core.utils.path_utils` to get the AstrBot data and temp directory.
7. Use Python 3.12+ type hinting syntax (e.g., `list[str]` over `List[str]`, `int | None` over `Optional[int]`). Avoid using `Any` and ensure comprehensive type annotations are provided.
8. When introducing new environment variables:
- Use the `ASTRBOT_` prefix for naming (e.g., `ASTRBOT_ENABLE_FEATURE`).
- Add the variable and description to `.env.example`.
- Update `astrbot/cli/commands/cmd_run.py`:
- Add to the module docstring under "Environment Variables Used in Project".
- Add to the `keys_to_print` list in the `run` function for debug output.
9. To check all available CLI commands and their usage recursively, run `astrbot help --all`.


## PR instructions
Expand Down
59 changes: 57 additions & 2 deletions astrbot/cli/__main__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""AstrBot CLI entry point"""

import os
import sys

import click
from click.shell_completion import get_completion_class

from . import __version__
from .commands import bk, conf, init, plug, run, uninstall
Expand All @@ -28,19 +30,43 @@ def cli() -> None:

@click.command()
@click.argument("command_name", required=False, type=str)
def help(command_name: str | None) -> None:
@click.option(
"--all", "-a", is_flag=True, help="Show help for all commands recursively."
)
def help(command_name: str | None, all: bool) -> None:
"""Display help information for commands

If COMMAND_NAME is provided, display detailed help for that command.
Otherwise, display general help information.
"""
ctx = click.get_current_context()

if all:

def print_recursive_help(command, parent_ctx):
name = command.name
if parent_ctx is None:
name = "astrbot"

cmd_ctx = click.Context(command, info_name=name, parent=parent_ctx)
click.echo(command.get_help(cmd_ctx))
click.echo("\n" + "-" * 50 + "\n")

if isinstance(command, click.Group):
for subcommand in command.commands.values():
print_recursive_help(subcommand, cmd_ctx)

print_recursive_help(cli, None)
return

if command_name:
# Find the specified command
command = cli.get_command(ctx, command_name)
if command:
# Display help for the specific command
click.echo(command.get_help(ctx))
parent = ctx.parent if ctx.parent else ctx
cmd_ctx = click.Context(command, info_name=command.name, parent=parent)
click.echo(command.get_help(cmd_ctx))
else:
click.echo(f"Unknown command: {command_name}")
sys.exit(1)
Expand All @@ -57,5 +83,34 @@ def help(command_name: str | None) -> None:
cli.add_command(uninstall)
cli.add_command(bk)


@click.command()
@click.argument("shell", required=False, type=click.Choice(["bash", "zsh", "fish"]))
def completion(shell: str | None) -> None:
"""Generate shell completion script"""
if shell is None:
shell_path = os.environ.get("SHELL", "")
if "zsh" in shell_path:
shell = "zsh"
elif "bash" in shell_path:
shell = "bash"
elif "fish" in shell_path:
shell = "fish"
else:
click.echo(
"Could not detect shell. Please specify one of: bash, zsh, fish",
err=True,
)
sys.exit(1)

comp_cls = get_completion_class(shell)
comp = comp_cls(
cli, ctx_args={}, prog_name="astrbot", complete_var="_ASTRBOT_COMPLETE"
)
click.echo(comp.source())


cli.add_command(completion)

if __name__ == "__main__":
cli()
58 changes: 8 additions & 50 deletions astrbot/cli/commands/cmd_init.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import asyncio
import platform
import shutil
import subprocess
import os
from pathlib import Path

import click
Expand All @@ -11,29 +9,6 @@

from ..utils import check_dashboard

SYSTEMD_SERVICE = r"""
# user service
[Unit]
Description=AstrBot Service
Documentation=https://github.com/AstrBotDevs/AstrBot
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=%h/.astrbot
ExecStart=/usr/bin/astrbot run --backend-only
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=astrbot-%u
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=default.target
"""


async def initialize_astrbot(
astrbot_root: Path, *, yes: bool, backend_only: bool
Expand Down Expand Up @@ -70,7 +45,11 @@ async def initialize_astrbot(
default=True,
)
):
await check_dashboard(astrbot_root)
# 避免在 systemd 模式下因等待输入而阻塞
if os.environ.get("ASTRBOT_SYSTEMD") == "1":
click.echo("Systemd detected: Skipping dashboard check.")
else:
await check_dashboard(astrbot_root)
else:
click.echo("你可以使用在线面版(v4.14.4+),填写后端地址的方式来控制。")

Expand All @@ -82,29 +61,8 @@ def init(yes: bool, backend_only: bool) -> None:
"""Initialize AstrBot"""
click.echo("Initializing AstrBot...")

# 检查当前系统是否为 Linux 且存在 systemd
if platform.system() == "Linux" and shutil.which("systemctl"):
if yes or click.confirm(
"Detected Linux with systemd. Install AstrBot user service?", default=True
):
user_config_dir = Path.home() / ".config" / "systemd" / "user"
user_config_dir.mkdir(parents=True, exist_ok=True)

service_path = user_config_dir / "astrbot.service"

service_path.write_text(SYSTEMD_SERVICE)
click.echo(f"Created service file at {service_path}")

try:
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True)
click.echo("Systemd daemon reloaded.")
click.echo("Management commands:")
click.echo(" Start: systemctl --user start astrbot")
click.echo(" Stop: systemctl --user stop astrbot")
click.echo(" Enable: systemctl --user enable astrbot")
click.echo(" Log: journalctl --user -u astrbot -f")
except subprocess.CalledProcessError as e:
click.echo(f"Failed to reload systemd daemon: {e}", err=True)
if os.environ.get("ASTRBOT_SYSTEMD") == "1":
yes = True

astrbot_root = astrbot_paths.root
lock_file = astrbot_root / "astrbot.lock"
Expand Down
Loading
Loading