diff --git a/pyproject.toml b/pyproject.toml index 9ac9ea0..d6fa7d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "speechd" -version = "1.2.7" +version = "1.2.8" description = "Speech-to-Text daemon with Groq Whisper API" readme = "README.md" requires-python = ">=3.11" @@ -23,6 +23,7 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/speechd"] +include = ["src/speechd/*.service"] [tool.hatch.build.targets.wheel.shared-scripts] "scripts/speechd-toggle" = "speechd-toggle" diff --git a/src/speechd/__main__.py b/src/speechd/__main__.py index 59f1693..90d5558 100644 --- a/src/speechd/__main__.py +++ b/src/speechd/__main__.py @@ -2,23 +2,14 @@ from __future__ import annotations import argparse import logging +from importlib import resources from speechd.config import Config from speechd.daemon import SpeechDaemon -SERVICE_UNIT = """[Unit] -Description=Speech-to-Text daemon -After=graphical-session.target -[Service] -Type=simple -ExecStart=%h/.local/bin/speechd -Restart=on-failure -RestartSec=3 - -[Install] -WantedBy=default.target -""" +def get_service_unit() -> str: + return resources.files("speechd").joinpath("speechd.service").read_text() def install_service(): @@ -28,14 +19,13 @@ def install_service(): service_file = service_dir / "speechd.service" service_dir.mkdir(parents=True, exist_ok=True) - service_file.write_text(SERVICE_UNIT) + service_file.write_text(get_service_unit()) print(f"Installed: {service_file}") print("\nNext steps:") - print(" 1. Run 'speechd' to create config file") - print(" 2. Edit ~/.config/speechd/config.toml and add your API key") - print(" 3. Run: systemctl --user enable --now speechd") - print(" 4. Toggle recording: speechd-toggle") + print(" 1. Create ~/.config/speechd/config.toml with your API key") + print(" 2. Run: systemctl --user enable --now speechd") + print(" 3. Toggle recording: speechd-toggle") def main(): diff --git a/src/speechd/config.py b/src/speechd/config.py index b63abe0..9601f2b 100644 --- a/src/speechd/config.py +++ b/src/speechd/config.py @@ -1,14 +1,15 @@ +import logging import os +import stat from dataclasses import dataclass from pathlib import Path -DEFAULT_CONFIG = """api_key = "your-grok-api-key" -model = "whisper-large-v3-turbo" -language = "ru" -sample_rate = 16000 -timeout = 300 -audio_quality = 0.8 -""" + +def get_config_path() -> Path: + config_home = os.environ.get("XDG_CONFIG_HOME") + if config_home: + return Path(config_home) / "speechd" / "config.toml" + return Path.home() / ".config" / "speechd" / "config.toml" @dataclass(frozen=True) @@ -25,14 +26,16 @@ class Config: def load(cls) -> "Config": import tomllib - config_path = Path.home() / ".config" / "speechd" / "config.toml" + config_path = get_config_path() if not config_path.exists(): - config_path.parent.mkdir(parents=True, exist_ok=True) - config_path.write_text(DEFAULT_CONFIG) - config_path.chmod(0o600) - raise RuntimeError( - f"Config created at {config_path}\nPlease edit and add your Groq API key" + raise RuntimeError(f"Config not found at {config_path}") + + mode = config_path.stat().st_mode + if mode & (stat.S_IRGRP | stat.S_IROTH): + logging.warning( + "WARNING: %s is world accessible. Consider limiting its permissions (600).", + config_path, ) with open(config_path, "rb") as f: diff --git a/src/speechd/speechd.service b/src/speechd/speechd.service new file mode 100644 index 0000000..24cc0fb --- /dev/null +++ b/src/speechd/speechd.service @@ -0,0 +1,15 @@ +[Unit] +Description=Speech-to-Text daemon +After=graphical-session.target + +[Service] +Type=simple +ExecStart=%h/.local/bin/speechd +Restart=on-failure +RestartSec=3 + +ProtectSystem=full +NoNewPrivileges=yes + +[Install] +WantedBy=default.target