redfetch 1.3.0__tar.gz → 1.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {redfetch-1.3.0 → redfetch-1.4.1}/.github/workflows/pipeline.yml +28 -12
- {redfetch-1.3.0 → redfetch-1.4.1}/.gitignore +4 -0
- redfetch-1.4.1/AGENTS.md +21 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/CHANGELOG.md +19 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/PKG-INFO +50 -44
- {redfetch-1.3.0 → redfetch-1.4.1}/README.md +45 -39
- {redfetch-1.3.0 → redfetch-1.4.1}/pyproject.toml +9 -3
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/__about__.py +2 -2
- redfetch-1.4.1/src/redfetch/api.py +50 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/config.py +31 -19
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/detecteq.py +26 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/download.py +199 -36
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/listener.py +14 -4
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/main.py +131 -163
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/navmesh.py +32 -98
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/net.py +1 -10
- redfetch-1.4.1/src/redfetch/post_update.py +143 -0
- redfetch-1.4.1/src/redfetch/processes.py +336 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/runtime_errors.py +1 -3
- redfetch-1.4.1/src/redfetch/shortcuts.py +229 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/store.py +11 -15
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync.py +74 -29
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_discovery.py +17 -53
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_planner.py +1 -3
- redfetch-1.4.1/src/redfetch/sync_remote.py +135 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_types.py +33 -4
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/terminal_ui.py +404 -590
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/terminal_ui.tcss +25 -77
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/utils.py +56 -2
- redfetch-1.4.1/tests/cli_reference.py +138 -0
- redfetch-1.4.1/tests/test_autologin_eqpath.py +74 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_check.py +2 -2
- redfetch-1.4.1/tests/test_close_gating.py +800 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_env_selection.py +19 -15
- redfetch-1.4.1/tests/test_fetch_sync_info.py +67 -0
- redfetch-1.4.1/tests/test_licensed_resources_filtering.py +83 -0
- redfetch-1.4.1/tests/test_process_lock.py +48 -0
- redfetch-1.4.1/tests/test_readme_cli_reference.py +36 -0
- redfetch-1.4.1/tests/test_remote_snapshot.py +97 -0
- redfetch-1.4.1/tests/test_self_heal_eqpath.py +198 -0
- redfetch-1.4.1/tests/test_shortcuts.py +250 -0
- redfetch-1.4.1/tests/test_staged_extraction.py +340 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_sync_queue_rules.py +29 -8
- redfetch-1.3.0/src/redfetch/api.py +0 -134
- redfetch-1.3.0/src/redfetch/processes.py +0 -118
- redfetch-1.3.0/src/redfetch/sync_remote.py +0 -182
- redfetch-1.3.0/src/redfetch/unloadmq.py +0 -148
- redfetch-1.3.0/tests/test_breadcrumb.py +0 -56
- redfetch-1.3.0/tests/test_cancellation_e2e.py +0 -123
- redfetch-1.3.0/tests/test_licensed_resources_filtering.py +0 -320
- redfetch-1.3.0/tests/test_publish_message_file_fallback.py +0 -158
- redfetch-1.3.0/tests/test_uninstall_e2e.py +0 -233
- {redfetch-1.3.0 → redfetch-1.4.1}/LICENSE +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/redfetch.ico +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/__init__.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/auth.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/config_firstrun.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/desktop_shortcut.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/meta.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/push.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/settings.toml +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/special.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_executor.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/update_status.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_first_run_setup.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_is_special_or_dependency.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_path_reset.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_settings_local_deltas.py +0 -0
- {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_sync_pipeline_regressions.py +0 -0
|
@@ -60,6 +60,13 @@ jobs:
|
|
|
60
60
|
run: |
|
|
61
61
|
hatch build
|
|
62
62
|
|
|
63
|
+
- name: Upload Python distribution
|
|
64
|
+
if: github.ref == 'refs/heads/develop'
|
|
65
|
+
uses: actions/upload-artifact@v7
|
|
66
|
+
with:
|
|
67
|
+
name: python-dist
|
|
68
|
+
path: dist/
|
|
69
|
+
|
|
63
70
|
- name: Get Version
|
|
64
71
|
id: get_version
|
|
65
72
|
run: |
|
|
@@ -89,22 +96,26 @@ jobs:
|
|
|
89
96
|
with:
|
|
90
97
|
fetch-depth: 0 # Ensure full Git history is available for versioning
|
|
91
98
|
|
|
99
|
+
- name: Download Python distribution
|
|
100
|
+
if: github.ref == 'refs/heads/develop'
|
|
101
|
+
uses: actions/download-artifact@v8
|
|
102
|
+
with:
|
|
103
|
+
name: python-dist
|
|
104
|
+
path: dist
|
|
105
|
+
|
|
92
106
|
- name: Set Environment Variables
|
|
93
107
|
id: set_env_vars
|
|
94
108
|
shell: pwsh
|
|
95
109
|
run: |
|
|
96
|
-
# Set static environment variables
|
|
97
|
-
"PYAPP_PROJECT_NAME=redfetch" >> $env:GITHUB_ENV
|
|
98
110
|
"PYAPP_EXEC_MODULE=redfetch.main" >> $env:GITHUB_ENV
|
|
99
111
|
"PYAPP_PASS_LOCATION=1" >> $env:GITHUB_ENV
|
|
100
112
|
|
|
101
|
-
# Conditionally set PYAPP_PIP_EXTRA_ARGS based on the branch or tag
|
|
102
113
|
if ($env:GITHUB_REF -eq 'refs/heads/develop') {
|
|
103
|
-
|
|
114
|
+
"PYAPP_EMBED_WHEEL=1" >> $env:GITHUB_ENV
|
|
104
115
|
} else {
|
|
105
|
-
|
|
116
|
+
"PYAPP_PROJECT_NAME=redfetch" >> $env:GITHUB_ENV
|
|
106
117
|
}
|
|
107
|
-
"PYAPP_PIP_EXTRA_ARGS
|
|
118
|
+
"PYAPP_PIP_EXTRA_ARGS=" >> $env:GITHUB_ENV
|
|
108
119
|
|
|
109
120
|
# Output the values for verification
|
|
110
121
|
Get-Content $env:GITHUB_ENV
|
|
@@ -145,18 +156,23 @@ jobs:
|
|
|
145
156
|
|
|
146
157
|
- name: Build PyApp Executable
|
|
147
158
|
run: |
|
|
148
|
-
$env:
|
|
159
|
+
if ($env:PYAPP_EMBED_WHEEL -eq '1') {
|
|
160
|
+
# Develop embeds the built wheel so its deps resolve from PyPI rather than TestPyPI
|
|
161
|
+
$wheel = Get-ChildItem -Path "$env:GITHUB_WORKSPACE\dist\*.whl" | Select-Object -First 1
|
|
162
|
+
if (-not $wheel) { throw "No redfetch wheel found in dist/" }
|
|
163
|
+
$env:PYAPP_PROJECT_PATH = $wheel.FullName
|
|
164
|
+
Remove-Item Env:PYAPP_PROJECT_NAME -ErrorAction SilentlyContinue
|
|
165
|
+
Remove-Item Env:PYAPP_PROJECT_VERSION -ErrorAction SilentlyContinue
|
|
166
|
+
}
|
|
167
|
+
|
|
149
168
|
$env:PYAPP_EXEC_MODULE = "${{ env.PYAPP_EXEC_MODULE }}"
|
|
150
169
|
$env:PYAPP_PASS_LOCATION = "${{ env.PYAPP_PASS_LOCATION }}"
|
|
151
170
|
$env:PYAPP_PIP_EXTRA_ARGS = "${{ env.PYAPP_PIP_EXTRA_ARGS }}"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Write-Host "PYAPP_PROJECT_NAME: $env:PYAPP_PROJECT_NAME"
|
|
171
|
+
|
|
155
172
|
Write-Host "PYAPP_EXEC_MODULE: $env:PYAPP_EXEC_MODULE"
|
|
156
173
|
Write-Host "PYAPP_PASS_LOCATION: $env:PYAPP_PASS_LOCATION"
|
|
157
174
|
Write-Host "PYAPP_PIP_EXTRA_ARGS: $env:PYAPP_PIP_EXTRA_ARGS"
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
|
|
160
176
|
cargo build --release
|
|
161
177
|
working-directory: .\pyapp-latest
|
|
162
178
|
shell: pwsh
|
redfetch-1.4.1/AGENTS.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
**redfetch** — a CLI/TUI for downloading and publishing MacroQuest and EverQuest scripts and software via the RedGuides API.
|
|
4
|
+
|
|
5
|
+
## Running tests
|
|
6
|
+
|
|
7
|
+
Use `hatch test` — not `pytest`, `uv`, or an ad-hoc venv
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
hatch test # full suite
|
|
11
|
+
hatch test tests/test_check.py # single file
|
|
12
|
+
```
|
|
13
|
+
## Libraries
|
|
14
|
+
|
|
15
|
+
- [Textual](https://textual.textualize.io/) - TUI
|
|
16
|
+
- [Hatch](https://hatch.pypa.io/latest/) - Build system
|
|
17
|
+
- [PYPA](https://www.pypa.io/en/latest/specifications/) - Package index
|
|
18
|
+
|
|
19
|
+
## Conventions
|
|
20
|
+
|
|
21
|
+
- Use the `dev` environment (`hatch shell dev`) for development
|
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.4.1] - 2026-07-05
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- The update button once again doubles as a *launch MQ* button, even when no update occurs.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- `redfetch run` and `redfetch open` CLI commands act as shortcuts, for example `redfetch run eqgame` will start EQgame.exe, `redfetch open eqhost` will open the eqhost.txt file, etc. These mirror the TUI shortcuts.
|
|
15
|
+
|
|
16
|
+
## [1.4.0] - 2026-07-05
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- You can now update while playing. Updates take effect the next time you start Very Vanilla MQ.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- New logic for restarting VVMQ after an update. redfetch will never touch EQ nor unload MQ.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
- The "Close MQ pre-update" setting and TUI toggle.
|
|
26
|
+
|
|
8
27
|
## [1.3.0] - 2026-06-26
|
|
9
28
|
|
|
10
29
|
### Added
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redfetch
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Download and publish EverQuest scripts and software using the RedGuides API
|
|
5
5
|
Project-URL: Homepage, https://www.redguides.com
|
|
6
6
|
Project-URL: Documentation, https://www.redguides.com/community/resources/redfetch.3177/
|
|
7
7
|
Project-URL: Repository, https://github.com/RedGuides/redfetch
|
|
8
8
|
Project-URL: Issues, https://github.com/RedGuides/redfetch/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/RedGuides/redfetch/blob/main/CHANGELOG.md
|
|
10
|
-
Project-URL: Source_archive, https://github.com/RedGuides/redfetch/archive/
|
|
10
|
+
Project-URL: Source_archive, https://github.com/RedGuides/redfetch/archive/6c3be23e13fb9b1cb6eed3c065b930111c484175.zip
|
|
11
11
|
Author-email: Redbot <ask@redguides.com>
|
|
12
12
|
License-Expression: GPL-3.0-or-later
|
|
13
13
|
License-File: LICENSE
|
|
@@ -21,9 +21,10 @@ Requires-Python: >=3.12
|
|
|
21
21
|
Requires-Dist: aiofiles~=25.1
|
|
22
22
|
Requires-Dist: aiohttp~=3.14
|
|
23
23
|
Requires-Dist: aiosqlite~=0.22
|
|
24
|
-
Requires-Dist: cachetools~=7.1
|
|
25
24
|
Requires-Dist: diskcache~=5.6
|
|
26
|
-
Requires-Dist: dynaconf
|
|
25
|
+
Requires-Dist: dynaconf>=3.3.2,~=3.3
|
|
26
|
+
Requires-Dist: filelock~=3.29
|
|
27
|
+
Requires-Dist: hishel[async,httpx]~=1.3
|
|
27
28
|
Requires-Dist: httpx[http2]~=0.28
|
|
28
29
|
Requires-Dist: keepachangelog~=2.0
|
|
29
30
|
Requires-Dist: keyring~=25.7
|
|
@@ -32,7 +33,6 @@ Requires-Dist: packaging~=26.2
|
|
|
32
33
|
Requires-Dist: platformdirs~=4.10
|
|
33
34
|
Requires-Dist: psutil~=7.2
|
|
34
35
|
Requires-Dist: pydantic~=2.13
|
|
35
|
-
Requires-Dist: pyperclip~=1.11
|
|
36
36
|
Requires-Dist: pywin32; sys_platform == 'win32'
|
|
37
37
|
Requires-Dist: rich~=15.0
|
|
38
38
|
Requires-Dist: tenacity~=9.1
|
|
@@ -105,42 +105,56 @@ To run redfetch from the command line:
|
|
|
105
105
|
|
|
106
106
|
## Command Line Reference
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
>
|
|
108
|
+
<!-- BEGIN GENERATED CLI REFERENCE -->
|
|
109
|
+
> Run `redfetch --help` for the current list, or `redfetch <COMMAND> --help` for a command's options. It looks like:
|
|
110
|
+
>
|
|
110
111
|
> ### 📦 Resource Management
|
|
111
|
-
> - `update` - Update all watched and special resources
|
|
112
|
-
> - `--force` / `-f` - Force re-download of all watched resources
|
|
113
|
-
> - `--server` / `-s` -
|
|
114
|
-
> - `download <ID_OR_URL>` - Download a specific resource by ID or URL
|
|
115
|
-
> -
|
|
116
|
-
> - `--
|
|
117
|
-
>
|
|
118
|
-
> - `
|
|
119
|
-
>
|
|
112
|
+
> - `update` - Update all *watched* and special resources.
|
|
113
|
+
> - `--force` / `-f` - Force re-download of all watched resources.
|
|
114
|
+
> - `--server` / `-s` - Update this server for this run only, without changing your current server (LIVE, TEST, EMU).
|
|
115
|
+
> - `download <ID_OR_URL>` - Download a specific resource by ID or URL.
|
|
116
|
+
> - `ID_OR_URL` - RedGuides resource ID or URL
|
|
117
|
+
> - `--force` / `-f` - Force re-download by resetting this resource's download date.
|
|
118
|
+
> - `--server` / `-s` - Download from this server for this run only, without changing your current server (LIVE, TEST, EMU).
|
|
119
|
+
> - `check` - Non-interactive update check (for automation.)
|
|
120
|
+
> - `--server` / `-s` - Check this server's env for this run only, without persisting it (LIVE, TEST, EMU).
|
|
121
|
+
> - `list` - List resources and dependencies in your local cache.
|
|
122
|
+
> - `reset` - Reset download dates for *watched resources* in the database.
|
|
123
|
+
>
|
|
124
|
+
> ### 🔧 System & Utilities
|
|
125
|
+
> - `ui` - Launch the *Terminal User Interface*.
|
|
126
|
+
> - `run [SHORTCUT]` - Run a shortcut (e.g. **vvmq**, **eqbcs**, **myseq**). **run** by itself will show a full list.
|
|
127
|
+
> - `SHORTCUT` - Shortcut to run: vvmq, eqbcs, eq, eqgame, etc.
|
|
128
|
+
> - `--server` / `-s` - Run for this server this run only, without changing your current server (LIVE, TEST, EMU).
|
|
129
|
+
> - `open [SHORTCUT]` - Open a folder or file (e.g. **downloads**, **eqhost**). **open** by itself will show a full list.
|
|
130
|
+
> - `SHORTCUT` - Folder/file to open: downloads, vvmq, eq, etc.
|
|
131
|
+
> - `--server` / `-s` - Resolve paths for this server this run only, without changing your current server (LIVE, TEST, EMU).
|
|
132
|
+
> - `web` - Launch the **RedGuides.com** web interface.
|
|
133
|
+
> - `version` - Show version and exit.
|
|
134
|
+
> - `uninstall` - Uninstall **redfetch** and clean up data.
|
|
135
|
+
> - `logout` - Log out and clear cached token and API cache.
|
|
136
|
+
>
|
|
120
137
|
> ### 🍔 Configuration
|
|
121
|
-
> - `
|
|
122
|
-
>
|
|
123
|
-
> - `SETTING_PATH` - Dot-separated setting path (e.g., `SPECIAL_RESOURCES.1974.opt_in`)
|
|
138
|
+
> - `config <SETTING_PATH> <VALUE>` - Update a setting by path and value.
|
|
139
|
+
> - `SETTING_PATH` - Dot-separated setting path (e.g., SPECIAL_RESOURCES.1974.opt_in)
|
|
124
140
|
> - `VALUE` - New value for the setting
|
|
125
|
-
> - `--server` / `-s` - Server to apply the change in (
|
|
126
|
-
> - `
|
|
141
|
+
> - `--server` / `-s` - Server to apply the change in (LIVE, TEST, EMU)
|
|
142
|
+
> - `server <SERVER>` - Switch the current server/environment to LIVE, TEST, or EMU.
|
|
143
|
+
> - `SERVER` - Server to use: LIVE, TEST, EMU
|
|
144
|
+
> - `status` - Show the configuration for the current or specified server.
|
|
127
145
|
> - `--server` / `-s` - Server to show (defaults to current)
|
|
128
|
-
>
|
|
129
|
-
> ### 🔧 System & Utilities
|
|
130
|
-
> - `ui` - Launch the Terminal User Interface
|
|
131
|
-
> - `web` - Launch the RedGuides.com web interface
|
|
132
|
-
> - `version` - Show version and exit
|
|
133
|
-
> - `logout` - Disconnect your account from redfetch
|
|
134
|
-
> - `uninstall` - Uninstall redfetch and clean up data
|
|
135
|
-
>
|
|
146
|
+
>
|
|
136
147
|
> ### 📤 Publishing
|
|
137
|
-
> - `publish <
|
|
138
|
-
> - `
|
|
139
|
-
> - `--description <README.md>` / `-d` - Path to a file (e.g.
|
|
140
|
-
> - `--version
|
|
141
|
-
> - `--message <CHANGELOG.md | MESSAGE>` / `-m` -
|
|
148
|
+
> - `publish <RESOURCE_ID>` - Publish updates to a **RedGuides** resource.
|
|
149
|
+
> - `RESOURCE_ID` - Existing RedGuides resource ID
|
|
150
|
+
> - `--description <README.md>` / `-d` - Path to a description file (e.g. README.md) to become the overview description.
|
|
151
|
+
> - `--version` / `-v` - New version string (e.g., v1.0.1)
|
|
152
|
+
> - `--message <CHANGELOG.md | MESSAGE>` / `-m` - Path to *CHANGELOG.md* (keep a changelog), other message file, or a direct message string.
|
|
142
153
|
> - `--file <FILE.zip>` / `-f` - Path to your zipped release file
|
|
143
|
-
> - `--domain
|
|
154
|
+
> - `--domain` - If description or message is a .md file with relative URLs, resolve them to this domain (e.g., https://raw.githubusercontent.com/your/repo/main/)
|
|
155
|
+
<!-- END GENERATED CLI REFERENCE -->
|
|
156
|
+
|
|
157
|
+
The `publish` command also has a [GitHub Action](https://github.com/marketplace/actions/redguides-publish).
|
|
144
158
|
|
|
145
159
|
## Settings
|
|
146
160
|
|
|
@@ -163,7 +177,7 @@ opt_in = true
|
|
|
163
177
|
```
|
|
164
178
|
* Note the use of single quotes around the path, which are required for windows paths.
|
|
165
179
|
|
|
166
|
-
The above will install Brewall's maps to the EQ maps directory the next time
|
|
180
|
+
The above will install Brewall's maps to the EQ maps directory the next time `redfetch update` is run for `LIVE` servers.
|
|
167
181
|
|
|
168
182
|
### Overwrite protection
|
|
169
183
|
|
|
@@ -220,14 +234,6 @@ command = ["C:\\Users\\Public\\Python\\python.exe", "C:\\Users\\Public\\redfetch
|
|
|
220
234
|
|
|
221
235
|
You can set these per-server, e.g. `[TEST.POST_UPDATE_LAUNCH]`, or global `[DEFAULT.POST_UPDATE_LAUNCH]`.
|
|
222
236
|
|
|
223
|
-
|
|
224
|
-
## Trailmap
|
|
225
|
-
- Add custom buttons for "fetch" tab.
|
|
226
|
-
- Option: Close after update
|
|
227
|
-
- Indicate when updated VV is available
|
|
228
|
-
- Run from MQ
|
|
229
|
-
- Deeper integration with the forums
|
|
230
|
-
|
|
231
237
|

|
|
232
238
|
|
|
233
239
|
## Contributing
|
|
@@ -61,42 +61,56 @@ To run redfetch from the command line:
|
|
|
61
61
|
|
|
62
62
|
## Command Line Reference
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
>
|
|
64
|
+
<!-- BEGIN GENERATED CLI REFERENCE -->
|
|
65
|
+
> Run `redfetch --help` for the current list, or `redfetch <COMMAND> --help` for a command's options. It looks like:
|
|
66
|
+
>
|
|
66
67
|
> ### 📦 Resource Management
|
|
67
|
-
> - `update` - Update all watched and special resources
|
|
68
|
-
> - `--force` / `-f` - Force re-download of all watched resources
|
|
69
|
-
> - `--server` / `-s` -
|
|
70
|
-
> - `download <ID_OR_URL>` - Download a specific resource by ID or URL
|
|
71
|
-
> -
|
|
72
|
-
> - `--
|
|
73
|
-
>
|
|
74
|
-
> - `
|
|
75
|
-
>
|
|
68
|
+
> - `update` - Update all *watched* and special resources.
|
|
69
|
+
> - `--force` / `-f` - Force re-download of all watched resources.
|
|
70
|
+
> - `--server` / `-s` - Update this server for this run only, without changing your current server (LIVE, TEST, EMU).
|
|
71
|
+
> - `download <ID_OR_URL>` - Download a specific resource by ID or URL.
|
|
72
|
+
> - `ID_OR_URL` - RedGuides resource ID or URL
|
|
73
|
+
> - `--force` / `-f` - Force re-download by resetting this resource's download date.
|
|
74
|
+
> - `--server` / `-s` - Download from this server for this run only, without changing your current server (LIVE, TEST, EMU).
|
|
75
|
+
> - `check` - Non-interactive update check (for automation.)
|
|
76
|
+
> - `--server` / `-s` - Check this server's env for this run only, without persisting it (LIVE, TEST, EMU).
|
|
77
|
+
> - `list` - List resources and dependencies in your local cache.
|
|
78
|
+
> - `reset` - Reset download dates for *watched resources* in the database.
|
|
79
|
+
>
|
|
80
|
+
> ### 🔧 System & Utilities
|
|
81
|
+
> - `ui` - Launch the *Terminal User Interface*.
|
|
82
|
+
> - `run [SHORTCUT]` - Run a shortcut (e.g. **vvmq**, **eqbcs**, **myseq**). **run** by itself will show a full list.
|
|
83
|
+
> - `SHORTCUT` - Shortcut to run: vvmq, eqbcs, eq, eqgame, etc.
|
|
84
|
+
> - `--server` / `-s` - Run for this server this run only, without changing your current server (LIVE, TEST, EMU).
|
|
85
|
+
> - `open [SHORTCUT]` - Open a folder or file (e.g. **downloads**, **eqhost**). **open** by itself will show a full list.
|
|
86
|
+
> - `SHORTCUT` - Folder/file to open: downloads, vvmq, eq, etc.
|
|
87
|
+
> - `--server` / `-s` - Resolve paths for this server this run only, without changing your current server (LIVE, TEST, EMU).
|
|
88
|
+
> - `web` - Launch the **RedGuides.com** web interface.
|
|
89
|
+
> - `version` - Show version and exit.
|
|
90
|
+
> - `uninstall` - Uninstall **redfetch** and clean up data.
|
|
91
|
+
> - `logout` - Log out and clear cached token and API cache.
|
|
92
|
+
>
|
|
76
93
|
> ### 🍔 Configuration
|
|
77
|
-
> - `
|
|
78
|
-
>
|
|
79
|
-
> - `SETTING_PATH` - Dot-separated setting path (e.g., `SPECIAL_RESOURCES.1974.opt_in`)
|
|
94
|
+
> - `config <SETTING_PATH> <VALUE>` - Update a setting by path and value.
|
|
95
|
+
> - `SETTING_PATH` - Dot-separated setting path (e.g., SPECIAL_RESOURCES.1974.opt_in)
|
|
80
96
|
> - `VALUE` - New value for the setting
|
|
81
|
-
> - `--server` / `-s` - Server to apply the change in (
|
|
82
|
-
> - `
|
|
97
|
+
> - `--server` / `-s` - Server to apply the change in (LIVE, TEST, EMU)
|
|
98
|
+
> - `server <SERVER>` - Switch the current server/environment to LIVE, TEST, or EMU.
|
|
99
|
+
> - `SERVER` - Server to use: LIVE, TEST, EMU
|
|
100
|
+
> - `status` - Show the configuration for the current or specified server.
|
|
83
101
|
> - `--server` / `-s` - Server to show (defaults to current)
|
|
84
|
-
>
|
|
85
|
-
> ### 🔧 System & Utilities
|
|
86
|
-
> - `ui` - Launch the Terminal User Interface
|
|
87
|
-
> - `web` - Launch the RedGuides.com web interface
|
|
88
|
-
> - `version` - Show version and exit
|
|
89
|
-
> - `logout` - Disconnect your account from redfetch
|
|
90
|
-
> - `uninstall` - Uninstall redfetch and clean up data
|
|
91
|
-
>
|
|
102
|
+
>
|
|
92
103
|
> ### 📤 Publishing
|
|
93
|
-
> - `publish <
|
|
94
|
-
> - `
|
|
95
|
-
> - `--description <README.md>` / `-d` - Path to a file (e.g.
|
|
96
|
-
> - `--version
|
|
97
|
-
> - `--message <CHANGELOG.md | MESSAGE>` / `-m` -
|
|
104
|
+
> - `publish <RESOURCE_ID>` - Publish updates to a **RedGuides** resource.
|
|
105
|
+
> - `RESOURCE_ID` - Existing RedGuides resource ID
|
|
106
|
+
> - `--description <README.md>` / `-d` - Path to a description file (e.g. README.md) to become the overview description.
|
|
107
|
+
> - `--version` / `-v` - New version string (e.g., v1.0.1)
|
|
108
|
+
> - `--message <CHANGELOG.md | MESSAGE>` / `-m` - Path to *CHANGELOG.md* (keep a changelog), other message file, or a direct message string.
|
|
98
109
|
> - `--file <FILE.zip>` / `-f` - Path to your zipped release file
|
|
99
|
-
> - `--domain
|
|
110
|
+
> - `--domain` - If description or message is a .md file with relative URLs, resolve them to this domain (e.g., https://raw.githubusercontent.com/your/repo/main/)
|
|
111
|
+
<!-- END GENERATED CLI REFERENCE -->
|
|
112
|
+
|
|
113
|
+
The `publish` command also has a [GitHub Action](https://github.com/marketplace/actions/redguides-publish).
|
|
100
114
|
|
|
101
115
|
## Settings
|
|
102
116
|
|
|
@@ -119,7 +133,7 @@ opt_in = true
|
|
|
119
133
|
```
|
|
120
134
|
* Note the use of single quotes around the path, which are required for windows paths.
|
|
121
135
|
|
|
122
|
-
The above will install Brewall's maps to the EQ maps directory the next time
|
|
136
|
+
The above will install Brewall's maps to the EQ maps directory the next time `redfetch update` is run for `LIVE` servers.
|
|
123
137
|
|
|
124
138
|
### Overwrite protection
|
|
125
139
|
|
|
@@ -176,14 +190,6 @@ command = ["C:\\Users\\Public\\Python\\python.exe", "C:\\Users\\Public\\redfetch
|
|
|
176
190
|
|
|
177
191
|
You can set these per-server, e.g. `[TEST.POST_UPDATE_LAUNCH]`, or global `[DEFAULT.POST_UPDATE_LAUNCH]`.
|
|
178
192
|
|
|
179
|
-
|
|
180
|
-
## Trailmap
|
|
181
|
-
- Add custom buttons for "fetch" tab.
|
|
182
|
-
- Option: Close after update
|
|
183
|
-
- Indicate when updated VV is available
|
|
184
|
-
- Run from MQ
|
|
185
|
-
- Deeper integration with the forums
|
|
186
|
-
|
|
187
193
|

|
|
188
194
|
|
|
189
195
|
## Contributing
|
|
@@ -21,9 +21,10 @@ dependencies = [
|
|
|
21
21
|
"aiofiles~=25.1",
|
|
22
22
|
"aiosqlite~=0.22",
|
|
23
23
|
"aiohttp~=3.14",
|
|
24
|
-
"cachetools~=7.1",
|
|
25
24
|
"diskcache~=5.6",
|
|
26
|
-
"dynaconf~=3.
|
|
25
|
+
"dynaconf~=3.3,>=3.3.2", # (pr 1409 fixed RecursionError)
|
|
26
|
+
"filelock~=3.29",
|
|
27
|
+
"hishel[httpx,async]~=1.3",
|
|
27
28
|
"httpx[http2]~=0.28",
|
|
28
29
|
"keepachangelog~=2.0",
|
|
29
30
|
"keyring~=25.7",
|
|
@@ -32,7 +33,6 @@ dependencies = [
|
|
|
32
33
|
"pydantic~=2.13",
|
|
33
34
|
"platformdirs~=4.10",
|
|
34
35
|
"psutil~=7.2",
|
|
35
|
-
"pyperclip~=1.11",
|
|
36
36
|
"rich~=15.0",
|
|
37
37
|
"tenacity~=9.1",
|
|
38
38
|
"textual~=8.2",
|
|
@@ -78,3 +78,9 @@ dependencies = [
|
|
|
78
78
|
"pip-tools",
|
|
79
79
|
"pip-check"
|
|
80
80
|
]
|
|
81
|
+
|
|
82
|
+
[tool.hatch.envs.dev.scripts]
|
|
83
|
+
# Regenerate the README 'Command Line Reference' from the typer app.
|
|
84
|
+
gen-docs = "python tests/cli_reference.py --write"
|
|
85
|
+
# Verify it's current
|
|
86
|
+
check-docs = "python tests/cli_reference.py --check"
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.
|
|
22
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.4.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 4, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Resource API client: fetch_*() takes a client, get_*() is self-contained."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from redfetch import net
|
|
5
|
+
from redfetch.sync_types import SyncInfo
|
|
6
|
+
|
|
7
|
+
BASE_URL = net.BASE_URL
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def fetch_sync_info(client: httpx.AsyncClient) -> SyncInfo:
|
|
11
|
+
"""Per-user sync info"""
|
|
12
|
+
url = f'{BASE_URL}/api/rgsync'
|
|
13
|
+
is_level_2 = is_moderator = False
|
|
14
|
+
watched: set[str] = set()
|
|
15
|
+
licenses: list[dict] = []
|
|
16
|
+
page = 1
|
|
17
|
+
while True:
|
|
18
|
+
data = await net.get_json(client, url, params={'page': page})
|
|
19
|
+
is_level_2 = bool(data.get('is_level_2', False))
|
|
20
|
+
is_moderator = bool(data.get('is_moderator', False))
|
|
21
|
+
watched.update(str(rid) for rid in data.get('watched', []))
|
|
22
|
+
licenses.extend(data.get('licenses', []))
|
|
23
|
+
if not data.get('pagination', {}).get('has_more', False):
|
|
24
|
+
break
|
|
25
|
+
page += 1
|
|
26
|
+
|
|
27
|
+
licensed_ids = {
|
|
28
|
+
str(lic['resource_id']) for lic in licenses if lic.get('resource_id') is not None
|
|
29
|
+
}
|
|
30
|
+
return SyncInfo(
|
|
31
|
+
is_level_2=is_level_2,
|
|
32
|
+
is_moderator=is_moderator,
|
|
33
|
+
watched=watched,
|
|
34
|
+
licensed_ids=licensed_ids,
|
|
35
|
+
licenses=licenses,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def get_sync_info(headers: dict) -> SyncInfo:
|
|
40
|
+
"""fetch_sync_info for callers that hold headers but not a client."""
|
|
41
|
+
async with httpx.AsyncClient(headers=headers, http2=True, timeout=30.0) as client:
|
|
42
|
+
return await fetch_sync_info(client)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def get_resource_details(resource_id: int, headers: dict) -> dict:
|
|
46
|
+
"""Retrieve details of a specific resource from the API."""
|
|
47
|
+
url = f'{BASE_URL}/api/resources/{resource_id}'
|
|
48
|
+
async with httpx.AsyncClient(headers=headers, http2=True, timeout=30.0) as client:
|
|
49
|
+
data = await net.get_json(client, url)
|
|
50
|
+
return data['resource']
|
|
@@ -8,6 +8,7 @@ import shutil
|
|
|
8
8
|
# third-party
|
|
9
9
|
import tomlkit
|
|
10
10
|
from dynaconf import Dynaconf, Validator, ValidationError
|
|
11
|
+
from dynaconf.loaders import env_loader
|
|
11
12
|
from platformdirs import user_config_dir, user_data_dir
|
|
12
13
|
|
|
13
14
|
# Parent Category to folder
|
|
@@ -162,12 +163,40 @@ def initialize_config():
|
|
|
162
163
|
]
|
|
163
164
|
)
|
|
164
165
|
|
|
166
|
+
self_heal_eqpath()
|
|
165
167
|
write_breadcrumb()
|
|
166
168
|
|
|
167
169
|
# Return the settings object for potential use
|
|
168
170
|
return settings
|
|
169
171
|
|
|
170
172
|
|
|
173
|
+
def self_heal_eqpath() -> None:
|
|
174
|
+
"""Fill a blank or broken EQ PATH from autologin's login.db, per env."""
|
|
175
|
+
from redfetch import utils, detecteq
|
|
176
|
+
|
|
177
|
+
for env in ("LIVE", "TEST", "EMU"):
|
|
178
|
+
try:
|
|
179
|
+
env_settings = settings.from_env(env)
|
|
180
|
+
stored = env_settings.get("EQPATH")
|
|
181
|
+
if stored and detecteq._is_valid_eq_dir(stored):
|
|
182
|
+
continue # heal only when blank or broken
|
|
183
|
+
|
|
184
|
+
vvmq_id = utils.get_current_vvmq_id(env)
|
|
185
|
+
if not vvmq_id:
|
|
186
|
+
continue
|
|
187
|
+
vvmq = utils.resolve_special_destination(
|
|
188
|
+
env_settings.SPECIAL_RESOURCES.get(vvmq_id), env_settings.DOWNLOAD_FOLDER
|
|
189
|
+
)
|
|
190
|
+
if not vvmq:
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
detected = detecteq.read_autologin_eq_path(os.path.join(vvmq, "config"), env)
|
|
194
|
+
if detected and detected != stored:
|
|
195
|
+
update_setting(["EQPATH"], detected, env=env)
|
|
196
|
+
except Exception:
|
|
197
|
+
continue # we're not stopping for this
|
|
198
|
+
|
|
199
|
+
|
|
171
200
|
def _resolve_redfetch_executable():
|
|
172
201
|
"""PYAPP will give a path when built with PYAPP_PASS_LOCATION=1"""
|
|
173
202
|
pyapp = os.environ.get("PYAPP")
|
|
@@ -459,25 +488,8 @@ def update_setting(setting_path, setting_value, env=None):
|
|
|
459
488
|
|
|
460
489
|
|
|
461
490
|
def write_env_to_file(new_env):
|
|
462
|
-
"""
|
|
491
|
+
"""Persist the selected environment to the .env file; dynaconf reads it back via env_switcher."""
|
|
463
492
|
if env_file_path is None:
|
|
464
493
|
raise RuntimeError("Configuration has not been initialized. Call initialize_config() first.")
|
|
465
494
|
|
|
466
|
-
|
|
467
|
-
with open(env_file_path, 'r') as file:
|
|
468
|
-
lines = file.readlines()
|
|
469
|
-
|
|
470
|
-
# Update the environment line
|
|
471
|
-
updated = False
|
|
472
|
-
for i, line in enumerate(lines):
|
|
473
|
-
if line.startswith('REDFETCH_ENV='):
|
|
474
|
-
lines[i] = f'REDFETCH_ENV={new_env}\n'
|
|
475
|
-
updated = True
|
|
476
|
-
break
|
|
477
|
-
|
|
478
|
-
# If the environment line was not found, add it
|
|
479
|
-
if not updated:
|
|
480
|
-
lines.append(f'REDFETCH_ENV={new_env}\n')
|
|
481
|
-
|
|
482
|
-
# Write the updated content back to the .env file
|
|
483
|
-
atomic_write_text(env_file_path, ''.join(lines))
|
|
495
|
+
env_loader.write(env_file_path, {"REDFETCH_ENV": new_env})
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sqlite3
|
|
2
3
|
import sys
|
|
4
|
+
from pathlib import Path
|
|
3
5
|
|
|
4
6
|
# Only import winreg on Windows
|
|
5
7
|
if sys.platform == 'win32':
|
|
@@ -112,5 +114,29 @@ def find_everquest_uninstall_location():
|
|
|
112
114
|
return None
|
|
113
115
|
|
|
114
116
|
|
|
117
|
+
def read_autologin_eq_path(mq_config_dir: str | None, server_type: str) -> str | None:
|
|
118
|
+
"""from autologin's login.db."""
|
|
119
|
+
if not mq_config_dir:
|
|
120
|
+
return None
|
|
121
|
+
db_path = os.path.join(mq_config_dir, "login.db")
|
|
122
|
+
if not os.path.isfile(db_path):
|
|
123
|
+
return None # autologin maybe hasn't run
|
|
124
|
+
|
|
125
|
+
con = None
|
|
126
|
+
try:
|
|
127
|
+
con = sqlite3.connect(Path(db_path).as_uri() + "?mode=ro", uri=True)
|
|
128
|
+
row = con.execute(
|
|
129
|
+
"SELECT eq_path FROM server_types WHERE type = LOWER(?)", (server_type,)
|
|
130
|
+
).fetchone()
|
|
131
|
+
except (sqlite3.Error, ValueError):
|
|
132
|
+
return None # locked or something
|
|
133
|
+
finally:
|
|
134
|
+
if con is not None:
|
|
135
|
+
con.close()
|
|
136
|
+
|
|
137
|
+
candidate = row[0] if row else None
|
|
138
|
+
return os.path.normpath(candidate) if _is_valid_eq_dir(candidate) else None
|
|
139
|
+
|
|
140
|
+
|
|
115
141
|
if __name__ == "__main__":
|
|
116
142
|
print(find_everquest_uninstall_location() or "")
|