Move app settings into config files
This commit is contained in:
@@ -1,11 +0,0 @@
|
|||||||
DISCORD_WEBHOOK_URL=
|
|
||||||
VRCHAT_USERNAME=
|
|
||||||
VRCHAT_PASSWORD=
|
|
||||||
|
|
||||||
OCR_CROP_LEFT=0.05
|
|
||||||
OCR_CROP_TOP=0.35
|
|
||||||
OCR_CROP_RIGHT=0.95
|
|
||||||
OCR_CROP_BOTTOM=0.95
|
|
||||||
OCR_PREPROCESS=1
|
|
||||||
OCR_SCALE=1.5
|
|
||||||
OCR_USE_ANGLE_CLS=0
|
|
||||||
2
VRWT_Tool/VRC_OSC/.gitignore
vendored
2
VRWT_Tool/VRC_OSC/.gitignore
vendored
@@ -3,6 +3,8 @@ __pycache__/
|
|||||||
|
|
||||||
.env
|
.env
|
||||||
config/config.toml
|
config/config.toml
|
||||||
|
config/secrets.toml
|
||||||
|
config/guests.txt
|
||||||
runtime/
|
runtime/
|
||||||
.vrchat_cookie.txt
|
.vrchat_cookie.txt
|
||||||
src/.vrchat_cookie.txt
|
src/.vrchat_cookie.txt
|
||||||
|
|||||||
@@ -5,7 +5,18 @@ name = ""
|
|||||||
names = []
|
names = []
|
||||||
|
|
||||||
[guest]
|
[guest]
|
||||||
names = []
|
file = "config/guests.txt"
|
||||||
|
|
||||||
[notice]
|
[notice]
|
||||||
missing_count = 0
|
missing_count = 0
|
||||||
|
|
||||||
|
[ocr.crop]
|
||||||
|
left = 0.05
|
||||||
|
top = 0.35
|
||||||
|
right = 0.95
|
||||||
|
bottom = 0.95
|
||||||
|
|
||||||
|
[ocr]
|
||||||
|
preprocess = true
|
||||||
|
scale = 1.5
|
||||||
|
use_angle_cls = false
|
||||||
|
|||||||
3
VRWT_Tool/VRC_OSC/config/guests.example.txt
Normal file
3
VRWT_Tool/VRC_OSC/config/guests.example.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
GuestUserA
|
||||||
|
GuestUserB
|
||||||
|
GuestUserC
|
||||||
6
VRWT_Tool/VRC_OSC/config/secrets.example.toml
Normal file
6
VRWT_Tool/VRC_OSC/config/secrets.example.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[discord]
|
||||||
|
webhook_url = ""
|
||||||
|
|
||||||
|
[vrchat]
|
||||||
|
username = ""
|
||||||
|
password = ""
|
||||||
@@ -34,65 +34,83 @@ names = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[guest]
|
[guest]
|
||||||
names = [
|
file = "config/guests.txt"
|
||||||
"GuestUserA",
|
|
||||||
"GuestUserB"
|
|
||||||
]
|
|
||||||
|
|
||||||
[notice]
|
[notice]
|
||||||
missing_count = 3
|
missing_count = 3
|
||||||
|
|
||||||
|
[ocr.crop]
|
||||||
|
left = 0.05
|
||||||
|
top = 0.35
|
||||||
|
right = 0.95
|
||||||
|
bottom = 0.95
|
||||||
|
|
||||||
|
[ocr]
|
||||||
|
preprocess = true
|
||||||
|
scale = 1.5
|
||||||
|
use_angle_cls = false
|
||||||
```
|
```
|
||||||
|
|
||||||
`self.name` は Discord 自動ミュート判定に使う。
|
`self.name` は Discord 自動ミュート判定に使う。
|
||||||
|
|
||||||
`staff.names` は表示対象だが guest 人数には含めない。
|
`staff.names` は表示対象だが guest 人数には含めない。
|
||||||
|
|
||||||
`guest.names` は guest 人数の母数と在室判定に使う。
|
`guest.file` は guest 名簿ファイル。guest 人数の母数と在室判定に使う。
|
||||||
|
|
||||||
`notice.missing_count` は「あと何人以内で全員集合か」の通知範囲。例えば guest が 23 人で `missing_count = 3` の場合、20〜22 人のときに未入室 guest を出す。
|
`notice.missing_count` は「あと何人以内で全員集合か」の通知範囲。例えば guest が 23 人で `missing_count = 3` の場合、20〜22 人のときに未入室 guest を出す。
|
||||||
|
|
||||||
## 秘密情報
|
|
||||||
|
|
||||||
`.env` に置く。このファイルは Git に入れない。
|
|
||||||
|
|
||||||
雛形は `.env.example`。
|
|
||||||
|
|
||||||
```env
|
|
||||||
DISCORD_WEBHOOK_URL=
|
|
||||||
VRCHAT_USERNAME=
|
|
||||||
VRCHAT_PASSWORD=
|
|
||||||
|
|
||||||
OCR_CROP_LEFT=0.05
|
|
||||||
OCR_CROP_TOP=0.35
|
|
||||||
OCR_CROP_RIGHT=0.95
|
|
||||||
OCR_CROP_BOTTOM=0.95
|
|
||||||
OCR_PREPROCESS=1
|
|
||||||
OCR_SCALE=1.5
|
|
||||||
OCR_USE_ANGLE_CLS=0
|
|
||||||
```
|
|
||||||
|
|
||||||
`DISCORD_WEBHOOK_URL` は VRC ログ結果の Discord Webhook 通知に使う。
|
|
||||||
|
|
||||||
`VRCHAT_USERNAME` / `VRCHAT_PASSWORD` は `src/getUser.py` を使う場合だけ必要。
|
|
||||||
|
|
||||||
OCR 設定は速度と精度の調整用。
|
OCR 設定は速度と精度の調整用。
|
||||||
|
|
||||||
`OCR_CROP_*` は VRChat ウィンドウ内の相対座標。`0.0` から `1.0` で指定する。デフォルトは画面下側の中央寄りだけを読む。
|
`ocr.crop` は VRChat ウィンドウ内の相対座標。`0.0` から `1.0` で指定する。デフォルトは画面下側の中央寄りだけを読む。
|
||||||
|
|
||||||
フル画面 OCR に戻す場合。
|
フル画面 OCR に戻す場合。
|
||||||
|
|
||||||
```env
|
```toml
|
||||||
OCR_CROP_LEFT=0
|
[ocr.crop]
|
||||||
OCR_CROP_TOP=0
|
left = 0
|
||||||
OCR_CROP_RIGHT=1
|
top = 0
|
||||||
OCR_CROP_BOTTOM=1
|
right = 1
|
||||||
|
bottom = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
`OCR_PREPROCESS=1` はグレースケール化、コントラスト補正、シャープ化、拡大を行う。
|
`ocr.preprocess = true` はグレースケール化、コントラスト補正、シャープ化、拡大を行う。
|
||||||
|
|
||||||
`OCR_SCALE` は前処理時の拡大率。大きいほど小さい文字に強くなるが遅くなる。
|
`ocr.scale` は前処理時の拡大率。大きいほど小さい文字に強くなるが遅くなる。
|
||||||
|
|
||||||
`OCR_USE_ANGLE_CLS=0` は角度分類を切って高速化する。縦書きや回転文字を読む必要がある場合だけ `1` にする。
|
`ocr.use_angle_cls = false` は角度分類を切って高速化する。縦書きや回転文字を読む必要がある場合だけ `true` にする。
|
||||||
|
|
||||||
|
## guest 名簿
|
||||||
|
|
||||||
|
`config/guests.txt` に 1 行 1 名で書く。このファイルは Git に入れない。
|
||||||
|
|
||||||
|
雛形は `config/guests.example.txt`。
|
||||||
|
|
||||||
|
```text
|
||||||
|
GuestUserA
|
||||||
|
GuestUserB
|
||||||
|
GuestUserC
|
||||||
|
```
|
||||||
|
|
||||||
|
空行と `#` で始まる行は無視する。
|
||||||
|
|
||||||
|
## 秘密情報
|
||||||
|
|
||||||
|
秘密情報は `config/secrets.toml` に置く。このファイルは Git に入れない。
|
||||||
|
|
||||||
|
雛形は `config/secrets.example.toml`。
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[discord]
|
||||||
|
webhook_url = ""
|
||||||
|
|
||||||
|
[vrchat]
|
||||||
|
username = ""
|
||||||
|
password = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
`discord.webhook_url` は VRC ログ結果の Discord Webhook 通知に使う。
|
||||||
|
|
||||||
|
`vrchat.username` / `vrchat.password` は `src/getUser.py` を使う場合だけ必要。
|
||||||
|
|
||||||
## OSC パラメータ
|
## OSC パラメータ
|
||||||
|
|
||||||
@@ -188,6 +206,8 @@ OCR text、翻訳 text、VRC ログ結果は `runtime/runtime.log` に追記す
|
|||||||
|
|
||||||
- `.env`
|
- `.env`
|
||||||
- `config/config.toml`
|
- `config/config.toml`
|
||||||
|
- `config/secrets.toml`
|
||||||
|
- `config/guests.txt`
|
||||||
- `runtime/`
|
- `runtime/`
|
||||||
- `__pycache__/`
|
- `__pycache__/`
|
||||||
- `*.pyc`
|
- `*.pyc`
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ from common.project_paths import ROOT_DIR
|
|||||||
|
|
||||||
CONFIG_DIR = ROOT_DIR / "config"
|
CONFIG_DIR = ROOT_DIR / "config"
|
||||||
CONFIG_FILE = CONFIG_DIR / "config.toml"
|
CONFIG_FILE = CONFIG_DIR / "config.toml"
|
||||||
|
SECRETS_FILE = CONFIG_DIR / "secrets.toml"
|
||||||
DEFAULT_EVENT_FILE = CONFIG_DIR / "event.toml"
|
DEFAULT_EVENT_FILE = CONFIG_DIR / "event.toml"
|
||||||
|
DEFAULT_GUEST_FILE = CONFIG_DIR / "guests.txt"
|
||||||
|
|
||||||
|
|
||||||
def loadTomlFile(path):
|
def loadTomlFile(path):
|
||||||
@@ -26,6 +28,47 @@ def resolveConfigPath(value, default_path):
|
|||||||
return (ROOT_DIR / candidate).resolve()
|
return (ROOT_DIR / candidate).resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def loadNameListFile(path):
|
||||||
|
if not path.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
|
names = []
|
||||||
|
|
||||||
|
for line in path.read_text(encoding="utf-8-sig", errors="replace").splitlines():
|
||||||
|
name = line.strip()
|
||||||
|
if not name or name.startswith("#"):
|
||||||
|
continue
|
||||||
|
names.append(name)
|
||||||
|
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def loadSecretConfig():
|
||||||
|
return loadTomlFile(SECRETS_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
def getSecretValue(section_name, key, default=""):
|
||||||
|
secrets = loadSecretConfig()
|
||||||
|
section = secrets.get(section_name, {})
|
||||||
|
return str(section.get(key, default)).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def loadOcrConfig():
|
||||||
|
config = loadTomlFile(CONFIG_FILE)
|
||||||
|
ocr = config.get("ocr", {})
|
||||||
|
crop = ocr.get("crop", {})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"crop_left": float(crop.get("left", 0.05)),
|
||||||
|
"crop_top": float(crop.get("top", 0.35)),
|
||||||
|
"crop_right": float(crop.get("right", 0.95)),
|
||||||
|
"crop_bottom": float(crop.get("bottom", 0.95)),
|
||||||
|
"preprocess": bool(ocr.get("preprocess", True)),
|
||||||
|
"scale": float(ocr.get("scale", 1.5)),
|
||||||
|
"use_angle_cls": bool(ocr.get("use_angle_cls", False)),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def loadVrcLogConfig():
|
def loadVrcLogConfig():
|
||||||
config = loadTomlFile(CONFIG_FILE)
|
config = loadTomlFile(CONFIG_FILE)
|
||||||
vrc_log = config.get("vrc_log", {})
|
vrc_log = config.get("vrc_log", {})
|
||||||
@@ -43,7 +86,18 @@ def loadVrcLogConfig():
|
|||||||
if not event_config:
|
if not event_config:
|
||||||
event_config = vrc_log or config
|
event_config = vrc_log or config
|
||||||
|
|
||||||
guest_names = event_config.get("guest", {}).get("names", [])
|
guest_section = event_config.get("guest", {})
|
||||||
|
guest_names = guest_section.get("names", [])
|
||||||
|
guest_file_value = (
|
||||||
|
guest_section.get("file")
|
||||||
|
or event_config.get("guest_file")
|
||||||
|
or config.get("guest_file")
|
||||||
|
)
|
||||||
|
guest_file = resolveConfigPath(guest_file_value, DEFAULT_GUEST_FILE)
|
||||||
|
|
||||||
|
if not guest_names:
|
||||||
|
guest_names = loadNameListFile(guest_file)
|
||||||
|
|
||||||
staff_names = event_config.get("staff", {}).get("names", [])
|
staff_names = event_config.get("staff", {}).get("names", [])
|
||||||
|
|
||||||
if not guest_names and isinstance(event_config.get("guest_names"), list):
|
if not guest_names and isinstance(event_config.get("guest_names"), list):
|
||||||
@@ -68,6 +122,7 @@ def loadVrcLogConfig():
|
|||||||
return {
|
return {
|
||||||
"config_file": CONFIG_FILE,
|
"config_file": CONFIG_FILE,
|
||||||
"event_file": event_file,
|
"event_file": event_file,
|
||||||
|
"guest_file": guest_file,
|
||||||
"guest_names": [str(name).strip() for name in guest_names if str(name).strip()],
|
"guest_names": [str(name).strip() for name in guest_names if str(name).strip()],
|
||||||
"staff_names": [str(name).strip() for name in staff_names if str(name).strip()],
|
"staff_names": [str(name).strip() for name in staff_names if str(name).strip()],
|
||||||
"missing_count": int(missing_count),
|
"missing_count": int(missing_count),
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from common.project_paths import ENV_FILE
|
|
||||||
|
|
||||||
|
|
||||||
def readDotEnvValue(key):
|
|
||||||
if not ENV_FILE.exists():
|
|
||||||
return ""
|
|
||||||
|
|
||||||
for line in ENV_FILE.read_text(encoding="utf-8-sig", errors="replace").splitlines():
|
|
||||||
stripped = line.strip()
|
|
||||||
if not stripped or stripped.startswith("#"):
|
|
||||||
continue
|
|
||||||
if not stripped.startswith(key + "="):
|
|
||||||
continue
|
|
||||||
|
|
||||||
value = stripped.split("=", 1)[1].strip()
|
|
||||||
return value.strip('"').strip("'")
|
|
||||||
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def getEnvValue(key, default=""):
|
|
||||||
value = os.environ.get(key, "").strip()
|
|
||||||
if value:
|
|
||||||
return value
|
|
||||||
|
|
||||||
value = readDotEnvValue(key).strip()
|
|
||||||
if value:
|
|
||||||
return value
|
|
||||||
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def getEnvBool(key, default=False):
|
|
||||||
value = getEnvValue(key, "").strip().lower()
|
|
||||||
if not value:
|
|
||||||
return default
|
|
||||||
|
|
||||||
if value in ("1", "true", "yes", "on"):
|
|
||||||
return True
|
|
||||||
if value in ("0", "false", "no", "off"):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def getEnvFloat(key, default=0.0):
|
|
||||||
value = getEnvValue(key, "").strip()
|
|
||||||
if not value:
|
|
||||||
return default
|
|
||||||
|
|
||||||
try:
|
|
||||||
return float(value)
|
|
||||||
except ValueError:
|
|
||||||
return default
|
|
||||||
@@ -4,4 +4,3 @@ SRC_DIR = Path(__file__).resolve().parents[1]
|
|||||||
ROOT_DIR = SRC_DIR.parent
|
ROOT_DIR = SRC_DIR.parent
|
||||||
RUNTIME_DIR = ROOT_DIR / "runtime"
|
RUNTIME_DIR = ROOT_DIR / "runtime"
|
||||||
RUNTIME_LOG_FILE = RUNTIME_DIR / "runtime.log"
|
RUNTIME_LOG_FILE = RUNTIME_DIR / "runtime.log"
|
||||||
ENV_FILE = ROOT_DIR / ".env"
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import os
|
|
||||||
import re
|
import re
|
||||||
from http.cookiejar import MozillaCookieJar
|
from http.cookiejar import MozillaCookieJar
|
||||||
|
|
||||||
@@ -9,6 +8,8 @@ from vrchatapi.exceptions import UnauthorizedException
|
|||||||
from vrchatapi.models.two_factor_auth_code import TwoFactorAuthCode
|
from vrchatapi.models.two_factor_auth_code import TwoFactorAuthCode
|
||||||
from vrchatapi.models.two_factor_email_code import TwoFactorEmailCode
|
from vrchatapi.models.two_factor_email_code import TwoFactorEmailCode
|
||||||
|
|
||||||
|
from common.config_loader import getSecretValue
|
||||||
|
|
||||||
COOKIE_FILE = ".vrchat_cookie.txt"
|
COOKIE_FILE = ".vrchat_cookie.txt"
|
||||||
|
|
||||||
USER_AGENT = (
|
USER_AGENT = (
|
||||||
@@ -17,29 +18,10 @@ USER_AGENT = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def read_dotenv_value(key):
|
def get_required_secret(section, key):
|
||||||
env_path = ".env"
|
value = getSecretValue(section, key)
|
||||||
if not os.path.exists(env_path):
|
|
||||||
return ""
|
|
||||||
|
|
||||||
with open(env_path, "r", encoding="utf-8-sig", errors="replace") as file:
|
|
||||||
for line in file:
|
|
||||||
stripped = line.strip()
|
|
||||||
if not stripped or stripped.startswith("#"):
|
|
||||||
continue
|
|
||||||
if not stripped.startswith(key + "="):
|
|
||||||
continue
|
|
||||||
|
|
||||||
value = stripped.split("=", 1)[1].strip()
|
|
||||||
return value.strip('"').strip("'")
|
|
||||||
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def get_required_secret(key):
|
|
||||||
value = os.environ.get(key, "").strip() or read_dotenv_value(key).strip()
|
|
||||||
if not value:
|
if not value:
|
||||||
raise RuntimeError(f"{key} is not set")
|
raise RuntimeError(f"{section}.{key} is not set in config/secrets.toml")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@@ -126,8 +108,8 @@ def main():
|
|||||||
return
|
return
|
||||||
|
|
||||||
configuration = vrchatapi.Configuration(
|
configuration = vrchatapi.Configuration(
|
||||||
username=get_required_secret("VRCHAT_USERNAME"),
|
username=get_required_secret("vrchat", "username"),
|
||||||
password=get_required_secret("VRCHAT_PASSWORD"),
|
password=get_required_secret("vrchat", "password"),
|
||||||
)
|
)
|
||||||
|
|
||||||
with vrchatapi.ApiClient(configuration) as api_client:
|
with vrchatapi.ApiClient(configuration) as api_client:
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ from PIL import Image
|
|||||||
from PIL import ImageFilter
|
from PIL import ImageFilter
|
||||||
from PIL import ImageOps
|
from PIL import ImageOps
|
||||||
|
|
||||||
from common.env_config import getEnvBool
|
from common.config_loader import loadOcrConfig
|
||||||
from common.env_config import getEnvFloat
|
|
||||||
from common.runtime_log import appendRuntimeLog
|
from common.runtime_log import appendRuntimeLog
|
||||||
from screen.screen_actions import captureOcrRegion
|
from screen.screen_actions import captureOcrRegion
|
||||||
|
|
||||||
@@ -32,9 +31,10 @@ def getOcrEngine():
|
|||||||
|
|
||||||
if _ocr_engine is None:
|
if _ocr_engine is None:
|
||||||
log("INFO", "PaddleOCR initializing")
|
log("INFO", "PaddleOCR initializing")
|
||||||
|
config = loadOcrConfig()
|
||||||
options = {
|
options = {
|
||||||
"lang": "japan",
|
"lang": "japan",
|
||||||
"use_angle_cls": getEnvBool("OCR_USE_ANGLE_CLS", False),
|
"use_angle_cls": config["use_angle_cls"],
|
||||||
"show_log": False,
|
"show_log": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,8 @@ def printOcrResult(text):
|
|||||||
|
|
||||||
|
|
||||||
def preprocessOcrImage(image_path):
|
def preprocessOcrImage(image_path):
|
||||||
enabled = getEnvBool("OCR_PREPROCESS", True)
|
config = loadOcrConfig()
|
||||||
|
enabled = config["preprocess"]
|
||||||
if not enabled:
|
if not enabled:
|
||||||
return image_path
|
return image_path
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ def preprocessOcrImage(image_path):
|
|||||||
image = ImageOps.autocontrast(image)
|
image = ImageOps.autocontrast(image)
|
||||||
image = image.filter(ImageFilter.SHARPEN)
|
image = image.filter(ImageFilter.SHARPEN)
|
||||||
|
|
||||||
scale = max(1.0, min(2.0, getEnvFloat("OCR_SCALE", 1.5)))
|
scale = max(1.0, min(2.0, config["scale"]))
|
||||||
if scale > 1.0:
|
if scale > 1.0:
|
||||||
width, height = image.size
|
width, height = image.size
|
||||||
resampling = getattr(
|
resampling = getattr(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import pyautogui
|
import pyautogui
|
||||||
import pygetwindow as gw
|
import pygetwindow as gw
|
||||||
|
|
||||||
from common.env_config import getEnvFloat
|
from common.config_loader import loadOcrConfig
|
||||||
from common.project_paths import RUNTIME_DIR
|
from common.project_paths import RUNTIME_DIR
|
||||||
|
|
||||||
SCREENSHOT_PATH = RUNTIME_DIR / "latest_screenshot.png"
|
SCREENSHOT_PATH = RUNTIME_DIR / "latest_screenshot.png"
|
||||||
@@ -100,13 +100,14 @@ def clampRatio(value):
|
|||||||
|
|
||||||
def getOcrRegion():
|
def getOcrRegion():
|
||||||
left, top, width, height = getVrchatRegion()
|
left, top, width, height = getVrchatRegion()
|
||||||
|
config = loadOcrConfig()
|
||||||
|
|
||||||
# Default to the lower central area. It is much faster than OCRing the
|
# Default to the lower central area. It is much faster than OCRing the
|
||||||
# whole VRChat window and usually contains chat/subtitle text.
|
# whole VRChat window and usually contains chat/subtitle text.
|
||||||
crop_left = clampRatio(getEnvFloat("OCR_CROP_LEFT", 0.05))
|
crop_left = clampRatio(config["crop_left"])
|
||||||
crop_top = clampRatio(getEnvFloat("OCR_CROP_TOP", 0.35))
|
crop_top = clampRatio(config["crop_top"])
|
||||||
crop_right = clampRatio(getEnvFloat("OCR_CROP_RIGHT", 0.95))
|
crop_right = clampRatio(config["crop_right"])
|
||||||
crop_bottom = clampRatio(getEnvFloat("OCR_CROP_BOTTOM", 0.95))
|
crop_bottom = clampRatio(config["crop_bottom"])
|
||||||
|
|
||||||
if crop_right <= crop_left:
|
if crop_right <= crop_left:
|
||||||
crop_left = 0.0
|
crop_left = 0.0
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from datetime import timedelta
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from common.config_loader import loadVrcLogConfig
|
from common.config_loader import loadVrcLogConfig
|
||||||
from common.project_paths import ENV_FILE
|
from common.config_loader import getSecretValue
|
||||||
from common.runtime_log import appendRuntimeLog
|
from common.runtime_log import appendRuntimeLog
|
||||||
from discord.discord_actions import setDiscordMuted
|
from discord.discord_actions import setDiscordMuted
|
||||||
|
|
||||||
@@ -49,34 +49,14 @@ def log(level, message):
|
|||||||
print(f"[{level}] {now} {message}", flush=True)
|
print(f"[{level}] {now} {message}", flush=True)
|
||||||
|
|
||||||
|
|
||||||
def readDotEnvValue(key):
|
|
||||||
if not ENV_FILE.exists():
|
|
||||||
return ""
|
|
||||||
|
|
||||||
for line in ENV_FILE.read_text(encoding="utf-8-sig", errors="replace").splitlines():
|
|
||||||
stripped = line.strip()
|
|
||||||
if not stripped or stripped.startswith("#"):
|
|
||||||
continue
|
|
||||||
if not stripped.startswith(key + "="):
|
|
||||||
continue
|
|
||||||
|
|
||||||
value = stripped.split("=", 1)[1].strip()
|
|
||||||
return value.strip('"').strip("'")
|
|
||||||
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def getWebhookUrl():
|
def getWebhookUrl():
|
||||||
webhook_url = os.environ.get("DISCORD_WEBHOOK_URL", "").strip()
|
return getSecretValue("discord", "webhook_url")
|
||||||
if webhook_url:
|
|
||||||
return webhook_url
|
|
||||||
return readDotEnvValue("DISCORD_WEBHOOK_URL")
|
|
||||||
|
|
||||||
|
|
||||||
def sendWebhook(text):
|
def sendWebhook(text):
|
||||||
webhook_url = getWebhookUrl().strip()
|
webhook_url = getWebhookUrl().strip()
|
||||||
if not webhook_url:
|
if not webhook_url:
|
||||||
log("ERROR", f"DISCORD_WEBHOOK_URL が未設定です env_file={ENV_FILE}")
|
log("ERROR", "Discord webhook URL is not set config=config/secrets.toml")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
message = str(text).strip()
|
message = str(text).strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user