feat(settings): remove user agent override

This commit is contained in:
Artificial Sweetener
2026-04-04 21:13:51 -04:00
parent e32233920d
commit 26b3a05be4
8 changed files with 63 additions and 30 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ Open ComfyUI Settings and look for the **Mutiny** section. From there, configure
- **Discord Token**: The private account credential Discord uses to identify
and authenticate your logged-in session. **This is effecitvely your username and password rolled into one**. Mutiny saves the supplied token into your operating system's secure credential storage.
> This project does **not** provide instructions for obtaining your Discord token, and you should read the disclaimer above before deciding whether to use one with Mutiny.
- **User Agent** and **API Endpoint**: Optional overrides for advanced cases. You can probably leave these alone.
- **API Endpoint**: Optional advanced override. You can probably leave this alone.
- **Artifact Cache RAM** and **Artifact Disk**: Control how much context Mutiny keeps around for recognizing earlier Midjourney outputs.
- **Task Timeout Minutes**: Controls how long ComfyUI waits before a running job times out.
+1 -1
View File
@@ -57,7 +57,7 @@ ComfyUI-Mutiny 在正式起航之前需要做一点配置。
- **Channel ID**:Mutiny 应该提交任务所使用的 Discord 频道。
- **Discord Token**:Discord 用来识别并验证你当前登录会话的私密账户凭据。**这实际上等同于把你的用户名和密码合并成一个值**。Mutiny 会把你提供的 token 保存到操作系统的安全凭据存储中。
> 本项目**不会**提供如何获取 Discord token 的说明。在决定是否将 token 与 Mutiny 一起使用之前,请先阅读上面的免责声明。
- **User Agent** 和 **API Endpoint**:面向高级场景的可选覆盖项。大多数情况下你可以保持默认。
- **API Endpoint**:面向高级场景的可选覆盖项。大多数情况下你可以保持默认。
- **Artifact Cache RAM** 和 **Artifact Disk**:控制 Mutiny 为识别先前 Midjourney 输出而保留多少上下文。
- **Task Timeout Minutes**:控制 ComfyUI 在运行中的任务超时之前等待多久。
+13 -6
View File
@@ -5,9 +5,6 @@ from __future__ import annotations
from dataclasses import asdict, dataclass, field, fields, is_dataclass, replace
from typing import Any, Mapping, MutableMapping
_DEFAULT_USER_AGENT = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0"
)
_DEFAULT_API_ENDPOINT = "https://discord.com/api/v9"
@@ -22,11 +19,10 @@ class SettingsMetadata:
@dataclass(frozen=True)
class DiscordSettings:
"""Store Discord identifiers and optional transport metadata."""
"""Store Discord identifiers and optional API endpoint override."""
guild_id: str = ""
channel_id: str = ""
user_agent: str = _DEFAULT_USER_AGENT
api_endpoint: str = _DEFAULT_API_ENDPOINT
@@ -64,7 +60,7 @@ class PluginSettings:
@classmethod
def from_dict(cls, data: Mapping[str, Any] | None) -> "PluginSettings":
"""Build a validated settings snapshot from persisted JSON data."""
payload = data or {}
payload = _drop_removed_settings_keys(data or {})
return cls(
metadata=_coerce_section(
SettingsMetadata, payload.get("metadata"), "metadata"
@@ -91,6 +87,17 @@ class PluginSettings:
return replace(self, **applied)
def _drop_removed_settings_keys(data: Mapping[str, Any]) -> dict[str, Any]:
"""Return persisted settings with known removed keys stripped out."""
payload = dict(data)
discord = payload.get("discord")
if isinstance(discord, Mapping) and "user_agent" in discord:
payload["discord"] = {
key: value for key, value in discord.items() if key != "user_agent"
}
return payload
def _coerce_section(section_cls, value: Any, label: str):
"""Coerce a persisted section into its dataclass representation."""
if value is None:
+1 -7
View File
@@ -101,7 +101,6 @@ class SettingsService:
token_provider=self.create_token_provider(),
guild_id=settings.discord.guild_id,
channel_id=settings.discord.channel_id,
user_agent=settings.discord.user_agent,
api_endpoint=settings.discord.api_endpoint,
cache=self._build_mutiny_cache_config(settings),
execution={
@@ -210,7 +209,7 @@ class SettingsService:
self._assert_known_keys(
section_name,
section_value,
{"guild_id", "channel_id", "user_agent", "api_endpoint"},
{"guild_id", "channel_id", "api_endpoint"},
)
normalized = {}
if "guild_id" in section_value:
@@ -221,11 +220,6 @@ class SettingsService:
normalized["channel_id"] = self._normalize_string(
section_value["channel_id"]
)
if "user_agent" in section_value:
normalized["user_agent"] = self._normalize_optional_string(
section_value["user_agent"],
default_value=PluginSettings().discord.user_agent,
)
if "api_endpoint" in section_value:
normalized["api_endpoint"] = self._normalize_optional_string(
section_value["api_endpoint"],
+36 -1
View File
@@ -41,7 +41,6 @@ def test_repository_round_trips_settings_snapshot(tmp_path):
"api_endpoint": original.discord.api_endpoint,
"channel_id": "channel-456",
"guild_id": "guild-123",
"user_agent": original.discord.user_agent,
}
@@ -53,3 +52,39 @@ def test_repository_raises_for_invalid_json(tmp_path):
with pytest.raises(RuntimeError, match="not valid JSON"):
repository.load()
def test_repository_ignores_removed_user_agent_from_existing_settings_file(tmp_path):
"""Load older settings files without preserving the removed user-agent override."""
settings_path = tmp_path / "mutiny.settings.json"
settings_path.write_text(
json.dumps(
{
"metadata": {
"schema_version": 1,
"migration_completed": True,
"migrated_from_legacy": False,
},
"discord": {
"guild_id": "guild-123",
"channel_id": "channel-456",
"user_agent": "legacy-agent",
"api_endpoint": "https://discord.com/api/v9",
},
"cache": {
"artifact_cache_ram_max_mb": 32,
"disk_cache_max_mb": 256,
},
"engine": {"execution": {"task_timeout_minutes": 5}},
}
),
encoding="utf-8",
)
repository = SettingsRepository(settings_path)
loaded = repository.load()
assert loaded.discord.guild_id == "guild-123"
assert loaded.discord.channel_id == "channel-456"
assert loaded.discord.api_endpoint == "https://discord.com/api/v9"
assert not hasattr(loaded.discord, "user_agent")
+10
View File
@@ -215,6 +215,16 @@ def test_service_reports_unknown_settings_sections(tmp_path):
service.save_settings({"unknown": {}})
def test_service_rejects_removed_user_agent_override_payloads(tmp_path):
"""Reject removed user-agent overrides instead of silently persisting them."""
service, _token_store = build_service(tmp_path)
with pytest.raises(
SettingsValidationError, match="Unknown discord keys: user_agent"
):
service.save_settings({"discord": {"user_agent": "legacy-agent"}})
def test_service_persists_migration_completion_when_no_legacy_files_exist(tmp_path):
"""Mark migration complete even when there is nothing to import."""
service, _token_store = build_service(tmp_path)
+1 -1
View File
@@ -15,7 +15,6 @@ def test_settings_ui_uses_native_grouped_mutiny_rows():
'id: "Mutiny.Discord.GuildId"',
'id: "Mutiny.Discord.ChannelId"',
'id: "Mutiny.Discord.DiscordToken"',
'id: "Mutiny.Discord.UserAgent"',
'id: "Mutiny.Discord.ApiEndpoint"',
'id: "Mutiny.Cache.ArtifactCacheRamMaxMb"',
'id: "Mutiny.Cache.ArtifactCacheRamHelp"',
@@ -172,6 +171,7 @@ def test_settings_ui_uses_native_grouped_mutiny_rows():
'id: "Mutiny.General.Actions"',
'id: "Mutiny.General.Status"',
'id: "Mutiny.Discord.Description"',
'id: "Mutiny.Discord.UserAgent"',
'id: "Mutiny.Cache.Description"',
"sectionDescriptionParts",
'"Test Configuration"',
-13
View File
@@ -200,7 +200,6 @@ function createDefaultSnapshot() {
guildId: "",
channelId: "",
tokenConfigured: false,
userAgent: "",
apiEndpoint: "",
},
cache: {
@@ -229,7 +228,6 @@ function normalizeSettingsStatus(payload = {}) {
guildId: discord.guild_id || "",
channelId: discord.channel_id || "",
tokenConfigured: Boolean(payload.token_configured),
userAgent: discord.user_agent || "",
apiEndpoint: discord.api_endpoint || "",
},
cache: {
@@ -1874,17 +1872,6 @@ const TEXT_ROW_DEFINITIONS = [
parseValue: (value) => value,
buildPayload: (value) => ({ discord: { channel_id: value } }),
},
{
id: "Mutiny.Discord.UserAgent",
name: "User Agent",
section: "discord",
tooltip: "Override the Discord HTTP user agent when needed.",
sortOrder: 370,
readValue: (snapshot) => snapshot.discord.userAgent,
formatValue: (value) => String(value || ""),
parseValue: (value) => value,
buildPayload: (value) => ({ discord: { user_agent: value } }),
},
{
id: "Mutiny.Discord.ApiEndpoint",
name: "API Endpoint",