mirror of
https://github.com/ikunshare/Onekey.git
synced 2026-01-12 16:25:53 +08:00
rm: all
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
15
.github/FUNDING.yml
vendored
15
.github/FUNDING.yml
vendored
@@ -1,15 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: ['https://afdian.com/a/ikun0014']
|
||||
25
.github/ISSUE_TEMPLATE/bug反馈.md
vendored
25
.github/ISSUE_TEMPLATE/bug反馈.md
vendored
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: Bug反馈
|
||||
about: 反馈一个虫子(确信)
|
||||
title: "[BUG]: "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**触发Bug后出现了什么症状**
|
||||
1.
|
||||
|
||||
**如何复现该Bug**
|
||||
步骤:
|
||||
1.
|
||||
|
||||
**日志**
|
||||
复制在这里
|
||||
|
||||
**截图**
|
||||
对程序的完整截图
|
||||
|
||||
**系统:**
|
||||
- OS: Windows
|
||||
- Version: 10
|
||||
124
.github/workflows/release.yml
vendored
124
.github/workflows/release.yml
vendored
@@ -1,124 +0,0 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get package version
|
||||
id: get_version
|
||||
shell: powershell
|
||||
run: |
|
||||
$packageJson = Get-Content package.json | ConvertFrom-Json
|
||||
$version = $packageJson.version
|
||||
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
|
||||
echo "Package version: $version"
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
architecture: "x64"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install Pillow
|
||||
pip install pyinstaller
|
||||
|
||||
|
||||
- name: Build executable
|
||||
run: |
|
||||
pyinstaller --onefile `
|
||||
--icon "./icon.jpg" `
|
||||
--name "Onekey_v${{ steps.get_version.outputs.VERSION }}" `
|
||||
--add-data "src;src" `
|
||||
--hidden-import=winreg `
|
||||
--hidden-import=httpx `
|
||||
--hidden-import=vdf `
|
||||
--hidden-import=colorama `
|
||||
--hidden-import=logzero `
|
||||
--hidden-import=ujson `
|
||||
--console `
|
||||
main.py
|
||||
|
||||
- name: Compress with UPX
|
||||
uses: crazy-max/ghaction-upx@v3.2.0
|
||||
with:
|
||||
version: latest
|
||||
files: |
|
||||
./dist/*.exe
|
||||
args: "-fq"
|
||||
|
||||
- name: Create git tag
|
||||
uses: pkgdeps/git-tag-action@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
github_repo: ${{ github.repository }}
|
||||
version: ${{ steps.get_version.outputs.VERSION }}
|
||||
git_commit_sha: ${{ github.sha }}
|
||||
git_tag_prefix: "v"
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: v${{ steps.get_version.outputs.VERSION }}
|
||||
files: dist/Onekey_v${{ steps.get_version.outputs.VERSION }}.exe
|
||||
prerelease: false
|
||||
draft: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload to Gitee Release
|
||||
continue-on-error: true
|
||||
uses: nicennnnnnnlee/action-gitee-release@v1.0.5
|
||||
with:
|
||||
gitee_owner: ikun0014
|
||||
gitee_repo: Onekey
|
||||
gitee_token: ${{ secrets.GITEE_TOKEN }}
|
||||
gitee_tag_name: v${{ steps.get_version.outputs.VERSION }}
|
||||
gitee_release_name: v${{ steps.get_version.outputs.VERSION }}
|
||||
gitee_release_body: "Onekey version ${{ steps.get_version.outputs.VERSION }} release"
|
||||
gitee_target_commitish: main
|
||||
gitee_upload_retry_times: 3
|
||||
gitee_file_name: Onekey_v${{ steps.get_version.outputs.VERSION }}.exe
|
||||
gitee_file_path: dist/Onekey_v${{ steps.get_version.outputs.VERSION }}.exe
|
||||
|
||||
- name: Upload to Telegram Channel
|
||||
continue-on-error: true
|
||||
shell: powershell
|
||||
run: |
|
||||
$version = "${{ steps.get_version.outputs.VERSION }}"
|
||||
$filePath = "dist/Onekey_v${version}.exe"
|
||||
|
||||
if (Test-Path $filePath) {
|
||||
curl.exe -F "chat_id=${{ secrets.TELEGRAM_TO }}" `
|
||||
-F "thread_id=${{ secrets.TELEGRAM_THREAD }}" `
|
||||
-F "document=@$filePath" `
|
||||
-F "caption=🎮 **Onekey New Update** v${version}`n`n✨ Steam Game Unlocker`n💻 Windows 10/11`n`n[GitHub](https://github.com/ikunshare/Onekey)" `
|
||||
-F "parse_mode=Markdown" `
|
||||
"https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendDocument"
|
||||
} else {
|
||||
Write-Error "File not found: $filePath"
|
||||
}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Onekey_v${{ steps.get_version.outputs.VERSION }}
|
||||
path: dist/Onekey_v${{ steps.get_version.outputs.VERSION }}.exe
|
||||
retention-days: 30
|
||||
25
.github/workflows/sync.yml
vendored
25
.github/workflows/sync.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Sync to Gitee
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync to Gitee
|
||||
uses: Yikun/hub-mirror-action@master
|
||||
with:
|
||||
src: github/ikunshare
|
||||
dst: gitee/ikun0014
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
src_account_type: org
|
||||
dst_account_type: user
|
||||
white_list: "Onekey"
|
||||
force_update: true
|
||||
debug: true
|
||||
166
.gitignore
vendored
166
.gitignore
vendored
@@ -1,166 +0,0 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
Pipfile.lock
|
||||
|
||||
# PEP 582
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Project specific
|
||||
config.json
|
||||
logs/
|
||||
*.exe
|
||||
icon.ico
|
||||
icon.jpg
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
htmlcov/
|
||||
.coverage
|
||||
coverage.xml
|
||||
*.cover
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
*.bak
|
||||
*.backup
|
||||
|
||||
# Build artifacts
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"python.analysis.autoImportCompletions": true,
|
||||
"python.analysis.typeCheckingMode": "off"
|
||||
}
|
||||
341
LICENSE
341
LICENSE
@@ -1,341 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Moe Ghoul>, 1 April 1989
|
||||
Moe Ghoul, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
79
README.md
79
README.md
@@ -1,79 +0,0 @@
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||

|
||||
[](https://github.com/ikunshare/Onekey/releases/latest)
|
||||
[](https://github.com/ikunshare/Onekey/releases)
|
||||
[](https://github.com/ikunshare/Onekey/blob/main/LICENSE)
|
||||
|
||||
[](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Onekey
|
||||
Onekey Steam Depot Manifest Downloader
|
||||
对本软件有意见的
|
||||
欢迎拨打中华人民共和国公安部门报警电话:110进行报警
|
||||
|
||||
## 先让我挂些人
|
||||
- 沧海颐粟,早期倒卖大手子,现在不知道跑哪了,通过一点手段查到在江西
|
||||
- 玩家资源站,贼喊捉贼笑传,随便改改别人软件的名字就是自己的,还去报官了
|
||||
|
||||
## 使用方法
|
||||
去Releases处下载最新的发布,并且安装好SteamTools或者GreenLuma
|
||||
然后打开Onekey输入App ID即可使用
|
||||
|
||||
## 开发
|
||||
本程序使用Python编程语言开发
|
||||
要求环境:
|
||||
1.Python 3.10及以上
|
||||
2.Windows 10及以上
|
||||
3.使用Git进行版本管理
|
||||
|
||||
1.克隆项目到本地
|
||||
|
||||
```
|
||||
git clone https://github.com/ikunshare/Onekey
|
||||
```
|
||||
|
||||
2.安装依赖
|
||||
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 项目协议
|
||||
本项目基于 GPL-2.0 许可证发行,以下协议是对于 GPL-2.0 原协议的补充,如有冲突,以以下协议为准。
|
||||
|
||||
词语约定: “使用者”指签署本协议的使用者;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。
|
||||
|
||||
本项目的数据来源原理是从Steam官方的CDN服务器中拉取游戏清单数据,经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的准确性负责。
|
||||
使用本项目的过程中可能会产生版权数据,对于这些版权数据,本项目不拥有它们的所有权,为了避免造成侵权,使用者务必在24 小时内清除使用本项目的过程中所产生的版权数据。
|
||||
由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。
|
||||
本项目完全免费,且开源发布于 GitHub 面向全世界人用作对技术的学习交流,本项目不对项目内的技术可能存在违反当地法律法规的行为作保证,禁止在违反当地法律法规的情况下使用本项目,对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
|
||||
而且,本项目已禁止使用于商业用途,以及不得进行未经允许的二次修改,否则必须同时发布源代码。
|
||||
若你使用了本项目,将代表你接受以上协议。
|
||||
|
||||
Steam正版平台不易,请尊重版权,支持正版。
|
||||
本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作。
|
||||
|
||||
## Star 趋势图
|
||||
|
||||
[](https://starchart.cc/ikunshare/Onekey)
|
||||
|
||||
## 贡献者
|
||||
|
||||
<a href="https://github.com/ikunshare/Onekey/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=ikunshare/Onekey" />
|
||||
</a>
|
||||
|
||||
## 常见问题解答(FAQ)
|
||||
查看 [FAQ](https://ikunshare.top/d/49) 获取常见问题的解答。
|
||||
|
||||
## 社区和支持
|
||||
加入我们的社区,参与讨论和支持:
|
||||
- [GitHub Discussions](https://github.com/ikunshare/Onekey/discussions)
|
||||
- [Telegram](https://t.me/ikunshare_qun)
|
||||
- [QQ](https://qm.qq.com/q/NPRVbglteK)
|
||||
14
main.py
14
main.py
@@ -1,14 +0,0 @@
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
from src.main import main
|
||||
|
||||
asyncio.run(main())
|
||||
except (asyncio.CancelledError, KeyboardInterrupt):
|
||||
print("\n程序已退出")
|
||||
except Exception as e:
|
||||
print(f"错误:{e}")
|
||||
finally:
|
||||
os.system("pause")
|
||||
22
package.json
22
package.json
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "onekey",
|
||||
"version": "1.5.1",
|
||||
"description": "一个Steam仓库清单下载器",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ikunshare/Onekey.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Onekey"
|
||||
],
|
||||
"author": "ikun0014",
|
||||
"license": "GPL-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ikunshare/Onekey/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ikunshare/Onekey#readme"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
colorama==0.4.6
|
||||
httpx==0.28.1
|
||||
logzero==1.7.0
|
||||
vdf==3.4
|
||||
@@ -1,3 +0,0 @@
|
||||
__version__ = "1.5.1"
|
||||
__author__ = "ikun0014"
|
||||
__website__ = "ikunshare.top"
|
||||
@@ -1,88 +0,0 @@
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import winreg
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .constants import CONFIG_FILE
|
||||
from .models import AppConfig
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
"Github_Personal_Token": "",
|
||||
"Custom_Steam_Path": "",
|
||||
"Debug_Mode": False,
|
||||
"Logging_Files": True,
|
||||
"Help": "Github Personal Token可在GitHub设置的Developer settings中生成",
|
||||
}
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
"""配置管理器"""
|
||||
|
||||
def __init__(self):
|
||||
self.config_path = CONFIG_FILE
|
||||
self._config_data: Dict = {}
|
||||
self.app_config: AppConfig = AppConfig()
|
||||
self.steam_path: Optional[Path] = None
|
||||
self._load_config()
|
||||
|
||||
def _generate_config(self) -> None:
|
||||
"""生成默认配置文件"""
|
||||
try:
|
||||
with open(self.config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(DEFAULT_CONFIG, f, indent=2, ensure_ascii=False)
|
||||
print("配置文件已生成")
|
||||
except IOError as e:
|
||||
print(f"配置文件创建失败: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
def _load_config(self) -> None:
|
||||
"""加载配置文件"""
|
||||
if not self.config_path.exists():
|
||||
self._generate_config()
|
||||
print("请填写配置文件后重新运行程序,5秒后退出")
|
||||
time.sleep(5)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
with open(self.config_path, "r", encoding="utf-8") as f:
|
||||
self._config_data = json.load(f)
|
||||
|
||||
self.app_config = AppConfig(
|
||||
github_token=self._config_data.get("Github_Personal_Token", ""),
|
||||
custom_steam_path=self._config_data.get("Custom_Steam_Path", ""),
|
||||
debug_mode=self._config_data.get("Debug_Mode", False),
|
||||
logging_files=self._config_data.get("Logging_Files", True),
|
||||
)
|
||||
|
||||
self.steam_path = self._get_steam_path()
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print("配置文件损坏,正在重新生成...")
|
||||
self._generate_config()
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"配置加载失败: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
def _get_steam_path(self) -> Path:
|
||||
"""获取Steam安装路径"""
|
||||
try:
|
||||
if self.app_config.custom_steam_path:
|
||||
return Path(self.app_config.custom_steam_path)
|
||||
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_CURRENT_USER, r"Software\Valve\Steam"
|
||||
) as key:
|
||||
return Path(winreg.QueryValueEx(key, "SteamPath")[0])
|
||||
except Exception as e:
|
||||
print(f"Steam路径获取失败: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
@property
|
||||
def github_headers(self) -> Optional[Dict[str, str]]:
|
||||
"""获取GitHub请求头"""
|
||||
if self.app_config.github_token:
|
||||
return {"Authorization": f"Bearer {self.app_config.github_token}"}
|
||||
return None
|
||||
@@ -1,35 +0,0 @@
|
||||
"""常量定义"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
APP_NAME = "Onekey"
|
||||
BANNER = r"""
|
||||
_____ __ _ _____ _ _ _____ __ __
|
||||
/ _ \ | \ | | | ____| | | / / | ____| \ \ / /
|
||||
| | | | | \| | | |__ | |/ / | |__ \ \/ /
|
||||
| | | | | |\ | | __| | |\ \ | __| \ /
|
||||
| |_| | | | \ | | |___ | | \ \ | |___ / /
|
||||
\_____/ |_| \_| |_____| |_| \_\ |_____| /_/
|
||||
"""
|
||||
|
||||
REPO_LIST = [
|
||||
"SteamAutoCracks/ManifestHub",
|
||||
"ikun0014/ManifestHub",
|
||||
"Auiowu/ManifestAutoUpdate",
|
||||
"tymolu233/ManifestAutoUpdate-fix",
|
||||
]
|
||||
|
||||
CN_CDN_LIST = [
|
||||
"https://cdn.jsdmirror.com/gh/{repo}@{sha}/{path}",
|
||||
"https://raw.gitmirror.com/{repo}/{sha}/{path}",
|
||||
"https://raw.dgithub.xyz/{repo}/{sha}/{path}",
|
||||
"https://gh.akass.cn/{repo}/{sha}/{path}",
|
||||
]
|
||||
|
||||
GLOBAL_CDN_LIST = ["https://raw.githubusercontent.com/{repo}/{sha}/{path}"]
|
||||
|
||||
GITHUB_API_BASE = "https://api.github.com"
|
||||
REGION_CHECK_URL = "https://mips.kugou.com/check/iscn?&format=json"
|
||||
|
||||
LOG_DIR = Path("logs")
|
||||
CONFIG_FILE = Path("config.json")
|
||||
@@ -1,67 +0,0 @@
|
||||
"""日志模块"""
|
||||
|
||||
import logging
|
||||
import colorama
|
||||
import logzero
|
||||
from logzero import setup_logger, LogFormatter
|
||||
|
||||
from .constants import LOG_DIR
|
||||
|
||||
|
||||
class Logger:
|
||||
"""统一的日志管理器"""
|
||||
|
||||
def __init__(self, name: str, debug_mode: bool = False, log_file: bool = True):
|
||||
self.name = name
|
||||
self.debug_mode = debug_mode
|
||||
self.log_file = log_file
|
||||
self._logger = self._setup_logger()
|
||||
|
||||
def _setup_logger(self) -> logging.Logger:
|
||||
"""设置日志器"""
|
||||
level = logzero.DEBUG if self.debug_mode else logzero.INFO
|
||||
|
||||
colors = {
|
||||
logzero.DEBUG: colorama.Fore.CYAN,
|
||||
logzero.INFO: colorama.Fore.GREEN,
|
||||
logzero.WARNING: colorama.Fore.YELLOW,
|
||||
logzero.ERROR: colorama.Fore.RED,
|
||||
logzero.CRITICAL: colorama.Fore.MAGENTA,
|
||||
}
|
||||
|
||||
terminal_formatter = LogFormatter(
|
||||
color=True,
|
||||
fmt="%(color)s%(message)s%(end_color)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
colors=colors,
|
||||
)
|
||||
|
||||
logger = setup_logger(self.name, level=level, formatter=terminal_formatter)
|
||||
|
||||
if self.log_file:
|
||||
LOG_DIR.mkdir(exist_ok=True)
|
||||
logfile = LOG_DIR / f"{self.name}.log"
|
||||
file_handler = logging.FileHandler(logfile, encoding="utf-8")
|
||||
file_formatter = logging.Formatter(
|
||||
"[%(asctime)s] | [%(name)s:%(levelname)s] | [%(module)s.%(funcName)s:%(lineno)d] - %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
return logger
|
||||
|
||||
def debug(self, msg: str):
|
||||
self._logger.debug(msg)
|
||||
|
||||
def info(self, msg: str):
|
||||
self._logger.info(msg)
|
||||
|
||||
def warning(self, msg: str):
|
||||
self._logger.warning(msg)
|
||||
|
||||
def error(self, msg: str):
|
||||
self._logger.error(msg)
|
||||
|
||||
def critical(self, msg: str):
|
||||
self._logger.critical(msg)
|
||||
170
src/main.py
170
src/main.py
@@ -1,170 +0,0 @@
|
||||
import traceback
|
||||
from typing import List, Dict, Tuple
|
||||
|
||||
from . import __version__, __author__, __website__
|
||||
from .constants import BANNER, REPO_LIST
|
||||
from .config import ConfigManager
|
||||
from .logger import Logger
|
||||
from .models import DepotInfo
|
||||
from .network.client import HttpClient
|
||||
from .network.github import GitHubAPI
|
||||
from .utils.region import RegionDetector
|
||||
from .utils.steam import parse_key_file, parse_manifest_filename
|
||||
from .tools.steamtools import SteamTools
|
||||
from .tools.greenluma import GreenLuma
|
||||
|
||||
|
||||
class OnekeyApp:
|
||||
"""Onekey主应用"""
|
||||
|
||||
def __init__(self):
|
||||
self.config = ConfigManager()
|
||||
self.logger = Logger(
|
||||
"Onekey",
|
||||
debug_mode=self.config.app_config.debug_mode,
|
||||
log_file=self.config.app_config.logging_files,
|
||||
)
|
||||
self.client = HttpClient()
|
||||
self.github = GitHubAPI(self.client, self.config.github_headers, self.logger)
|
||||
|
||||
def show_banner(self):
|
||||
"""显示横幅"""
|
||||
self.logger.info(BANNER)
|
||||
self.logger.info(
|
||||
f"本程序源代码基于GPL 2.0许可证开放于Github"
|
||||
)
|
||||
self.logger.info(
|
||||
f"作者: {__author__} | 版本: {__version__} | 官网: {__website__}"
|
||||
)
|
||||
self.logger.info("项目仓库: GitHub: https://github.com/ikunshare/Onekey")
|
||||
self.logger.warning("ikunshare.top | 严禁倒卖")
|
||||
self.logger.warning(
|
||||
"提示: 请确保已安装Windows 10/11并正确配置Steam;SteamTools/GreenLuma"
|
||||
)
|
||||
if not self.config.app_config.github_token:
|
||||
self.logger.warning("开梯子必须配置Token, 你的IP我不相信能干净到哪")
|
||||
|
||||
async def handle_depot_files(
|
||||
self, app_id: str
|
||||
) -> Tuple[List[DepotInfo], Dict[str, List[str]]]:
|
||||
"""处理仓库文件"""
|
||||
depot_list = []
|
||||
depot_map = {}
|
||||
|
||||
repo_info = await self.github.get_latest_repo_info(REPO_LIST, app_id)
|
||||
if not repo_info:
|
||||
return depot_list, depot_map
|
||||
|
||||
self.logger.info(f"当前选择清单仓库: https://github.com/{repo_info.name}")
|
||||
self.logger.info(f"此清单分支上次更新时间:{repo_info.last_update}")
|
||||
|
||||
branch_url = f"https://api.github.com/repos/{repo_info.name}/branches/{app_id}"
|
||||
branch_res = await self.client.get(
|
||||
branch_url, headers=self.config.github_headers
|
||||
)
|
||||
branch_res.raise_for_status()
|
||||
|
||||
tree_url = branch_res.json()["commit"]["commit"]["tree"]["url"]
|
||||
tree_res = await self.client.get(tree_url)
|
||||
tree_res.raise_for_status()
|
||||
|
||||
depot_cache = self.config.steam_path / "depotcache"
|
||||
depot_cache.mkdir(exist_ok=True)
|
||||
|
||||
for item in tree_res.json()["tree"]:
|
||||
file_path = item["path"]
|
||||
|
||||
if file_path.endswith(".manifest"):
|
||||
save_path = depot_cache / file_path
|
||||
if save_path.exists():
|
||||
self.logger.warning(f"已存在清单: {save_path}")
|
||||
continue
|
||||
|
||||
content = await self.github.fetch_file(
|
||||
repo_info.name, repo_info.sha, file_path
|
||||
)
|
||||
save_path.write_bytes(content)
|
||||
self.logger.info(f"清单下载成功: {file_path}")
|
||||
|
||||
depot_id, manifest_id = parse_manifest_filename(file_path)
|
||||
if depot_id and manifest_id:
|
||||
depot_map.setdefault(depot_id, []).append(manifest_id)
|
||||
|
||||
elif "key.vdf" in file_path.lower():
|
||||
key_content = await self.github.fetch_file(
|
||||
repo_info.name, repo_info.sha, file_path
|
||||
)
|
||||
depot_list.extend(parse_key_file(key_content))
|
||||
|
||||
for depot_id in depot_map:
|
||||
depot_map[depot_id].sort(key=lambda x: int(x), reverse=True)
|
||||
|
||||
return depot_list, depot_map
|
||||
|
||||
async def run(self, app_id: str):
|
||||
"""运行主程序"""
|
||||
try:
|
||||
detector = RegionDetector(self.client, self.logger)
|
||||
is_cn, country = await detector.check_cn()
|
||||
self.github.is_cn = is_cn
|
||||
|
||||
await self.github.check_rate_limit()
|
||||
|
||||
self.logger.info(f"正在处理游戏 {app_id}...")
|
||||
depot_data, depot_map = await self.handle_depot_files(app_id)
|
||||
|
||||
if not depot_data:
|
||||
self.logger.error("未找到此游戏的清单")
|
||||
return
|
||||
|
||||
print("\n请选择解锁工具:")
|
||||
print("1. SteamTools")
|
||||
print("2. GreenLuma")
|
||||
|
||||
choice = input("请输入选择 (1/2): ").strip()
|
||||
|
||||
if choice == "1":
|
||||
tool = SteamTools(self.config.steam_path)
|
||||
|
||||
version_lock = False
|
||||
lock_choice = input(
|
||||
"是否锁定版本(推荐在选择仓库SteamAutoCracks/ManifestHub时使用)?(y/n): "
|
||||
).lower()
|
||||
if lock_choice == "y":
|
||||
version_lock = True
|
||||
|
||||
success = await tool.setup(
|
||||
depot_data, app_id, depot_map=depot_map, version_lock=version_lock
|
||||
)
|
||||
elif choice == "2":
|
||||
tool = GreenLuma(self.config.steam_path)
|
||||
success = await tool.setup(depot_data, app_id)
|
||||
else:
|
||||
self.logger.error("无效的选择")
|
||||
return
|
||||
|
||||
if success:
|
||||
self.logger.info("游戏解锁配置成功!")
|
||||
self.logger.info("重启Steam后生效")
|
||||
else:
|
||||
self.logger.error("配置失败")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"运行错误: {traceback.format_exc()}")
|
||||
finally:
|
||||
await self.client.close()
|
||||
|
||||
|
||||
async def main():
|
||||
"""程序入口"""
|
||||
app = OnekeyApp()
|
||||
app.show_banner()
|
||||
|
||||
app_id = input("\n请输入游戏AppID: ").strip()
|
||||
|
||||
app_id_list = [id for id in app_id.split("-") if id.isdigit()]
|
||||
if not app_id_list:
|
||||
app.logger.error("App ID无效")
|
||||
return
|
||||
|
||||
await app.run(app_id_list[0])
|
||||
@@ -1,35 +0,0 @@
|
||||
from typing import List
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
class DepotInfo:
|
||||
"""仓库信息"""
|
||||
|
||||
depot_id: str
|
||||
decryption_key: str
|
||||
manifest_ids: List[str] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.manifest_ids is None:
|
||||
self.manifest_ids = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class RepoInfo:
|
||||
"""GitHub仓库信息"""
|
||||
|
||||
name: str
|
||||
last_update: datetime
|
||||
sha: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class AppConfig:
|
||||
"""应用配置"""
|
||||
|
||||
github_token: str = ""
|
||||
custom_steam_path: str = ""
|
||||
debug_mode: bool = False
|
||||
logging_files: bool = True
|
||||
@@ -1,25 +0,0 @@
|
||||
"""HTTP客户端模块"""
|
||||
|
||||
import httpx
|
||||
from typing import Optional, Dict
|
||||
|
||||
|
||||
class HttpClient:
|
||||
"""HTTP客户端封装"""
|
||||
|
||||
def __init__(self):
|
||||
self._client = httpx.AsyncClient(verify=False, timeout=30.0)
|
||||
|
||||
async def get(self, url: str, headers: Optional[Dict] = None) -> httpx.Response:
|
||||
"""GET请求"""
|
||||
return await self._client.get(url, headers=headers)
|
||||
|
||||
async def close(self):
|
||||
"""关闭客户端"""
|
||||
await self._client.aclose()
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
await self.close()
|
||||
@@ -1,92 +0,0 @@
|
||||
import time
|
||||
from typing import List, Dict, Optional
|
||||
from datetime import datetime
|
||||
|
||||
from ..constants import GITHUB_API_BASE, CN_CDN_LIST, GLOBAL_CDN_LIST
|
||||
from ..models import RepoInfo
|
||||
from ..logger import Logger
|
||||
from .client import HttpClient
|
||||
|
||||
|
||||
class GitHubAPI:
|
||||
"""GitHub API封装"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
client: HttpClient,
|
||||
headers: Optional[Dict] = None,
|
||||
logger: Optional[Logger] = None,
|
||||
):
|
||||
self.client = client
|
||||
self.headers = headers or {}
|
||||
self.logger = logger or Logger("GitHubAPI")
|
||||
self.is_cn = True
|
||||
|
||||
async def check_rate_limit(self) -> None:
|
||||
"""检查API请求限制"""
|
||||
url = f"{GITHUB_API_BASE}/rate_limit"
|
||||
try:
|
||||
r = await self.client.get(url, headers=self.headers)
|
||||
if r.status_code == 200:
|
||||
r_json = r.json()
|
||||
rate_limit = r_json.get("rate", {})
|
||||
remaining = rate_limit.get("remaining", 0)
|
||||
reset_time = rate_limit.get("reset", 0)
|
||||
reset_formatted = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(reset_time)
|
||||
)
|
||||
self.logger.info(f"剩余Github API请求次数: {remaining}")
|
||||
if remaining == 0:
|
||||
self.logger.warning(
|
||||
f"GitHub API 请求数已用尽, 将在 {reset_formatted} 重置"
|
||||
)
|
||||
else:
|
||||
self.logger.error("Github请求数检查失败, 网络错误")
|
||||
except Exception as e:
|
||||
self.logger.error(f"检查Github API 请求数失败: {str(e)}")
|
||||
|
||||
async def get_latest_repo_info(
|
||||
self, repos: List[str], app_id: str
|
||||
) -> Optional[RepoInfo]:
|
||||
"""获取最新的仓库信息"""
|
||||
latest_date = None
|
||||
selected_repo = None
|
||||
selected_sha = None
|
||||
|
||||
for repo in repos:
|
||||
url = f"{GITHUB_API_BASE}/repos/{repo}/branches/{app_id}"
|
||||
try:
|
||||
r = await self.client.get(url, headers=self.headers)
|
||||
if r.status_code == 200:
|
||||
r_json = r.json()
|
||||
if "commit" in r_json:
|
||||
date_str = r_json["commit"]["commit"]["author"]["date"]
|
||||
date = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
|
||||
if latest_date is None or date > latest_date:
|
||||
latest_date = date
|
||||
selected_repo = repo
|
||||
selected_sha = r_json["commit"]["sha"]
|
||||
except Exception as e:
|
||||
self.logger.warning(f"检查仓库 {repo} 失败: {str(e)}")
|
||||
|
||||
if selected_repo:
|
||||
return RepoInfo(
|
||||
name=selected_repo, last_update=latest_date, sha=selected_sha
|
||||
)
|
||||
return None
|
||||
|
||||
async def fetch_file(self, repo: str, sha: str, path: str) -> bytes:
|
||||
"""获取文件内容"""
|
||||
cdn_list = CN_CDN_LIST if self.is_cn else GLOBAL_CDN_LIST
|
||||
|
||||
for _ in range(3):
|
||||
for cdn_template in cdn_list:
|
||||
url = cdn_template.format(repo=repo, sha=sha, path=path)
|
||||
try:
|
||||
r = await self.client.get(url, headers=self.headers)
|
||||
if r.status_code == 200:
|
||||
return r.content
|
||||
except Exception as e:
|
||||
self.logger.debug(f"从 {url} 下载失败: {str(e)}")
|
||||
|
||||
raise Exception(f"无法下载文件: {path}")
|
||||
@@ -1,17 +0,0 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List
|
||||
from pathlib import Path
|
||||
|
||||
from ..models import DepotInfo
|
||||
|
||||
|
||||
class UnlockTool(ABC):
|
||||
"""解锁工具基类"""
|
||||
|
||||
def __init__(self, steam_path: Path):
|
||||
self.steam_path = steam_path
|
||||
|
||||
@abstractmethod
|
||||
async def setup(self, depot_data: List[DepotInfo], app_id: str, **kwargs) -> bool:
|
||||
"""设置解锁"""
|
||||
pass
|
||||
@@ -1,39 +0,0 @@
|
||||
import vdf
|
||||
from typing import List
|
||||
|
||||
from .base import UnlockTool
|
||||
from ..models import DepotInfo
|
||||
|
||||
|
||||
class GreenLuma(UnlockTool):
|
||||
"""GreenLuma解锁工具实现"""
|
||||
|
||||
async def setup(self, depot_data: List[DepotInfo], app_id: str, **kwargs) -> bool:
|
||||
"""设置GreenLuma解锁"""
|
||||
applist_dir = self.steam_path / "AppList"
|
||||
applist_dir.mkdir(exist_ok=True)
|
||||
|
||||
for f in applist_dir.glob("*.txt"):
|
||||
f.unlink()
|
||||
|
||||
for idx, depot in enumerate(depot_data, 1):
|
||||
(applist_dir / f"{idx}.txt").write_text(depot.depot_id)
|
||||
|
||||
config_path = self.steam_path / "config" / "config.vdf"
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
content = vdf.loads(f.read())
|
||||
|
||||
content.setdefault("depots", {}).update(
|
||||
{
|
||||
depot.depot_id: {"DecryptionKey": depot.decryption_key}
|
||||
for depot in depot_data
|
||||
}
|
||||
)
|
||||
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
f.write(vdf.dumps(content))
|
||||
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@@ -1,38 +0,0 @@
|
||||
from typing import List, Dict
|
||||
|
||||
from .base import UnlockTool
|
||||
from ..models import DepotInfo
|
||||
|
||||
|
||||
class SteamTools(UnlockTool):
|
||||
"""SteamTools解锁工具实现"""
|
||||
|
||||
async def setup(
|
||||
self,
|
||||
depot_data: List[DepotInfo],
|
||||
app_id: str,
|
||||
depot_map: Dict[str, List[str]] = None,
|
||||
version_lock: bool = False,
|
||||
) -> bool:
|
||||
"""设置SteamTools解锁"""
|
||||
st_path = self.steam_path / "config" / "stplug-in"
|
||||
st_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
lua_content = f'addappid({app_id}, 1, "None")\n'
|
||||
|
||||
for depot in depot_data:
|
||||
if version_lock and depot_map and depot.depot_id in depot_map:
|
||||
for manifest_id in depot_map[depot.depot_id]:
|
||||
lua_content += (
|
||||
f'addappid({depot.depot_id}, 1, "{depot.decryption_key}")\n'
|
||||
f'setManifestid({depot.depot_id},"{manifest_id}")\n'
|
||||
)
|
||||
else:
|
||||
lua_content += (
|
||||
f'addappid({depot.depot_id}, 1, "{depot.decryption_key}")\n'
|
||||
)
|
||||
|
||||
lua_file = st_path / f"{app_id}.lua"
|
||||
lua_file.write_text(lua_content, encoding="utf-8")
|
||||
|
||||
return True
|
||||
@@ -1,32 +0,0 @@
|
||||
from typing import Tuple
|
||||
|
||||
from ..constants import REGION_CHECK_URL
|
||||
from ..network.client import HttpClient
|
||||
from ..logger import Logger
|
||||
|
||||
|
||||
class RegionDetector:
|
||||
"""地区检测器"""
|
||||
|
||||
def __init__(self, client: HttpClient, logger: Logger):
|
||||
self.client = client
|
||||
self.logger = logger
|
||||
|
||||
async def check_cn(self) -> Tuple[bool, str]:
|
||||
"""检查是否在中国大陆"""
|
||||
try:
|
||||
req = await self.client.get(REGION_CHECK_URL)
|
||||
body = req.json()
|
||||
is_cn = bool(body.get("flag", True))
|
||||
country = body.get("country", "Unknown")
|
||||
|
||||
if not is_cn:
|
||||
self.logger.info(
|
||||
f"您在非中国大陆地区({country})上使用了项目, "
|
||||
"已自动切换回Github官方下载CDN"
|
||||
)
|
||||
|
||||
return is_cn, country
|
||||
except Exception as e:
|
||||
self.logger.warning("检查服务器位置失败,自动认为你在中国大陆")
|
||||
return True, "CN"
|
||||
@@ -1,32 +0,0 @@
|
||||
import vdf
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from ..models import DepotInfo
|
||||
|
||||
|
||||
def parse_key_file(content: bytes) -> List[DepotInfo]:
|
||||
"""解析密钥文件"""
|
||||
try:
|
||||
depots = vdf.loads(content.decode("utf-8"))["depots"]
|
||||
return [
|
||||
DepotInfo(depot_id=d_id, decryption_key=d_info["DecryptionKey"])
|
||||
for d_id, d_info in depots.items()
|
||||
]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def parse_manifest_filename(filename: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""解析清单文件名"""
|
||||
if not filename.endswith(".manifest"):
|
||||
return None, None
|
||||
|
||||
name = filename.replace(".manifest", "")
|
||||
if "_" not in name:
|
||||
return None, None
|
||||
|
||||
parts = name.split("_", 1)
|
||||
if len(parts) != 2 or not all(p.isdigit() for p in parts):
|
||||
return None, None
|
||||
|
||||
return parts[0], parts[1]
|
||||
Reference in New Issue
Block a user