Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ab26f7848 | |||
| d646eacda1 | |||
| fb029f729b | |||
| 5ef6663269 | |||
| 5c86b53000 | |||
| efb6adbd9b | |||
| 209202b5cf | |||
| c0a081e226 |
@@ -1,242 +0,0 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*]
|
||||
charset = utf-8
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.{js,json,xaml,axaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# c# 文件
|
||||
[*.cs]
|
||||
|
||||
#### Core EditorConfig 选项 ####
|
||||
|
||||
# 缩进和间距
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# 新行首选项
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET 编码约定 ####
|
||||
|
||||
# 组织 Using
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. 和 Me. 首选项
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
|
||||
# 语言关键字与 bcl 类型首选项
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
# 括号首选项
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# 修饰符首选项
|
||||
dotnet_style_require_accessibility_modifiers = never
|
||||
|
||||
# 表达式级首选项
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_return = true
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# 字段首选项
|
||||
dotnet_style_readonly_field = true
|
||||
|
||||
# 参数首选项
|
||||
dotnet_code_quality_unused_parameters = all
|
||||
|
||||
# 禁止显示首选项
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
#### c# 编码约定 ####
|
||||
|
||||
# var 首选项
|
||||
csharp_style_var_elsewhere = true
|
||||
csharp_style_var_for_built_in_types = true
|
||||
csharp_style_var_when_type_is_apparent = true
|
||||
|
||||
# Expression-bodied 成员
|
||||
csharp_style_expression_bodied_accessors = true
|
||||
csharp_style_expression_bodied_constructors = false
|
||||
csharp_style_expression_bodied_indexers = true
|
||||
csharp_style_expression_bodied_lambdas = true
|
||||
csharp_style_expression_bodied_local_functions = false
|
||||
csharp_style_expression_bodied_methods = false
|
||||
csharp_style_expression_bodied_operators = false
|
||||
csharp_style_expression_bodied_properties = true
|
||||
|
||||
# 模式匹配首选项
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true
|
||||
csharp_style_prefer_switch_expression = true
|
||||
|
||||
# Null 检查首选项
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# 修饰符首选项
|
||||
csharp_prefer_static_local_function = true
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
|
||||
|
||||
# 代码块首选项
|
||||
csharp_prefer_braces = false
|
||||
csharp_prefer_simple_using_statement = true
|
||||
|
||||
# 表达式级首选项
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_deconstructed_variable_declaration = true
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_pattern_local_over_anonymous_function = true
|
||||
csharp_style_prefer_index_operator = true
|
||||
csharp_style_prefer_range_operator = true
|
||||
csharp_style_throw_expression = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
|
||||
# "using" 指令首选项
|
||||
csharp_using_directive_placement = outside_namespace
|
||||
|
||||
#### C# 格式规则 ####
|
||||
|
||||
# 新行首选项
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# 缩进首选项
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# 空格键首选项
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# 包装首选项
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### 命名样式 ####
|
||||
|
||||
# 命名规则
|
||||
|
||||
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
|
||||
|
||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
|
||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
|
||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||
|
||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
|
||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
|
||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||
|
||||
# 符号规范
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.类型.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.类型.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.非字段成员.required_modifiers =
|
||||
|
||||
# 命名样式
|
||||
|
||||
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.以_i_开始.required_prefix = I
|
||||
dotnet_naming_style.以_i_开始.required_suffix =
|
||||
dotnet_naming_style.以_i_开始.word_separator =
|
||||
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
||||
|
||||
# IDE0010: 添加缺失的事例
|
||||
dotnet_diagnostic.IDE0010.severity = none
|
||||
|
||||
# IDE0011: 添加大括号
|
||||
dotnet_diagnostic.IDE0011.severity = none
|
||||
|
||||
# IDE0040: 添加可访问性修饰符
|
||||
dotnet_diagnostic.IDE0040.severity = none
|
||||
|
||||
# IDE0130: 命名空间与文件夹结构不匹配
|
||||
dotnet_style_namespace_match_folder = false
|
||||
|
||||
# IDE0130: 命名空间与文件夹结构不匹配
|
||||
dotnet_diagnostic.IDE0130.severity = none
|
||||
@@ -0,0 +1,13 @@
|
||||
### 该问题是怎么引起的?
|
||||
|
||||
|
||||
|
||||
### 重现步骤
|
||||
|
||||
|
||||
|
||||
### 报错信息
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
### 相关的Issue
|
||||
|
||||
|
||||
### 原因(目的、解决的问题等)
|
||||
|
||||
|
||||
### 描述(做了什么,变更了什么)
|
||||
|
||||
|
||||
### 测试用例(新增、改动、可能影响的功能)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
name: Bug Report 🐛
|
||||
about: 创建 Bug 报告以帮助我们改进 / Create a report to help us improve
|
||||
title: "🐛[BUG] "
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### 🐛 描述(Description)
|
||||
|
||||
<!--
|
||||
详细地描述 bug,让大家都能理解
|
||||
-->
|
||||
|
||||
### 📷 复现步骤(Steps to Reproduce)
|
||||
|
||||
<!--
|
||||
清晰描述复现步骤,让别人也能看到问题
|
||||
-->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### 📄 [异常/日志]信息([Exception/Log] Information)
|
||||
|
||||
### 🚑 基本信息(Basic Information)
|
||||
|
||||
- 程序版本(Program Version):
|
||||
- 系统版本号(OS Version): <!-- example Windows 10.19042.844(see winver) -->
|
||||
|
||||
### 🖼 截图(Screenshots)
|
||||
|
||||
<!--
|
||||
截图可以贴在这里
|
||||
-->
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: Feature request 👑
|
||||
about: 对程序的需求或建议 / Suggest an idea for program
|
||||
title: "👑[Enhancement] "
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### 🥰 需求描述(Description)
|
||||
|
||||
<!--
|
||||
详细地描述需求,让大家都能理解
|
||||
-->
|
||||
|
||||
### 🧐 解决方案(Solution)
|
||||
|
||||
<!--
|
||||
如果你有解决方案,在这里清晰地阐述
|
||||
-->
|
||||
|
||||
### 🚑 其他信息(Other Information)
|
||||
|
||||
<!--
|
||||
如截图等其他信息可以贴在这里
|
||||
-->
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: question 🧐
|
||||
about: 对程序使用的疑问或需要帮助 / Questions about the use of the program or need help
|
||||
title: "🧐[Question] "
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### 🧐 问题描述(Description)
|
||||
|
||||
<!--
|
||||
详细地描述需求,让大家都能理解
|
||||
-->
|
||||
|
||||
### 🚑 其他信息(Other Information)
|
||||
|
||||
<!--
|
||||
如截图等其他信息可以贴在这里
|
||||
-->
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
name: Sync To Gitee
|
||||
|
||||
on: [ push, delete, create ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync to Gitee
|
||||
uses: wearerequired/git-mirror-action@master
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
with:
|
||||
source-repo: "git@github.com:SteamTools-Team/SteamTools.git"
|
||||
destination-repo: "git@gitee.com:rmbgame/SteamTools"
|
||||
@@ -1,61 +0,0 @@
|
||||
name: .NET
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest, macOS-latest, ubuntu-latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup .NET 5
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 5.0.x
|
||||
- name: Setup .NET 6
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 6.0.100-preview.4.21255.9
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: dotnet info
|
||||
run: dotnet --info
|
||||
|
||||
- name: dotnet restore SteamToolsV2+.sln
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: dotnet restore ./SteamToolsV2+.sln
|
||||
|
||||
- name: dotnet restore SteamToolsV2+.Linux.sln
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: dotnet restore ./SteamToolsV2+.Linux.sln
|
||||
|
||||
- name: dotnet restore SteamToolsV2+.Mac.sln
|
||||
if: matrix.os == 'macOS-latest'
|
||||
run: dotnet restore ./SteamToolsV2+.Mac.sln
|
||||
|
||||
- name: dotnet AvaloniaApp build
|
||||
run: dotnet build ./src/ST.Client.Desktop.Avalonia.App/ST.Client.Desktop.Avalonia.App.csproj
|
||||
- name: dotnet CommonUnit build
|
||||
run: dotnet build ./tests/Common.UnitTest/Common.UnitTest.csproj
|
||||
- name: dotnet ST.Client.UnitTest build
|
||||
run: dotnet build ./tests/ST.Client.UnitTest/ST.Client.UnitTest.csproj
|
||||
- name: dotnet ST.Client.Desktop.UnitTest build
|
||||
run: dotnet build ./tests/ST.Client.Desktop.UnitTest/ST.Client.Desktop.UnitTest.csproj
|
||||
|
||||
- name: dotnet CommonUnit test
|
||||
run: dotnet test ./tests/Common.UnitTest/Common.UnitTest.csproj
|
||||
- name: dotnet ST.Client.UnitTest test
|
||||
run: dotnet test ./tests/ST.Client.UnitTest/ST.Client.UnitTest.csproj
|
||||
- name: dotnet ST.Client.Desktop.UnitTest test
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: dotnet test ./tests/ST.Client.Desktop.UnitTest/ST.Client.Desktop.UnitTest.csproj
|
||||
@@ -338,26 +338,4 @@ ASALocalRun/
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Design Resource
|
||||
*.psd
|
||||
|
||||
# Android Resource Designer
|
||||
Resource.designer.cs
|
||||
|
||||
# csproject launchSettings
|
||||
launchSettings.json
|
||||
|
||||
# Android KeyStore
|
||||
*.jks
|
||||
/Xamarin.Android.KeyStore.props
|
||||
healthchecksdb
|
||||
@@ -1,21 +0,0 @@
|
||||
[submodule "references/Titanium-Web-Proxy"]
|
||||
path = references/Titanium-Web-Proxy
|
||||
url = https://github.com/SteamTools-Team/Titanium-Web-Proxy.git
|
||||
[submodule "references/MetroRadiance"]
|
||||
path = references/MetroRadiance
|
||||
url = https://github.com/rmbadmin/MetroRadiance.git
|
||||
[submodule "references/SteamAchievementManager"]
|
||||
path = references/SteamAchievementManager
|
||||
url = https://github.com/SteamTools-Team/SteamAchievementManager.git
|
||||
[submodule "references/Steam4NET"]
|
||||
path = references/Steam4NET
|
||||
url = https://github.com/SteamTools-Team/Steam4NET.git
|
||||
[submodule "references/Depressurizer"]
|
||||
path = references/Depressurizer
|
||||
url = https://github.com/SteamTools-Team/Depressurizer.git
|
||||
[submodule "references/AvaloniaGif"]
|
||||
path = references/AvaloniaGif
|
||||
url = https://github.com/SteamTools-Team/AvaloniaGif
|
||||
[submodule "references/WinAuth"]
|
||||
path = references/WinAuth
|
||||
url = https://github.com/SteamTools-Team/WinAuth
|
||||
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 427 KiB After Width: | Height: | Size: 427 KiB |
@@ -0,0 +1,155 @@
|
||||
[
|
||||
{
|
||||
"Index": 1,
|
||||
"Name": "*瑞",
|
||||
"Amount": 5,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "请大佬喝瓶饮料",
|
||||
"PayTime": "2020-12-17 19:41:57"
|
||||
},
|
||||
{
|
||||
"Index": 2,
|
||||
"Name": "**耀",
|
||||
"Amount": 5,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "小小支持一波!",
|
||||
"PayTime": "2020-12-17 20:46:01"
|
||||
},
|
||||
{
|
||||
"Index": 3,
|
||||
"Name": "**皓",
|
||||
"Amount": 1,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "2077掏空了我,等挂卡出来。在。。em",
|
||||
"PayTime": "2020-12-17 22:41:27"
|
||||
},
|
||||
{
|
||||
"Index": 4,
|
||||
"Name": "**宇",
|
||||
"Amount": 1,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "",
|
||||
"PayTime": "2020-12-18 08:39:42"
|
||||
},
|
||||
{
|
||||
"Index": 5,
|
||||
"Name": "*钊",
|
||||
"Amount": 20,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "其乐坛友bzll6538一点心意",
|
||||
"PayTime": "2020-12-19 17:10:44"
|
||||
},
|
||||
{
|
||||
"Index": 6,
|
||||
"Name": "R*n",
|
||||
"Amount": 20,
|
||||
"OrderNumber": "",
|
||||
"PayType": 2,
|
||||
"Remark": "绿豆",
|
||||
"PayTime": "2020-12-17 14:32:00"
|
||||
},
|
||||
{
|
||||
"Index": 7,
|
||||
"Name": "*炜",
|
||||
"Amount": 100,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "其乐坛友Davyheaven",
|
||||
"PayTime": "2020-12-20 12:02:17"
|
||||
},
|
||||
{
|
||||
"Index": 8,
|
||||
"Name": "**智",
|
||||
"Amount": 20,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "派大星",
|
||||
"PayTime": "2020-12-20 12:21:04"
|
||||
},
|
||||
{
|
||||
"Index": 9,
|
||||
"Name": "*森",
|
||||
"Amount": 66,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "区区心意,不成敬意(三个六给不起)",
|
||||
"PayTime": "2020-12-21 23:03:26"
|
||||
},
|
||||
{
|
||||
"Index": 10,
|
||||
"Name": "*珂",
|
||||
"Amount": 50,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "其乐YK93,支持大佬",
|
||||
"PayTime": "2020-12-21 23:18:09"
|
||||
},
|
||||
{
|
||||
"Index": 11,
|
||||
"Name": "**丰",
|
||||
"Amount": 1,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "",
|
||||
"PayTime": "2020-12-22 12:33:46"
|
||||
},
|
||||
{
|
||||
"Index": 12,
|
||||
"Name": "Ruby",
|
||||
"Amount": 77,
|
||||
"OrderNumber": "",
|
||||
"PayType": 2,
|
||||
"Remark": "资瓷一下",
|
||||
"PayTime": "2020-12-24 19:40:25"
|
||||
},
|
||||
{
|
||||
"Index": 13,
|
||||
"Name": "**桐",
|
||||
"Amount": 1,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "",
|
||||
"PayTime": "2020-12-26 09:32:57"
|
||||
},
|
||||
{
|
||||
"Index": 14,
|
||||
"Name": "**珂",
|
||||
"Amount": 5,
|
||||
"OrderNumber": "",
|
||||
"PayType": 1,
|
||||
"Remark": "其乐坛友wjkhj123,小小支持一波",
|
||||
"PayTime": "2020-12-26 13:28:07"
|
||||
},
|
||||
{
|
||||
"Index": 15,
|
||||
"Name": "z.w",
|
||||
"Amount": 10,
|
||||
"OrderNumber": "",
|
||||
"PayType": 2,
|
||||
"Remark": "滋瓷一下",
|
||||
"PayTime": "2020-12-26 15:34:50"
|
||||
},
|
||||
{
|
||||
"Index": 16,
|
||||
"Name": "**林",
|
||||
"Amount": 6,
|
||||
"OrderNumber": "",
|
||||
"PayType": 2,
|
||||
"Remark": "一丶心意,大佬加油!",
|
||||
"PayTime": "2020-12-26 18:25:06"
|
||||
},
|
||||
{
|
||||
"Index": 17,
|
||||
"Name": "**立",
|
||||
"Amount": 1,
|
||||
"OrderNumber": "",
|
||||
"PayType": 2,
|
||||
"Remark": "",
|
||||
"PayTime": "2020-12-26 18:56:57"
|
||||
}
|
||||
]
|
||||
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 199 KiB After Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -1,20 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<TrimMode>link</TrimMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" Exists('$(MSBuildProjectDirectory)\..\..\Unicorn.pfx') ">
|
||||
<DefineConstants>SIGN_ASSEMBLY;$(DefineConstants)</DefineConstants>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\Unicorn.pfx</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)'=='Release' ">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
Before Width: | Height: | Size: 364 KiB After Width: | Height: | Size: 364 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 364 KiB After Width: | Height: | Size: 364 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,167 +1,85 @@
|
||||
<h1 align="center">Steam++ Tools 🧰</h1>
|
||||
# Steam++ SteamTools
|
||||
|
||||
<div align="center">
|
||||
|
||||
「Steam++」is a toolkit that contains a variety of Steam tools, most of which require you to download and install Steam in order to use them.
|
||||
「Steam++」是一个包含多种Steam工具功能的工具箱。
|
||||
|
||||

|
||||
[](https://github.com/rmbadmin/SteamTools/releases/latest)
|
||||
[](LICENSE)
|
||||
[](https://github.com/rmbadmin/SteamTools/stargazers)
|
||||
[](https://github.com/rmbadmin/SteamTools/network/members)
|
||||

|
||||
[](https://github.com/SteamTools-Team/SteamTools/search?l=c%23)
|
||||
[](https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-4)
|
||||
[](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9)
|
||||
此工具的大部分功能都是需要您下载安装Steam才能使用。
|
||||
|
||||
[](https://github.com/AvaloniaUI/Avalonia)
|
||||
[](https://github.com/xamarin/Xamarin.Forms)
|
||||
[](https://github.com/ant-design/ant-design)
|
||||
[](https://github.com/ant-design-blazor/ant-design-blazor)
|
||||
------
|
||||
|
||||
[](https://actions-badge.atrox.dev/SteamTools-Team/SteamTools/goto?ref=develop)
|
||||
[](https://github.com/SteamTools-Team/SteamTools)
|
||||
[](https://gitee.com/rmbgame/SteamTools)
|
||||
</div>
|
||||
## 核心功能
|
||||
|
||||
<div align="center"><img src="./resources/screenshots.en.jpg" /></div>
|
||||
### 1. 反代Steam的社区网页使其能正常访问
|
||||
|
||||
English | [简体中文](./README.md)
|
||||
功能类似羽翼城大佬的Steam302,使用[Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)开源项目进行本地反代,相比302工具具有更快的启动速度,以及支持简单的脚本注入。
|
||||
|
||||
## ✨ Functions
|
||||
1. Local reverse proxy for Steam's community webpage to enable normal access in **the Chinese Mainland, Mainland of China**
|
||||
2. Quickly switch the Steam account that the current PC has remembered to log into
|
||||
- This feature is to read the local user login records stored under the Steam path to directly display the operation, you can switch between multiple accounts without re-entering passwords and tokens.
|
||||
3. Achievement stat modifications for Steam games
|
||||
- Function reference SteamAchievementManager for secondary development, modified the game list loading and operation ease of use.
|
||||
4. Steam Local Two-Step Authenticator
|
||||
- The function reference [WinAuth](https://github.com/winauth/winauth) is developed to enable you to view your token without launching the mobile version of Steam App, similar function software are [WinAuth](https://github.com/winauth/winauth), [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator).
|
||||
5. Some game tools
|
||||
- Currently there is a forced game borderless windowing, CSGO fix mistake VAC shield.
|
||||
---
|
||||
### 2. 快速切换当前PC已经记住登陆的Steam账号
|
||||
|
||||
<!--Prerequisites Microsoft Visual C++ 2015-2019 Redistributable [64 bit](https://aka.ms/vs/16/release/vc_redist.x64.exe) / [32 bit](https://aka.ms/vs/16/release/vc_redist.x86.exe)-->
|
||||
## 🖥 Supported Operating Systems
|
||||
- Desktop(Only 64 bit is supported)
|
||||
- Windows 10 1607+ / Windows Server, version 1909+ / Windows Server 2019 / Windows Server 2016
|
||||
- Windows 8.1 / Windows Server 2012 R2
|
||||
- Windows 7 SP1 [ESU](https://docs.microsoft.com/troubleshoot/windows-client/windows-7-eos-faq/windows-7-extended-security-updates-faq)
|
||||
- Prerequisites
|
||||
- KB3063858 [64 bit](https://www.microsoft.com/download/details.aspx?id=47442) / [32 bit](https://www.microsoft.com/download/details.aspx?id=47409)
|
||||
- macOS 10.13 High Sierra Or Higher
|
||||
- Linux Distribution
|
||||
- Arch Linux
|
||||
- Alpine Linux 3.11+
|
||||
- CentOS 7+
|
||||
- Debian 9+
|
||||
- Deepin 20.1 / UOS 20
|
||||
- Fedora 32+
|
||||
- Linux Mint 18+
|
||||
- openSUSE 15+
|
||||
- Red Hat Enterprise Linux 7+
|
||||
- SUSE Enterprise Linux (SLES) 12 SP2+
|
||||
- Ubuntu 20.10, 20.04, 18.04, 16.04
|
||||
- Mobile(Only ARM64 or ARM32 are supported)
|
||||
- Android 5.0+
|
||||
- iOS 10.0+
|
||||
该功能是读取Steam路径下存储的本地用户登录记录直接展示操作,可以多账号切换无需重新输入密码和令牌。
|
||||
|
||||
## ⛔ Unsupported Operating System
|
||||
- Windows 8
|
||||
- [Due to Microsoft's official support for the product has ended](https://docs.microsoft.com/lifecycle/products/windows-8), so this program cannot run on this operating system, [It is recommended to upgrade to Windows 8.1](https://support.microsoft.com/windows/update-to-windows-8-1-from-windows-8-17fc54a7-a465-6b5a-c1a0-34140afd0669)
|
||||
- Windows Server 2012 / 2008 R2 SP1
|
||||
- Only version 1.X is available, and version 2.X is not supported. It is recommended to upgrade to **Windows Server 2012 R2** or higher
|
||||
- Windows Server / Linux version without desktop GUI
|
||||
---
|
||||
### 3. Steam游戏的成就统计管理功能
|
||||
功能参考[SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)进行二次开发,修改了游戏列表的加载和操作易用性。
|
||||
|
||||
## 🌏 Roadmap
|
||||
Read what we [milestones](https://github.com/SteamTools-Team/SteamTools/milestones), and feel free to ask questions.
|
||||
---
|
||||
|
||||
## ⌨️ Development Environment
|
||||
[Visual Studio 2019 Version 16.10 Or Higher](https://visualstudio.microsoft.com/vs/) Or [JetBrains Rider](https://www.jetbrains.com/rider/) Or ~~[Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/)~~ Or ~~[Visual Studio Code](https://code.visualstudio.com/)~~
|
||||
- Supported Operating Systems
|
||||
- [Windows 10 version 2004 or higher: Home, Professional, Education, and Enterprise (LTSC and S are not supported)](https://docs.microsoft.com/en-us/visualstudio/releases/2019/system-requirements)
|
||||
- [macOS 10.13 High Sierra Or Higher](https://docs.microsoft.com/en-us/visualstudio/productinfo/vs2019-system-requirements-mac)
|
||||
- Workload
|
||||
- Web and Cloud
|
||||
- ASP.NET and Web Development
|
||||
- Desktop and Mobile Applications
|
||||
- .NET Desktop Development
|
||||
- UWP Development
|
||||
- Mobile Development using .Net
|
||||
- Other Toolsets
|
||||
- .NET Core Cross Platform Development
|
||||
- Single Component
|
||||
- GitHub Extension for Visual Studio
|
||||
- Windows 10 SDK (10.0.19041.0)
|
||||
- [Visual Studio Marketplace](https://marketplace.visualstudio.com/)
|
||||
- [Avalonia for Visual Studio](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
|
||||
- [NUnit VS Templates](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
|
||||
### 4. Steam本地两步身份验证器
|
||||
功能参考[WinAuth](https://github.com/winauth/winauth)开发,可以使您不用启动移动版Steam App也能查看您的令牌,功能类似的软件有[WinAuth](https://github.com/winauth/winauth)、[SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)。
|
||||
|
||||
## 📄 Thanks to the following Open Source Projects
|
||||
---
|
||||
|
||||
### 5. 一些游戏工具
|
||||
目前已有强制游戏无边框窗口化,CSGO修复VAC屏蔽。
|
||||
这一块是随缘做一些我经常用或者闲着没事捣鼓的功能。
|
||||
|
||||
---
|
||||
|
||||
## 预计后续添加的功能
|
||||
|
||||
### Steam自动挂卡
|
||||
尝试用社区反代功能结合成就解锁功能来重新实现,目的是实现在软件内登录Steam即可直接获取徽章卡片信息并开始挂卡。
|
||||
|
||||
### Steam皮肤设计器
|
||||
挖坑画大饼,可视化编辑Steam皮肤,而且如果软件能上架Steam的话打算支持创意工坊分享设计的Steam皮肤,短期内肯定做不完。
|
||||
|
||||
### 插件形式加载运行ASF
|
||||
以插件形式支持ASF在工具内运行并增强ASF在Windows Desktop环境下的使用。
|
||||
|
||||
### Steam自定义封面管理
|
||||
增强Steam自定义封面的管理以及从[SteamGridDB](https://www.steamgriddb.com/)快速匹配下载应用封面。
|
||||
|
||||
### 更多其它游戏平台功能
|
||||
|
||||
|
||||
### 更多非作弊游戏功能
|
||||
|
||||
---
|
||||
|
||||
## 运行环境
|
||||
> 程序使用C# WPF在 .NET Framework4.7.2环境下开发,如果无法运行请下载安装[.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)。
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
> [GPL-3.0 License](LICENSE.txt)
|
||||
> 根据GPLv3许可发布的开源/免费软件。
|
||||
|
||||
---
|
||||
|
||||
## 感谢以下开源项目
|
||||
* [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
|
||||
* [MetroRadiance](https://github.com/Grabacr07/MetroRadiance)
|
||||
* [MetroTrilithon](https://github.com/Grabacr07/MetroTrilithon)
|
||||
* [Livet](https://github.com/runceel/Livet)
|
||||
* [StatefulModel](https://github.com/ugaya40/StatefulModel)
|
||||
* [StatefulModel](https://github.com/Grabacr07/MetroTrilithonhttps://github.com/ugaya40/StatefulModel)
|
||||
* [Hardcodet.NotifyIcon](https://github.com/HavenDV/Hardcodet.NotifyIcon.Wpf.NetCore)
|
||||
* [System.Reactive](https://github.com/dotnet/reactive)
|
||||
* [Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)
|
||||
* [BrotliSharpLib](https://github.com/master131/BrotliSharpLib)
|
||||
* [Portable.BouncyCastle](https://github.com/novotnyllc/bc-csharp)
|
||||
* [Ninject](https://github.com/ninject/Ninject)
|
||||
* [log4net](https://github.com/apache/logging-log4net)
|
||||
* [SteamDB-API](https://github.com/SteamDB-API/api)
|
||||
* [SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)
|
||||
* [ArchiSteamFarm](https://github.com/JustArchiNET/ArchiSteamFarm)
|
||||
* [Steam4NET](https://github.com/SteamRE/Steam4NET)
|
||||
* [WinAuth](https://github.com/winauth/winauth)
|
||||
* [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
* [Gameloop.Vdf](https://github.com/shravan2x/Gameloop.Vdf)
|
||||
* [DnsClient.NET](https://github.com/MichaCo/DnsClient.NET)
|
||||
* [Costura.Fody](https://github.com/Fody/Costura)
|
||||
* [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)
|
||||
* [Nito.Comparers](https://github.com/StephenCleary/Comparers)
|
||||
* [Crc32.NET](https://github.com/force-net/Crc32.NET)
|
||||
* [gfoidl.Base64](https://github.com/gfoidl/Base64)
|
||||
* [sqlite-net-pcl](https://github.com/praeclarum/sqlite-net)
|
||||
* [Polly](https://github.com/App-vNext/Polly)
|
||||
* [TaskScheduler](https://github.com/dahall/taskscheduler)
|
||||
* [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
|
||||
* [LibVLCSharp](https://github.com/videolan/libvlcsharp)
|
||||
* [Depressurizer](https://github.com/Depressurizer/Depressurizer)
|
||||
* [NLog](https://github.com/nlog/NLog)
|
||||
* [NUnit](https://github.com/nunit/nunit)
|
||||
* [ReactiveUI](https://github.com/reactiveui/reactiveui)
|
||||
* [MessageBox.Avalonia](https://github.com/AvaloniaUtils/MessageBox.Avalonia)
|
||||
* [AvaloniaUI](https://github.com/AvaloniaUI/Avalonia)
|
||||
* [AvaloniaGif](https://github.com/jmacato/AvaloniaGif)
|
||||
* [Avalonia XAML Behaviors](https://github.com/wieslawsoltes/AvaloniaBehaviors)
|
||||
* [APNG.NET](https://github.com/jz5/APNG.NET)
|
||||
* [Chromium Embedded Framework (CEF)](https://github.com/chromiumembedded/cef)
|
||||
* [Moq](https://github.com/moq/moq4)
|
||||
* [NPOI](https://github.com/nissl-lab/npoi)
|
||||
* [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)
|
||||
* [AspNet.Security.OpenId.Providers](https://github.com/aspnet-contrib/AspNet.Security.OpenId.Providers)
|
||||
* [AspNet.Security.OAuth.Providers](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers)
|
||||
* [CefNet](https://github.com/CefNet/CefNet)
|
||||
* [CefSharp](https://github.com/cefsharp/CefSharp)
|
||||
* [React](https://github.com/facebook/react)
|
||||
* [Ant Design](https://github.com/ant-design/ant-design)
|
||||
* [Ant Design Blazor](https://github.com/ant-design-blazor/ant-design-blazor)
|
||||
* [Toast messages for Xamarin.iOS](https://github.com/andrius-k/Toast)
|
||||
* [XF.Material Library](https://github.com/Baseflow/XF-Material-Library)
|
||||
* [Visual Studio App Center SDK for .NET](https://github.com/microsoft/appcenter-sdk-dotnet)
|
||||
* [AppCenter-XMac](https://github.com/nor0x/AppCenter-XMac)
|
||||
* [MSBuild.Sdk.Extras](https://github.com/novotnyllc/MSBuildSdkExtras)
|
||||
* [Xamarin.Essentials](https://github.com/xamarin/essentials)
|
||||
* [Xamarin.Forms](https://github.com/xamarin/Xamarin.Forms)
|
||||
* [Open Source Components for Xamarin](https://github.com/xamarin/XamarinComponents)
|
||||
* [AndroidX for Xamarin.Android](https://github.com/xamarin/AndroidX)
|
||||
* [MSAL.NET](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet)
|
||||
* [Entity Framework Core](https://github.com/dotnet/efcore)
|
||||
* [ASP.NET Core](https://github.com/dotnet/aspnetcore)
|
||||
* [Windows Forms](https://github.com/dotnet/winforms)
|
||||
* [Windows Presentation Foundation (WPF)](https://github.com/dotnet/wpf)
|
||||
* [C#/WinRT](https://github.com/microsoft/CsWinRT)
|
||||
* [command-line-api](https://github.com/dotnet/command-line-api)
|
||||
* [.NET Runtime](https://github.com/dotnet/runtime)
|
||||
* [Fluent UI System Icons](https://github.com/microsoft/fluentui-system-icons)
|
||||
* [Material design icons](https://github.com/google/material-design-icons)
|
||||
* [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
@@ -1,179 +1,89 @@
|
||||
<h1 align="center">Steam++ 工具箱 🧰</h1>
|
||||
# Steam++工具箱
|
||||
|
||||
<div align="center">
|
||||
「Steam++」是一个包含多种Steam工具功能的工具箱。
|
||||
|
||||
「Steam++」是一个包含多种Steam工具功能的工具箱,此工具的大部分功能都是需要您下载安装Steam才能使用。
|
||||
此工具的大部分功能都是需要您下载安装Steam才能使用。
|
||||
|
||||

|
||||
[](https://github.com/rmbadmin/SteamTools/releases/latest)
|
||||
[](LICENSE)
|
||||
[](https://github.com/rmbadmin/SteamTools/stargazers)
|
||||
[](https://github.com/rmbadmin/SteamTools/network/members)
|
||||

|
||||
[](https://github.com/SteamTools-Team/SteamTools/search?l=c%23)
|
||||
[](https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-4)
|
||||
[](https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-9)
|
||||
------
|
||||
|
||||
[](https://github.com/AvaloniaUI/Avalonia)
|
||||
[](https://github.com/xamarin/Xamarin.Forms)
|
||||
[](https://github.com/ant-design/ant-design)
|
||||
[](https://github.com/ant-design-blazor/ant-design-blazor)
|
||||
## 核心功能
|
||||
|
||||
[](https://actions-badge.atrox.dev/SteamTools-Team/SteamTools/goto?ref=develop)
|
||||
[](https://github.com/SteamTools-Team/SteamTools)
|
||||
[](https://gitee.com/rmbgame/SteamTools)
|
||||
[](https://qm.qq.com/cgi-bin/qm/qr?k=JVlZF31RKSDqcsxfEaw6AQE75P2NF18F&jump_from=webapi)
|
||||
</div>
|
||||
### 1. 反代Steam的社区网页使其能正常访问
|
||||
|
||||
<div align="center"><img src="./resources/screenshots.jpg" /></div>
|
||||
功能类似羽翼城大佬的Steam302,使用[Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)开源项目进行本地反代,相比302工具具有更快的启动速度,以及支持简单的脚本注入。
|
||||
|
||||
[English](./README.en.md) | 简体中文
|
||||
---
|
||||
### 2. 快速切换当前PC已经记住登陆的Steam账号
|
||||
|
||||
## ✨ 功能
|
||||
1. 反代 Steam 的社区网页使其能正常访问
|
||||
- 功能类似羽翼城大佬的 [steamcommunity_302](https://www.dogfight360.com/blog/686/)
|
||||
- 使用 [Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy) 开源项目进行本地反代,使国内用户可以正常访问 Steam 社区页
|
||||
- 相比 **302工具** 具有更快的启动速度,以及支持简单的脚本注入(还顺便支持了Pixiv、Discord、Twitch等网站的反代支持)
|
||||
2. 快速切换当前设备已记住登陆的 Steam 账号
|
||||
- 该功能是读取 Steam 路径下存储的本地用户登录记录直接展示操作,可以多账号切换无需重新输入密码和令牌
|
||||
3. Steam游戏的成就统计管理功能
|
||||
- 功能参考 [SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager) 进行二次开发,修改了游戏列表的加载和操作易用性
|
||||
4. Steam本地两步身份验证器
|
||||
- 功能参考 [WinAuth](https://github.com/winauth/winauth) 开发,可以使您不用启动移动版 Steam App 也能查看您的令牌
|
||||
- 本地令牌交易市场报价确认
|
||||
- 功能类似的软件例如:
|
||||
- [WinAuth](https://github.com/winauth/winauth)
|
||||
- [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
5. 其他游戏工具
|
||||
- 目前已有强制游戏无边框窗口化,CSGO 修复 VAC 误屏蔽
|
||||
该功能是读取Steam路径下存储的本地用户登录记录直接展示操作,可以多账号切换无需重新输入密码和令牌。
|
||||
|
||||
<!--发布配置SelfContained=true时会自动打包VC++相关程序集-->
|
||||
<!--先决条件 Microsoft Visual C++ 2015-2019 Redistributable [64 位](https://aka.ms/vs/16/release/vc_redist.x64.exe) / [32 位](https://aka.ms/vs/16/release/vc_redist.x86.exe)-->
|
||||
## 🖥 系统要求
|
||||
- 桌面端(仅支持 64 位)
|
||||
- Windows 10 1607+ / Windows Server 版本 1903+ / Windows Server 2019 / Windows Server 2016
|
||||
- Windows 8.1 / Windows Server 2012 R2
|
||||
- Windows 7 SP1 [ESU](https://docs.microsoft.com/zh-cn/troubleshoot/windows-client/windows-7-eos-faq/windows-7-extended-security-updates-faq)
|
||||
- 先决条件
|
||||
- KB3063858 [64 位](https://www.microsoft.com/zh-cn/download/details.aspx?id=47442) / [32 位](https://www.microsoft.com/zh-cn/download/details.aspx?id=47409)
|
||||
- macOS 10.13 High Sierra 或更高版本
|
||||
- Linux 发行版
|
||||
- Arch Linux
|
||||
- Alpine Linux 3.11+
|
||||
- CentOS 7+
|
||||
- Debian 9+
|
||||
- Deepin 20.1 / UOS 20
|
||||
- Fedora 32+
|
||||
- Linux Mint 18+
|
||||
- openSUSE 15+
|
||||
- Red Hat Enterprise Linux 7+
|
||||
- SUSE Enterprise Linux (SLES) 12 SP2+
|
||||
- Ubuntu 20.10, 20.04, 18.04, 16.04
|
||||
- 移动端(仅支持 ARM64 或 ARM32)
|
||||
- Android 5.0+
|
||||
- iOS 10.0+
|
||||
---
|
||||
### 3. Steam游戏的成就统计管理功能
|
||||
功能参考[SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)进行二次开发,修改了游戏列表的加载和操作易用性。
|
||||
|
||||
## ⛔ 不受支持的操作系统
|
||||
- Windows 8
|
||||
- [由于微软官方对该产品的支持已结束](https://docs.microsoft.com/zh-cn/lifecycle/products/windows-8),故本程序无法在此操作系统上运行,[建议升级到 Windows 8.1](https://support.microsoft.com/zh-cn/windows/%E4%BB%8E-windows-8-%E6%9B%B4%E6%96%B0%E5%88%B0-windows-8-1-17fc54a7-a465-6b5a-c1a0-34140afd0669)
|
||||
- Windows Server 2012 / 2008 R2 SP1
|
||||
- 仅可使用 1.X 版本,2.X 开始不受支持,建议升级到 **Windows Server 2012 R2** 或更高版本
|
||||
- 无桌面 GUI 的 Windows Server / Linux 版本
|
||||
---
|
||||
|
||||
## 🌏 路线图
|
||||
查看这个 [milestones](https://github.com/SteamTools-Team/SteamTools/milestones) 来了解我们下一步的开发计划,并随时提出问题。
|
||||
### 4. Steam本地两步身份验证器
|
||||
功能参考[WinAuth](https://github.com/winauth/winauth)开发,可以使您不用启动移动版Steam App也能查看您的令牌,功能类似的软件有[WinAuth](https://github.com/winauth/winauth)、[SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)。
|
||||
|
||||
## ⌨️ 开发环境
|
||||
[Visual Studio 2019 版本 16.10 或更高版本](https://visualstudio.microsoft.com/zh-hans/vs/) 或 [JetBrains Rider](https://www.jetbrains.com/rider/) 或 ~~[Visual Studio for Mac](https://visualstudio.microsoft.com/zh-hans/vs/mac/)~~ 或 ~~[Visual Studio Code](https://code.visualstudio.com/)~~
|
||||
- 系统要求
|
||||
- [Windows 10 版本 2004 或更高版本:家庭版、专业版、教育版和企业版(不支持 LTSC 和 Windows 10 S,在较早的操作系统上可能不受支持)](https://docs.microsoft.com/zh-cn/visualstudio/releases/2019/system-requirements)
|
||||
- [macOS 10.13 High Sierra 或更高版本](https://docs.microsoft.com/zh-cn/visualstudio/productinfo/vs2019-system-requirements-mac)
|
||||
- 工作负载
|
||||
- Web 和云
|
||||
- ASP.NET 和 Web 开发
|
||||
- 桌面应用和移动应用
|
||||
- .NET 桌面开发
|
||||
- 通用 Windows 平台开发
|
||||
- 使用 .NET 的移动开发
|
||||
- 其他工具集
|
||||
- .NET Core 跨平台开发
|
||||
- 单个组件
|
||||
- GitHub Extension for Visual Studio
|
||||
- Windows 10 SDK (10.0.19041.0)
|
||||
- [Visual Studio Marketplace](https://marketplace.visualstudio.com/)
|
||||
- [Avalonia for Visual Studio](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
|
||||
- [NUnit VS Templates](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
|
||||
---
|
||||
|
||||
## 🏗️ [项目结构](./src/README.md)
|
||||
### 5. 一些游戏工具
|
||||
目前已有强制游戏无边框窗口化,CSGO修复VAC屏蔽。
|
||||
这一块是随缘做一些我经常用或者闲着没事捣鼓的功能。
|
||||
|
||||
<!--👇图标如果发生更改,还需更改 Tools.OpenSourceLibraryList(Program.OpenSourceLibraryListEmoji) -->
|
||||
## 📄 感谢以下开源项目
|
||||
---
|
||||
|
||||
## 预计后续添加的功能
|
||||
|
||||
### Steam自动挂卡
|
||||
尝试用社区反代功能结合成就解锁功能来重新实现,目的是实现在软件内无需登录Steam帐户即可直接获取徽章卡片信息并开始挂卡。
|
||||
|
||||
### Steam皮肤设计器
|
||||
挖坑画大饼,可视化编辑Steam皮肤,而且如果软件能上架Steam的话打算支持创意工坊分享设计的Steam皮肤,短期内肯定做不完。
|
||||
|
||||
### 插件形式加载运行ASF
|
||||
以插件形式支持ASF在工具内运行并增强ASF在Windows Desktop环境下的使用。
|
||||
|
||||
### Steam自定义封面管理
|
||||
增强Steam自定义封面的管理以及从[SteamGridDB](https://www.steamgriddb.com/)快速匹配下载应用封面。
|
||||
|
||||
### 更多其它游戏平台功能
|
||||
|
||||
|
||||
### 更多非作弊游戏功能
|
||||
|
||||
---
|
||||
|
||||
## 运行环境
|
||||
> 程序使用C# WPF在 .NET Framework4.7.2环境下开发,如果无法运行请下载安装[.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)。
|
||||
> 缺失的程序集为我自己修改后的项目
|
||||
* [Titanium-Web-Proxy](https://github.com/rmbadmin/Titanium-Web-Proxy)
|
||||
* [MetroRadiance](https://github.com/rmbadmin/MetroRadiance)
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
> [GPL-3.0 License](LICENSE.txt)
|
||||
> 根据GPLv3许可发布的开源/免费软件。
|
||||
|
||||
---
|
||||
|
||||
## 感谢以下开源项目
|
||||
* [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
|
||||
* [MetroRadiance](https://github.com/Grabacr07/MetroRadiance)
|
||||
* [MetroTrilithon](https://github.com/Grabacr07/MetroTrilithon)
|
||||
* [Livet](https://github.com/runceel/Livet)
|
||||
* [StatefulModel](https://github.com/ugaya40/StatefulModel)
|
||||
* [StatefulModel](https://github.com/Grabacr07/MetroTrilithonhttps://github.com/ugaya40/StatefulModel)
|
||||
* [Hardcodet.NotifyIcon](https://github.com/HavenDV/Hardcodet.NotifyIcon.Wpf.NetCore)
|
||||
* [System.Reactive](https://github.com/dotnet/reactive)
|
||||
* [Titanium-Web-Proxy](https://github.com/justcoding121/Titanium-Web-Proxy)
|
||||
* [BrotliSharpLib](https://github.com/master131/BrotliSharpLib)
|
||||
* [Portable.BouncyCastle](https://github.com/novotnyllc/bc-csharp)
|
||||
* [Ninject](https://github.com/ninject/Ninject)
|
||||
* [log4net](https://github.com/apache/logging-log4net)
|
||||
* [SteamDB-API](https://github.com/SteamDB-API/api)
|
||||
* [SteamAchievementManager](https://github.com/gibbed/SteamAchievementManager)
|
||||
* [ArchiSteamFarm](https://github.com/JustArchiNET/ArchiSteamFarm)
|
||||
* [Steam4NET](https://github.com/SteamRE/Steam4NET)
|
||||
* [WinAuth](https://github.com/winauth/winauth)
|
||||
* [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator)
|
||||
* [Gameloop.Vdf](https://github.com/shravan2x/Gameloop.Vdf)
|
||||
* [DnsClient.NET](https://github.com/MichaCo/DnsClient.NET)
|
||||
* [Costura.Fody](https://github.com/Fody/Costura)
|
||||
* [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)
|
||||
* [Nito.Comparers](https://github.com/StephenCleary/Comparers)
|
||||
* [Crc32.NET](https://github.com/force-net/Crc32.NET)
|
||||
* [gfoidl.Base64](https://github.com/gfoidl/Base64)
|
||||
* [sqlite-net-pcl](https://github.com/praeclarum/sqlite-net)
|
||||
* [Polly](https://github.com/App-vNext/Polly)
|
||||
* [TaskScheduler](https://github.com/dahall/taskscheduler)
|
||||
* [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
|
||||
* [LibVLCSharp](https://github.com/videolan/libvlcsharp)
|
||||
* [Depressurizer](https://github.com/Depressurizer/Depressurizer)
|
||||
* [NLog](https://github.com/nlog/NLog)
|
||||
* [NUnit](https://github.com/nunit/nunit)
|
||||
* [ReactiveUI](https://github.com/reactiveui/reactiveui)
|
||||
* [MessageBox.Avalonia](https://github.com/AvaloniaUtils/MessageBox.Avalonia)
|
||||
* [AvaloniaUI](https://github.com/AvaloniaUI/Avalonia)
|
||||
* [AvaloniaGif](https://github.com/jmacato/AvaloniaGif)
|
||||
* [Avalonia XAML Behaviors](https://github.com/wieslawsoltes/AvaloniaBehaviors)
|
||||
* [APNG.NET](https://github.com/jz5/APNG.NET)
|
||||
* [Chromium Embedded Framework (CEF)](https://github.com/chromiumembedded/cef)
|
||||
* [Moq](https://github.com/moq/moq4)
|
||||
* [NPOI](https://github.com/nissl-lab/npoi)
|
||||
* [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)
|
||||
* [AspNet.Security.OpenId.Providers](https://github.com/aspnet-contrib/AspNet.Security.OpenId.Providers)
|
||||
* [AspNet.Security.OAuth.Providers](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers)
|
||||
* [CefNet](https://github.com/CefNet/CefNet)
|
||||
* [CefSharp](https://github.com/cefsharp/CefSharp)
|
||||
* [React](https://github.com/facebook/react)
|
||||
* [Ant Design](https://github.com/ant-design/ant-design)
|
||||
* [Ant Design Blazor](https://github.com/ant-design-blazor/ant-design-blazor)
|
||||
* [Toast messages for Xamarin.iOS](https://github.com/andrius-k/Toast)
|
||||
* [XF.Material Library](https://github.com/Baseflow/XF-Material-Library)
|
||||
* [Visual Studio App Center SDK for .NET](https://github.com/microsoft/appcenter-sdk-dotnet)
|
||||
* [AppCenter-XMac](https://github.com/nor0x/AppCenter-XMac)
|
||||
* [MSBuild.Sdk.Extras](https://github.com/novotnyllc/MSBuildSdkExtras)
|
||||
* [Xamarin.Essentials](https://github.com/xamarin/essentials)
|
||||
* [Xamarin.Forms](https://github.com/xamarin/Xamarin.Forms)
|
||||
* [Open Source Components for Xamarin](https://github.com/xamarin/XamarinComponents)
|
||||
* [AndroidX for Xamarin.Android](https://github.com/xamarin/AndroidX)
|
||||
* [MSAL.NET](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet)
|
||||
* [Entity Framework Core](https://github.com/dotnet/efcore)
|
||||
* [ASP.NET Core](https://github.com/dotnet/aspnetcore)
|
||||
* [Windows Forms](https://github.com/dotnet/winforms)
|
||||
* [Windows Presentation Foundation (WPF)](https://github.com/dotnet/wpf)
|
||||
* [C#/WinRT](https://github.com/microsoft/CsWinRT)
|
||||
* [command-line-api](https://github.com/dotnet/command-line-api)
|
||||
* [.NET Runtime](https://github.com/dotnet/runtime)
|
||||
* [Fluent UI System Icons](https://github.com/microsoft/fluentui-system-icons)
|
||||
* [Material design icons](https://github.com/google/material-design-icons)
|
||||
* [Costura.Fody](https://github.com/Fody/Costura)
|
||||
@@ -0,0 +1,880 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Colin Mackie.
|
||||
* This software is distributed under the terms of the GNU General Public License.
|
||||
*
|
||||
* 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 3 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Cache;
|
||||
using System.Security.Cryptography;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using System.Xml.XPath;
|
||||
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Paddings;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
#if NUNIT
|
||||
using NUnit.Framework;
|
||||
#endif
|
||||
|
||||
#if NETCF
|
||||
using OpenNETCF.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
namespace WinAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that implements Steam's SteamGuadrd version of the RFC6238 Authenticator
|
||||
/// </summary>
|
||||
public class SteamAuthenticator : Authenticator
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of characters in code
|
||||
/// </summary>
|
||||
private const int CODE_DIGITS = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Number of minutes to ignore syncing if network error
|
||||
/// </summary>
|
||||
private const int SYNC_ERROR_MINUTES = 60;
|
||||
|
||||
/// <summary>
|
||||
/// Number of attempts to activate
|
||||
/// </summary>
|
||||
private const int ENROLL_ACTIVATE_RETRIES = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Incorrect activation code
|
||||
/// </summary>
|
||||
private const int INVALID_ACTIVATION_CODE = 89;
|
||||
|
||||
/// <summary>
|
||||
/// Time for http request when calling Sync in ms
|
||||
/// </summary>
|
||||
private const int SYNC_TIMEOUT = 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Steam issuer for KeyUri
|
||||
/// </summary>
|
||||
private const string STEAM_ISSUER = "Steam";
|
||||
|
||||
/// <summary>
|
||||
/// URLs for all mobile services
|
||||
/// </summary>
|
||||
private static string COMMUNITY_BASE = "https://steamcommunity.com";
|
||||
private static string WEBAPI_BASE = "https://api.steampowered.com";
|
||||
private static string SYNC_URL = "https://api.steampowered.com:443/ITwoFactorService/QueryTime/v0001";
|
||||
|
||||
/// <summary>
|
||||
/// Character set for authenticator code
|
||||
/// </summary>
|
||||
private static char[] STEAMCHARS = new char[] {
|
||||
'2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C',
|
||||
'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
|
||||
'R', 'T', 'V', 'W', 'X', 'Y'};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enrolling state
|
||||
/// </summary>
|
||||
public class EnrollState
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string CaptchaId { get; set; }
|
||||
public string CaptchaUrl { get; set; }
|
||||
public string CaptchaText { get; set; }
|
||||
public string EmailDomain { get; set; }
|
||||
public string EmailAuthText { get; set; }
|
||||
public string ActivationCode { get; set; }
|
||||
public CookieContainer Cookies { get; set; }
|
||||
|
||||
public string SteamId { get; set; }
|
||||
public string OAuthToken { get; set; }
|
||||
|
||||
public bool RequiresLogin { get; set; }
|
||||
public bool RequiresCaptcha { get; set; }
|
||||
public bool Requires2FA { get; set; }
|
||||
public bool RequiresEmailAuth { get; set; }
|
||||
public bool RequiresActivation { get; set; }
|
||||
|
||||
public string RevocationCode { get; set; }
|
||||
public string SecretKey { get; set; }
|
||||
public bool Success { get; set; }
|
||||
|
||||
public string Error { get; set; }
|
||||
}
|
||||
|
||||
#region Authenticator data
|
||||
|
||||
/// <summary>
|
||||
/// Time of last Sync error
|
||||
/// </summary>
|
||||
private static DateTime _lastSyncError = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Returned serial number of authenticator
|
||||
/// </summary>
|
||||
public string Serial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Random device ID we created and registered
|
||||
/// </summary>
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// JSON steam data
|
||||
/// </summary>
|
||||
public string SteamData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// JSON session data
|
||||
/// </summary>
|
||||
public string SessionData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string PollData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current Steam client instance
|
||||
/// </summary>
|
||||
public SteamClient Client { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Expanding offsets to retry when creating first code
|
||||
/// </summary>
|
||||
private int[] ENROLL_OFFSETS = new int[] { 0, -30, 30, -60, 60, -90, 90, -120, 120};
|
||||
|
||||
/// <summary>
|
||||
/// Create a new Authenticator object
|
||||
/// </summary>
|
||||
public SteamAuthenticator()
|
||||
: base(CODE_DIGITS)
|
||||
{
|
||||
Issuer = STEAM_ISSUER;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the combined secret data value
|
||||
/// </summary>
|
||||
public override string SecretData
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.Client != null && this.Client.Session != null)
|
||||
{
|
||||
this.SessionData = this.Client.Session.ToString();
|
||||
}
|
||||
|
||||
//if (Logger != null)
|
||||
//{
|
||||
// Logger.Debug("Get Steam data: {0}, Session:{1}", (SteamData ?? string.Empty).Replace("\n"," ").Replace("\r",""), (SessionData ?? string.Empty).Replace("\n", " ").Replace("\r", ""));
|
||||
//}
|
||||
|
||||
// this is the key | serial | deviceid
|
||||
return base.SecretData
|
||||
+ "|" + Authenticator.ByteArrayToString(Encoding.UTF8.GetBytes(Serial))
|
||||
+ "|" + Authenticator.ByteArrayToString(Encoding.UTF8.GetBytes(DeviceId))
|
||||
+ "|" + Authenticator.ByteArrayToString(Encoding.UTF8.GetBytes(SteamData))
|
||||
+ "|" + (string.IsNullOrEmpty(SessionData) == false ? Authenticator.ByteArrayToString(Encoding.UTF8.GetBytes(SessionData)) : string.Empty);
|
||||
}
|
||||
set
|
||||
{
|
||||
// extract key + serial + deviceid
|
||||
if (string.IsNullOrEmpty(value) == false)
|
||||
{
|
||||
string[] parts = value.Split('|');
|
||||
base.SecretData = value;
|
||||
Serial = (parts.Length > 1 ? Encoding.UTF8.GetString(Authenticator.StringToByteArray(parts[1])) : null);
|
||||
DeviceId = (parts.Length > 2 ? Encoding.UTF8.GetString(Authenticator.StringToByteArray(parts[2])) : null);
|
||||
SteamData = (parts.Length > 3 ? Encoding.UTF8.GetString(Authenticator.StringToByteArray(parts[3])) : string.Empty);
|
||||
|
||||
if (string.IsNullOrEmpty(SteamData) == false && SteamData[0] != '{')
|
||||
{
|
||||
// convert old recovation code into SteamData json
|
||||
SteamData = "{\"revocation_code\":\"" + SteamData + "\"}";
|
||||
}
|
||||
string session = (parts.Length > 4 ? Encoding.UTF8.GetString(Authenticator.StringToByteArray(parts[4])) : null);
|
||||
|
||||
//if (Logger != null)
|
||||
//{
|
||||
// Logger.Debug("Set Steam data: {0}, Session:{1}", (SteamData ?? string.Empty).Replace("\n", " ").Replace("\r", ""), (SessionData ?? string.Empty).Replace("\n", " ").Replace("\r", ""));
|
||||
//}
|
||||
|
||||
if (string.IsNullOrEmpty(session) == false)
|
||||
{
|
||||
SessionData = session;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SecretKey = null;
|
||||
Serial = null;
|
||||
DeviceId = null;
|
||||
SteamData = null;
|
||||
SessionData = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get (or create) the current Steam client for this Authenticator
|
||||
/// </summary>
|
||||
/// <returns>current or new SteamClient</returns>
|
||||
public SteamClient GetClient()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this.Client == null)
|
||||
{
|
||||
this.Client = new SteamClient(this, this.SessionData);
|
||||
}
|
||||
|
||||
return this.Client;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a request to the Steam WebAPI service
|
||||
/// </summary>
|
||||
/// <param name="url">API url</param>
|
||||
/// <param name="method">GET or POST</param>
|
||||
/// <param name="data">Name-data pairs</param>
|
||||
/// <param name="cookies">current cookie container</param>
|
||||
/// <returns>response body</returns>
|
||||
private string Request(string url, string method, NameValueCollection data = null, CookieContainer cookies = null, NameValueCollection headers = null, int timeout = 0)
|
||||
{
|
||||
// create form-encoded data for query or body
|
||||
string query = (data == null ? string.Empty : string.Join("&", Array.ConvertAll(data.AllKeys, key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(data[key])))));
|
||||
if (string.Compare(method, "GET", true) == 0)
|
||||
{
|
||||
url += (url.IndexOf("?") == -1 ? "?" : "&") + query;
|
||||
}
|
||||
|
||||
// call the server
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = method;
|
||||
request.Accept = "text/javascript, text/html, application/xml, text/xml, */*";
|
||||
request.ServicePoint.Expect100Continue = false;
|
||||
request.UserAgent = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30";
|
||||
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
|
||||
request.Referer = COMMUNITY_BASE; // + "/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client";
|
||||
if (headers != null)
|
||||
{
|
||||
request.Headers.Add(headers);
|
||||
}
|
||||
if (cookies != null)
|
||||
{
|
||||
request.CookieContainer = cookies;
|
||||
}
|
||||
if (timeout != 0)
|
||||
{
|
||||
request.Timeout = timeout;
|
||||
}
|
||||
|
||||
if (string.Compare(method, "POST", true) == 0)
|
||||
{
|
||||
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
|
||||
request.ContentLength = query.Length;
|
||||
|
||||
StreamWriter requestStream = new StreamWriter(request.GetRequestStream());
|
||||
requestStream.Write(query);
|
||||
requestStream.Close();
|
||||
}
|
||||
|
||||
try {
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
LogRequest(method, url, cookies, data, response.StatusCode.ToString() + " " + response.StatusDescription);
|
||||
|
||||
// OK?
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new InvalidRequestException(string.Format("{0}: {1}", (int)response.StatusCode, response.StatusDescription));
|
||||
}
|
||||
|
||||
// load the response
|
||||
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
string responseData = responseStream.ReadToEnd();
|
||||
LogRequest(method, url, cookies, data, responseData);
|
||||
return responseData;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(method, url, cookies, data, ex);
|
||||
|
||||
if (ex is WebException && ((WebException)ex).Response != null && ((HttpWebResponse)((WebException)ex).Response).StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
throw new UnauthorisedRequestException(ex);
|
||||
}
|
||||
|
||||
throw new InvalidRequestException(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enroll the authenticator with the server
|
||||
/// </summary>
|
||||
public bool Enroll(EnrollState state)
|
||||
{
|
||||
// clear error
|
||||
state.Error = null;
|
||||
|
||||
try
|
||||
{
|
||||
var data = new NameValueCollection();
|
||||
var cookies = state.Cookies = state.Cookies ?? new CookieContainer();
|
||||
string response;
|
||||
|
||||
if (string.IsNullOrEmpty(state.OAuthToken) == true)
|
||||
{
|
||||
// get session
|
||||
if (cookies.Count == 0)
|
||||
{
|
||||
cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("mobileClientVersion", "3067969+%282.1.3%29"));
|
||||
cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("mobileClient", "android"));
|
||||
cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("steamid", ""));
|
||||
cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("steamLogin", ""));
|
||||
cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("Steam_Language", "english"));
|
||||
cookies.Add(new Uri(COMMUNITY_BASE + "/"), new Cookie("dob", ""));
|
||||
|
||||
NameValueCollection headers = new NameValueCollection();
|
||||
headers.Add("X-Requested-With", "com.valvesoftware.android.steam.community");
|
||||
|
||||
response = Request("https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client", "GET", null, cookies, headers);
|
||||
}
|
||||
|
||||
// Steam strips any non-ascii chars from username and password
|
||||
state.Username = Regex.Replace(state.Username, @"[^\u0000-\u007F]", string.Empty);
|
||||
state.Password = Regex.Replace(state.Password, @"[^\u0000-\u007F]", string.Empty);
|
||||
|
||||
// get the user's RSA key
|
||||
data.Add("username", state.Username);
|
||||
response = Request(COMMUNITY_BASE + "/mobilelogin/getrsakey", "POST", data, cookies);
|
||||
var rsaresponse = JObject.Parse(response);
|
||||
if (rsaresponse.SelectToken("success").Value<bool>() != true)
|
||||
{
|
||||
throw new InvalidEnrollResponseException("Cannot get steam information for user: " + state.Username);
|
||||
}
|
||||
|
||||
// encrypt password with RSA key
|
||||
RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
|
||||
byte[] encryptedPassword;
|
||||
using (var rsa = new RSACryptoServiceProvider())
|
||||
{
|
||||
var passwordBytes = Encoding.ASCII.GetBytes(state.Password);
|
||||
var p = rsa.ExportParameters(false);
|
||||
p.Exponent = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_exp").Value<string>());
|
||||
p.Modulus = Authenticator.StringToByteArray(rsaresponse.SelectToken("publickey_mod").Value<string>());
|
||||
rsa.ImportParameters(p);
|
||||
encryptedPassword = rsa.Encrypt(passwordBytes, false);
|
||||
}
|
||||
|
||||
// login request
|
||||
data = new NameValueCollection();
|
||||
data.Add("password", Convert.ToBase64String(encryptedPassword));
|
||||
data.Add("username", state.Username);
|
||||
data.Add("twofactorcode", "");
|
||||
data.Add("emailauth", (state.EmailAuthText != null ? state.EmailAuthText : string.Empty));
|
||||
data.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile");
|
||||
data.Add("captchagid", (state.CaptchaId != null ? state.CaptchaId : "-1"));
|
||||
data.Add("captcha_text", (state.CaptchaText != null ? state.CaptchaText : "enter above characters"));
|
||||
data.Add("emailsteamid", (state.EmailAuthText != null ? state.SteamId ?? string.Empty : string.Empty));
|
||||
data.Add("rsatimestamp", rsaresponse.SelectToken("timestamp").Value<string>());
|
||||
data.Add("remember_login", "false");
|
||||
data.Add("oauth_client_id", "DE45CD61");
|
||||
data.Add("oauth_scope", "read_profile write_profile read_client write_client");
|
||||
data.Add("donotache", new DateTime().ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds.ToString());
|
||||
response = Request(COMMUNITY_BASE + "/mobilelogin/dologin/", "POST", data, cookies);
|
||||
Dictionary<string, object> loginresponse = JsonConvert.DeserializeObject<Dictionary<string, object>>(response);
|
||||
|
||||
if (loginresponse.ContainsKey("emailsteamid") == true)
|
||||
{
|
||||
state.SteamId = loginresponse["emailsteamid"] as string;
|
||||
}
|
||||
|
||||
// require captcha
|
||||
if (loginresponse.ContainsKey("captcha_needed") == true && (bool)loginresponse["captcha_needed"] == true)
|
||||
{
|
||||
state.RequiresCaptcha = true;
|
||||
state.CaptchaId = (string)loginresponse["captcha_gid"];
|
||||
state.CaptchaUrl = COMMUNITY_BASE + "/public/captcha.php?gid=" + state.CaptchaId;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.RequiresCaptcha = false;
|
||||
state.CaptchaId = null;
|
||||
state.CaptchaUrl = null;
|
||||
state.CaptchaText = null;
|
||||
}
|
||||
|
||||
// require email auth
|
||||
if (loginresponse.ContainsKey("emailauth_needed") == true && (bool)loginresponse["emailauth_needed"] == true)
|
||||
{
|
||||
if (loginresponse.ContainsKey("emaildomain") == true)
|
||||
{
|
||||
var emaildomain = (string)loginresponse["emaildomain"];
|
||||
if (string.IsNullOrEmpty(emaildomain) == false)
|
||||
{
|
||||
state.EmailDomain = emaildomain;
|
||||
}
|
||||
}
|
||||
state.RequiresEmailAuth = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.EmailDomain = null;
|
||||
state.RequiresEmailAuth = false;
|
||||
}
|
||||
|
||||
// require email auth
|
||||
if (loginresponse.ContainsKey("requires_twofactor") == true && (bool)loginresponse["requires_twofactor"] == true)
|
||||
{
|
||||
state.Requires2FA = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.Requires2FA = false;
|
||||
}
|
||||
|
||||
// if we didn't login, return the result
|
||||
if (loginresponse.ContainsKey("login_complete") == false || (bool)loginresponse["login_complete"] == false || loginresponse.ContainsKey("oauth") == false)
|
||||
{
|
||||
if (loginresponse.ContainsKey("oauth") == false)
|
||||
{
|
||||
state.Error = "Invalid response from Steam (No OAuth token)";
|
||||
}
|
||||
if (loginresponse.ContainsKey("message") == true)
|
||||
{
|
||||
state.Error = (string)loginresponse["message"];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the OAuth token - is stringified json
|
||||
string oauth = (string)loginresponse["oauth"];
|
||||
var oauthjson = JObject.Parse(oauth);
|
||||
state.OAuthToken = oauthjson.SelectToken("oauth_token").Value<string>();
|
||||
if (oauthjson.SelectToken("steamid") != null)
|
||||
{
|
||||
state.SteamId = oauthjson.SelectToken("steamid").Value<string>();
|
||||
}
|
||||
}
|
||||
|
||||
// login to webapi
|
||||
data.Clear();
|
||||
data.Add("access_token", state.OAuthToken);
|
||||
response = Request(WEBAPI_BASE + "/ISteamWebUserPresenceOAuth/Logon/v0001", "POST", data);
|
||||
|
||||
var sessionid = cookies.GetCookies(new Uri(COMMUNITY_BASE + "/"))["sessionid"].Value;
|
||||
|
||||
if (state.RequiresActivation == false)
|
||||
{
|
||||
data.Clear();
|
||||
data.Add("op", "has_phone");
|
||||
data.Add("arg", "null");
|
||||
data.Add("sessionid", sessionid);
|
||||
|
||||
response = Request(COMMUNITY_BASE + "/steamguard/phoneajax", "POST", data, cookies);
|
||||
var jsonresponse = JObject.Parse(response);
|
||||
bool hasPhone = jsonresponse.SelectToken("has_phone").Value<Boolean>();
|
||||
if (hasPhone == false)
|
||||
{
|
||||
state.OAuthToken = null; // force new login
|
||||
state.RequiresLogin = true;
|
||||
state.Cookies = null;
|
||||
state.Error = "Your Steam account must have a SMS-capable phone number attached. Go into Account Details of the Steam client or Steam website and click Add a Phone Number.";
|
||||
return false;
|
||||
}
|
||||
|
||||
//response = Request(COMMUNITY_BASE + "/steamguard/phone_checksms?bForTwoFactor=1&bRevoke2fOnCancel=", "GET", null, cookies);
|
||||
|
||||
// add a new authenticator
|
||||
data.Clear();
|
||||
string deviceId = BuildRandomId();
|
||||
data.Add("access_token", state.OAuthToken);
|
||||
data.Add("steamid", state.SteamId);
|
||||
data.Add("authenticator_type", "1");
|
||||
data.Add("device_identifier", deviceId);
|
||||
data.Add("sms_phone_id", "1");
|
||||
response = Request(WEBAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", data);
|
||||
var tfaresponse = JObject.Parse(response);
|
||||
if (response.IndexOf("status") == -1 && tfaresponse.SelectToken("response.status").Value<int>() == 84)
|
||||
{
|
||||
// invalid response
|
||||
state.OAuthToken = null; // force new login
|
||||
state.RequiresLogin = true;
|
||||
state.Cookies = null;
|
||||
state.Error = "Unable to send SMS. Check your phone is registered on your Steam account.";
|
||||
return false;
|
||||
}
|
||||
if (response.IndexOf("shared_secret") == -1)
|
||||
{
|
||||
// invalid response
|
||||
state.OAuthToken = null; // force new login
|
||||
state.RequiresLogin = true;
|
||||
state.Cookies = null;
|
||||
state.Error = "Invalid response from Steam: " + response;
|
||||
return false;
|
||||
}
|
||||
|
||||
// save data into this authenticator
|
||||
var secret = tfaresponse.SelectToken("response.shared_secret").Value<string>();
|
||||
this.SecretKey = Convert.FromBase64String(secret);
|
||||
this.Serial = tfaresponse.SelectToken("response.serial_number").Value<string>();
|
||||
this.DeviceId = deviceId;
|
||||
state.RevocationCode = tfaresponse.SelectToken("response.revocation_code").Value<string>();
|
||||
|
||||
// add the steamid into the data
|
||||
var steamdata = JObject.Parse(tfaresponse.SelectToken("response").ToString());
|
||||
if (steamdata.SelectToken("steamid") == null)
|
||||
{
|
||||
steamdata.Add("steamid", state.SteamId);
|
||||
}
|
||||
if (steamdata.SelectToken("steamguard_scheme") == null)
|
||||
{
|
||||
steamdata.Add("steamguard_scheme", "2");
|
||||
}
|
||||
this.SteamData = steamdata.ToString(Newtonsoft.Json.Formatting.None);
|
||||
|
||||
// calculate server drift
|
||||
long servertime = tfaresponse.SelectToken("response.server_time").Value<long>() * 1000;
|
||||
ServerTimeDiff = servertime - CurrentTime;
|
||||
LastServerTime = DateTime.Now.Ticks;
|
||||
|
||||
state.RequiresActivation = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// finalize adding the authenticator
|
||||
data.Clear();
|
||||
data.Add("access_token", state.OAuthToken);
|
||||
data.Add("steamid", state.SteamId);
|
||||
data.Add("activation_code", state.ActivationCode);
|
||||
|
||||
// try and authorise
|
||||
var retries = 0;
|
||||
while (state.RequiresActivation == true && retries < ENROLL_ACTIVATE_RETRIES)
|
||||
{
|
||||
data.Add("authenticator_code", this.CalculateCode(false));
|
||||
data.Add("authenticator_time", this.ServerTime.ToString());
|
||||
response = Request(WEBAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", data);
|
||||
var finalizeresponse = JObject.Parse(response);
|
||||
if (response.IndexOf("status") != -1 && finalizeresponse.SelectToken("response.status").Value<int>() == INVALID_ACTIVATION_CODE)
|
||||
{
|
||||
state.Error = "Invalid activation code";
|
||||
return false;
|
||||
}
|
||||
|
||||
// reset our time
|
||||
if (response.IndexOf("server_time") != -1)
|
||||
{
|
||||
long servertime = finalizeresponse.SelectToken("response.server_time").Value<long>() * 1000;
|
||||
ServerTimeDiff = servertime - CurrentTime;
|
||||
LastServerTime = DateTime.Now.Ticks;
|
||||
}
|
||||
|
||||
// check success
|
||||
if (finalizeresponse.SelectToken("response.success").Value<bool>() == true)
|
||||
{
|
||||
if (response.IndexOf("want_more") != -1 && finalizeresponse.SelectToken("response.want_more").Value<bool>() == true)
|
||||
{
|
||||
ServerTimeDiff += ((long)this.Period * 1000L);
|
||||
retries++;
|
||||
continue;
|
||||
}
|
||||
state.RequiresActivation = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ServerTimeDiff += ((long)this.Period * 1000L);
|
||||
retries++;
|
||||
}
|
||||
if (state.RequiresActivation == true)
|
||||
{
|
||||
state.Error = "There was a problem activating. There might be an issue with the Steam servers. Please try again later.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark and successful and return key
|
||||
state.Success = true;
|
||||
state.SecretKey = Authenticator.ByteArrayToString(this.SecretKey);
|
||||
|
||||
// send confirmation email
|
||||
data.Clear();
|
||||
data.Add("access_token", state.OAuthToken);
|
||||
data.Add("steamid", state.SteamId);
|
||||
data.Add("email_type", "2");
|
||||
response = Request(WEBAPI_BASE + "/ITwoFactorService/SendEmail/v0001", "POST", data);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (UnauthorisedRequestException ex)
|
||||
{
|
||||
throw new InvalidEnrollResponseException("You are not allowed to add an authenticator. Have you enabled 'community-generated content' in Family View?", ex);
|
||||
}
|
||||
catch (InvalidRequestException ex)
|
||||
{
|
||||
throw new InvalidEnrollResponseException("Error enrolling new authenticator", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronise this authenticator's time with Steam.
|
||||
/// </summary>
|
||||
public override void Sync()
|
||||
{
|
||||
// check if data is protected
|
||||
if (this.SecretKey == null && this.EncryptedData != null)
|
||||
{
|
||||
throw new EncryptedSecretDataException();
|
||||
}
|
||||
|
||||
// don't retry for 5 minutes
|
||||
if (_lastSyncError >= DateTime.Now.AddMinutes(0 - SYNC_ERROR_MINUTES))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = Request(SYNC_URL, "POST", null, null, null, SYNC_TIMEOUT);
|
||||
var json = JObject.Parse(response);
|
||||
|
||||
// get servertime in ms
|
||||
long servertime = json.SelectToken("response.server_time").Value<long>() * 1000;
|
||||
|
||||
// get the difference between the server time and our current time
|
||||
ServerTimeDiff = servertime - CurrentTime;
|
||||
LastServerTime = DateTime.Now.Ticks;
|
||||
|
||||
// clear any sync error
|
||||
_lastSyncError = DateTime.MinValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// don't retry for a while after error
|
||||
_lastSyncError = DateTime.Now;
|
||||
throw ex;
|
||||
// set to zero to force reset
|
||||
//ServerTimeDiff = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the current code for the authenticator.
|
||||
/// </summary>
|
||||
/// <param name="resyncTime">flag to resync time</param>
|
||||
/// <returns>authenticator code</returns>
|
||||
protected override string CalculateCode(bool resyncTime = false, long interval = -1)
|
||||
{
|
||||
// sync time if required
|
||||
if (resyncTime == true || ServerTimeDiff == 0)
|
||||
{
|
||||
if (interval > 0)
|
||||
{
|
||||
ServerTimeDiff = (interval * ((long)this.Period * 1000L)) - CurrentTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
Sync();
|
||||
}
|
||||
}
|
||||
|
||||
HMac hmac = new HMac(new Sha1Digest());
|
||||
hmac.Init(new KeyParameter(SecretKey));
|
||||
|
||||
byte[] codeIntervalArray = BitConverter.GetBytes(CodeInterval);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(codeIntervalArray);
|
||||
}
|
||||
hmac.BlockUpdate(codeIntervalArray, 0, codeIntervalArray.Length);
|
||||
|
||||
byte[] mac = new byte[hmac.GetMacSize()];
|
||||
hmac.DoFinal(mac, 0);
|
||||
|
||||
// the last 4 bits of the mac say where the code starts (e.g. if last 4 bit are 1100, we start at byte 12)
|
||||
int start = mac[19] & 0x0f;
|
||||
|
||||
// extract those 4 bytes
|
||||
byte[] bytes = new byte[4];
|
||||
Array.Copy(mac, start, bytes, 0, 4);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(bytes);
|
||||
}
|
||||
uint fullcode = BitConverter.ToUInt32(bytes, 0) & 0x7fffffff;
|
||||
|
||||
// build the alphanumeric code
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (var i=0; i<CODE_DIGITS; i++)
|
||||
{
|
||||
code.Append(STEAMCHARS[fullcode % STEAMCHARS.Length]);
|
||||
fullcode /= (uint)STEAMCHARS.Length;
|
||||
}
|
||||
|
||||
return code.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a random Device ID string for Enrolling
|
||||
/// </summary>
|
||||
/// <returns>Random string</returns>
|
||||
private static string BuildRandomId()
|
||||
{
|
||||
return "android:" + Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log an exception from a Request
|
||||
/// </summary>
|
||||
/// <param name="method">Get or POST</param>
|
||||
/// <param name="url">Request URL</param>
|
||||
/// <param name="cookies">cookie container</param>
|
||||
/// <param name="request">Request data</param>
|
||||
/// <param name="ex">Thrown exception</param>
|
||||
private static void LogException(string method, string url, CookieContainer cookies, NameValueCollection request, Exception ex)
|
||||
{
|
||||
StringBuilder data = new StringBuilder();
|
||||
if (cookies != null)
|
||||
{
|
||||
foreach (Cookie cookie in cookies.GetCookies(new Uri(url)))
|
||||
{
|
||||
if (data.Length == 0)
|
||||
{
|
||||
data.Append("Cookies:");
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Append("&");
|
||||
}
|
||||
data.Append(cookie.Name + "=" + cookie.Value);
|
||||
}
|
||||
data.Append(" ");
|
||||
}
|
||||
|
||||
if (request != null)
|
||||
{
|
||||
foreach (var key in request.AllKeys)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
{
|
||||
data.Append("Req:");
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Append("&");
|
||||
}
|
||||
data.Append(key + "=" + request[key]);
|
||||
}
|
||||
data.Append(" ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a normal response
|
||||
/// </summary>
|
||||
/// <param name="method">Get or POST</param>
|
||||
/// <param name="url">Request URL</param>
|
||||
/// <param name="cookies">cookie container</param>
|
||||
/// <param name="request">Request data</param>
|
||||
/// <param name="response">response body</param>
|
||||
private static void LogRequest(string method, string url, CookieContainer cookies, NameValueCollection request, string response)
|
||||
{
|
||||
StringBuilder data = new StringBuilder();
|
||||
if (cookies != null)
|
||||
{
|
||||
foreach (Cookie cookie in cookies.GetCookies(new Uri(url)))
|
||||
{
|
||||
if (data.Length == 0)
|
||||
{
|
||||
data.Append("Cookies:");
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Append("&");
|
||||
}
|
||||
data.Append(cookie.Name + "=" + cookie.Value);
|
||||
}
|
||||
data.Append(" ");
|
||||
}
|
||||
|
||||
if (request != null)
|
||||
{
|
||||
foreach (var key in request.AllKeys)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
{
|
||||
data.Append("Req:");
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Append("&");
|
||||
}
|
||||
data.Append(key + "=" + request[key]);
|
||||
}
|
||||
data.Append(" ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Our custom exception for the internal Http Request
|
||||
/// </summary>
|
||||
class InvalidRequestException : ApplicationException
|
||||
{
|
||||
public InvalidRequestException(string msg = null, Exception ex = null) : base(msg, ex) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 403 forbidden responses
|
||||
/// </summary>
|
||||
class UnauthorisedRequestException : InvalidRequestException
|
||||
{
|
||||
public UnauthorisedRequestException(Exception ex = null) : base("Unauthorised", ex) { }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.8" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using SteamTools.Model;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SteamTools.Helper
|
||||
{
|
||||
public static class ConfigHelper
|
||||
{
|
||||
private const string ConfigFileName = "SteamToolData.dat";
|
||||
|
||||
private static readonly string ConfigPath =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ConfigFileName);
|
||||
|
||||
public static SteamToolConfig SteamToolConfig { get; set; }
|
||||
|
||||
public static void SaveConfig()
|
||||
{
|
||||
var jsonData = JsonConvert.SerializeObject(SteamToolConfig);
|
||||
File.WriteAllText(ConfigPath, jsonData, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static SteamToolConfig ReadConfig()
|
||||
{
|
||||
SteamToolConfig = JsonConvert.DeserializeObject<SteamToolConfig>(File.ReadAllText(ConfigPath, Encoding.UTF8));
|
||||
return SteamToolConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace SteamTools.Helper
|
||||
{
|
||||
public static class RegistryKeyHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建注册表项
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static RegistryKey CreateRegistryKey(RegistryKey registryKey, string path)
|
||||
{
|
||||
return registryKey.CreateSubKey(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除注册表项
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static void DeleteRegistryKey(RegistryKey registryKey, string path)
|
||||
{
|
||||
registryKey.DeleteSubKey(path, true); //该方法无返回值,直接调用即可
|
||||
registryKey.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取注册表值
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReadRegistryKey(RegistryKey registryKey, string path, string key)
|
||||
{
|
||||
var myreg = registryKey.OpenSubKey(path);
|
||||
|
||||
if (myreg != null)
|
||||
{
|
||||
var info = myreg.GetValue(key).ToString();
|
||||
myreg.Close();
|
||||
return info;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增或修改注册表值
|
||||
/// </summary>
|
||||
/// <param name="registryKey"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="registryValueKind"></param>
|
||||
/// <returns></returns>
|
||||
public static void AddOrUpdateRegistryKey(RegistryKey registryKey, string path, string key, string value, RegistryValueKind registryValueKind)
|
||||
{
|
||||
RegistryKey myreg = registryKey.OpenSubKey(path, true); //该项必须已存在
|
||||
if (myreg != null)
|
||||
{
|
||||
myreg.SetValue(key, value, registryValueKind);
|
||||
myreg.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Gameloop.Vdf.Linq;
|
||||
using Microsoft.Win32;
|
||||
using SteamTool.Common.Helper;
|
||||
using SteamTools.Model;
|
||||
|
||||
namespace SteamTools.Helper
|
||||
{
|
||||
public class SteamHelper
|
||||
{
|
||||
public static readonly SteamHelper Instance = new SteamHelper();
|
||||
|
||||
public string SteamPath { get; set; }
|
||||
|
||||
public string SteamExePath { get; set; }
|
||||
|
||||
private const string SteamRegistryPath = @"SOFTWARE\Valve\Steam";
|
||||
|
||||
public SteamHelper()
|
||||
{
|
||||
var steamExePath = RegistryKeyHelper.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamExe");
|
||||
var steamPath = RegistryKeyHelper.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamPath");
|
||||
if (File.Exists(steamExePath))
|
||||
{
|
||||
SteamExePath = steamExePath;
|
||||
}
|
||||
if (Directory.Exists(steamPath))
|
||||
{
|
||||
SteamPath = steamPath;
|
||||
}
|
||||
}
|
||||
|
||||
public void KillSteamProcess()
|
||||
{
|
||||
Process[] processes = Process.GetProcesses();
|
||||
foreach (Process process in processes)
|
||||
{
|
||||
if (process.ProcessName == "Steam" || process.ProcessName == "SteamService" || process.ProcessName == "steamwebhelper")
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StartSteam(string param = "")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SteamExePath))
|
||||
{
|
||||
Process.Start(SteamExePath, param);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最后一次自动登陆steam用户名称
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetLastLoginUserName()
|
||||
{
|
||||
return RegistryKeyHelper.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "AutoLoginUser");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有记住登陆steam用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<SteamUser> GetAllUser()
|
||||
{
|
||||
var users = new List<SteamUser>();
|
||||
if (SteamPath != null)
|
||||
{
|
||||
var v = VdfHelper.GetVdfModelByPath(SteamPath + "/config/loginusers.vdf");
|
||||
|
||||
foreach (var item in v.Value)
|
||||
{
|
||||
var i = item.Value;
|
||||
var user = new SteamUser
|
||||
{
|
||||
AccountName = i.AccountName.ToString(),
|
||||
PersonaName = i.PersonaName.ToString(),
|
||||
RememberPassword = Convert.ToBoolean(Convert.ToInt64(i.RememberPassword.ToString())),
|
||||
MostRecent = Convert.ToBoolean(Convert.ToInt64(i.MostRecent.ToString())),
|
||||
Timestamp = Convert.ToInt64(i.Timestamp.ToString())
|
||||
};
|
||||
user.LastLoginTime= new DateTime(1970, 1, 1, 8, 0, 0).AddSeconds(user.Timestamp);
|
||||
users.Add(user);
|
||||
}
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置下次登陆steam用户
|
||||
/// </summary>
|
||||
public void SetCurrentUser(string username)
|
||||
{
|
||||
RegistryKeyHelper.AddOrUpdateRegistryKey(Registry.CurrentUser, SteamRegistryPath, "AutoLoginUser", username, RegistryValueKind.String);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Gameloop.Vdf;
|
||||
using Gameloop.Vdf.Linq;
|
||||
|
||||
namespace SteamTool.Common.Helper
|
||||
{
|
||||
public static class VdfHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取VDF类型内容
|
||||
/// </summary>
|
||||
/// <param name="path">vdf文件内容</param>
|
||||
/// <returns></returns>
|
||||
public static dynamic GetVdfModel(string text)
|
||||
{
|
||||
return VdfConvert.Deserialize(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据路径获取VDF类型内容
|
||||
/// </summary>
|
||||
/// <param name="path">vdf文件路径</param>
|
||||
/// <returns></returns>
|
||||
public static dynamic GetVdfModelByPath(string path)
|
||||
{
|
||||
string text = File.ReadAllText(path,Encoding.UTF8);
|
||||
return GetVdfModel(text); ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamTools.Model
|
||||
{
|
||||
public class SteamToolConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动参数
|
||||
/// </summary>
|
||||
public string SteamRunParam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 保存用户
|
||||
/// </summary>
|
||||
public List<SteamUser> SteamUsers { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamTools.Model
|
||||
{
|
||||
public class SteamUser
|
||||
{
|
||||
|
||||
public string SteamId3 { get; set; }
|
||||
public int SteamId3_Int { get; }
|
||||
|
||||
public string SteamId32 { get; set; }
|
||||
public int SteamId32_Int { get; }
|
||||
|
||||
public long SteamId64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 个人资料链接
|
||||
/// </summary>
|
||||
public string ProfileUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public string PersonaName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
public string AccountName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否记住密码
|
||||
/// </summary>
|
||||
public bool RememberPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string PassWord { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后登录时间戳
|
||||
/// </summary>
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后登录时间
|
||||
/// </summary>
|
||||
public DateTime LastLoginTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最近登录
|
||||
/// </summary>
|
||||
public bool MostRecent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 有关程序集的一般信息由以下
|
||||
// 控制。更改这些特性值可修改
|
||||
// 与程序集关联的信息。
|
||||
[assembly: AssemblyTitle("SteamTool.Common")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SteamTool.Common")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||
//请将此类型的 ComVisible 特性设置为 true。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||
[assembly: Guid("75e148c7-5f32-4f01-9444-cedefa28748a")]
|
||||
|
||||
// 程序集的版本信息由下列四个值组成:
|
||||
//
|
||||
// 主版本
|
||||
// 次版本
|
||||
// 生成号
|
||||
// 修订号
|
||||
//
|
||||
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
//通过使用 "*",如下所示:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System
|
||||
namespace SteamTool.Common.Service
|
||||
{
|
||||
public enum WindowLongFlags
|
||||
public class SteamToolsService
|
||||
{
|
||||
GWL_STYLE = -16,
|
||||
GWL_EXSTYLE = -20,
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{75E148C7-5F32-4F01-9444-CEDEFA28748A}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SteamTool.Common</RootNamespace>
|
||||
<AssemblyName>SteamTool.Common</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '发布|AnyCPU'">
|
||||
<OutputPath>bin\发布\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Gameloop.Vdf, Version=0.6.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Gameloop.Vdf.0.6.1\lib\net45\Gameloop.Vdf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Helper\ConfigHelper.cs" />
|
||||
<Compile Include="Helper\RegistryKeyHelper.cs" />
|
||||
<Compile Include="Helper\SteamHelper.cs" />
|
||||
<Compile Include="Helper\VdfHelper.cs" />
|
||||
<Compile Include="Model\SteamToolConfig.cs" />
|
||||
<Compile Include="Model\SteamUser.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Service\SteamToolsService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Gameloop.Vdf" version="0.6.1" targetFramework="net46" />
|
||||
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net46" />
|
||||
</packages>
|
||||
@@ -0,0 +1,26 @@
|
||||
using SteamTool.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
namespace SteamTool.Proxy
|
||||
{
|
||||
public static class CertificateEx
|
||||
{
|
||||
public static string GetPublicPemCertificateString(this X509Certificate2 @this)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine(Const.HOST_TAG);
|
||||
builder.AppendLine("-----BEGIN CERTIFICATE-----");
|
||||
builder.AppendLine(
|
||||
Convert.ToBase64String(@this.RawData, Base64FormattingOptions.InsertLineBreaks));
|
||||
builder.AppendLine("-----END CERTIFICATE-----");
|
||||
builder.AppendLine(Const.HOST_TAG);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,6 @@ namespace SteamTool.Core.Common
|
||||
{
|
||||
public static class Common
|
||||
{
|
||||
/// <summary>
|
||||
/// 时间戳转DateTime(系统时区)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static DateTime ToDateTime(this long timestamp)
|
||||
{
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(timestamp), TimeZoneInfo.Local);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前时间戳
|
||||
/// </summary>
|
||||
@@ -46,16 +37,6 @@ namespace SteamTool.Core.Common
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除字符串内所有\r \n \t
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static string RemovePattern(this string inputString)
|
||||
{
|
||||
return inputString.Replace("\t", "").Replace("\r", "").Replace("\n", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取两个字符串中间的字符串
|
||||
/// </summary>
|
||||
@@ -119,7 +100,6 @@ namespace SteamTool.Core.Common
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("V2 中使用 MessagePack反序列化与序列化进行拷贝,同时仅扩展序列化接口,避免扩展大范围全局污染")]
|
||||
public static T Clone<T>(this T source)
|
||||
{
|
||||
//如果是字符串或值类型则直接返回
|
||||
@@ -15,10 +15,6 @@ namespace SteamTool.Core.Common
|
||||
/// <returns></returns>
|
||||
public static string CompressString(this string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(text);
|
||||
var memoryStream = new MemoryStream();
|
||||
using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
|
||||
@@ -44,10 +40,6 @@ namespace SteamTool.Core.Common
|
||||
/// <returns></returns>
|
||||
public static string DecompressString(this string compressedText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(compressedText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] gZipBuffer = Convert.FromBase64String(compressedText);
|
||||
using var memoryStream = new MemoryStream();
|
||||
int dataLength = BitConverter.ToInt32(gZipBuffer, 0);
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SteamTool.Core.Common
|
||||
{
|
||||
public static class EnumerableEx
|
||||
{
|
||||
public static IEnumerable<TResult> Return<TResult>(TResult value)
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
|
||||
public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
{
|
||||
var set = new HashSet<TKey>(EqualityComparer<TKey>.Default);
|
||||
|
||||
foreach (var item in source)
|
||||
{
|
||||
var key = keySelector(item);
|
||||
if (set.Add(key)) yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
{
|
||||
HashSet<TKey> seenKeys = new HashSet<TKey>();
|
||||
foreach (TSource element in source)
|
||||
{
|
||||
if (seenKeys.Add(keySelector(element)))
|
||||
{
|
||||
yield return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// コレクションを展開し、メンバーの文字列表現を指定した区切り文字で連結した文字列を返します。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">コレクションに含まれる任意の型。</typeparam>
|
||||
/// <param name="source">対象のコレクション。</param>
|
||||
/// <param name="separator">セパレーターとして使用する文字列。</param>
|
||||
/// <returns>コレクションの文字列表現を展開し、指定したセパレーターで連結した文字列。</returns>
|
||||
public static string ToString<T>(this IEnumerable<T> source, string separator)
|
||||
{
|
||||
return string.Join(separator, source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// シーケンスが null でなく、1 つ以上の要素を含んでいるかどうかを確認します。
|
||||
/// </summary>
|
||||
public static bool HasItems<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return source != null && source.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using System.Text;
|
||||
|
||||
namespace SteamTool.Core.Common
|
||||
{
|
||||
[Obsolete("", true)]
|
||||
public static class GuidRandom
|
||||
{
|
||||
public static Random GetGuidRandom(this Random random)
|
||||
@@ -13,25 +13,12 @@ namespace SteamTool.Core
|
||||
{
|
||||
public class HostsService
|
||||
{
|
||||
public readonly static string HostsPath = @$"{Environment.SystemDirectory}\drivers\etc\hosts";
|
||||
public readonly static string HostsPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.System)}\\drivers\\etc\\hosts";
|
||||
|
||||
static HostsService()
|
||||
{
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
using var fs = File.Create(HostsPath);
|
||||
fs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public OperationResult<List<string>> ReadHostsAllLines()
|
||||
{
|
||||
var result = new OperationResult<List<string>>(OperationResultType.Error, Resources.Hosts_ReadError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
var dataLines = File.ReadAllLines(HostsPath).ToList();
|
||||
@@ -61,11 +48,7 @@ namespace SteamTool.Core
|
||||
public OperationResult UpdateHosts(string ip, string domain)
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
@@ -92,7 +75,7 @@ namespace SteamTool.Core
|
||||
try
|
||||
{
|
||||
dataLines.Add($"{ip} {domain} {Const.HOST_TAG}");
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
@@ -111,11 +94,7 @@ namespace SteamTool.Core
|
||||
public OperationResult UpdateHosts(List<(string ip, string domain)> hosts)
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
|
||||
if (hosts.Count == 0)
|
||||
{
|
||||
return result;
|
||||
@@ -153,7 +132,7 @@ namespace SteamTool.Core
|
||||
}
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
result.Message = Resources.Hosts_UpdateSuccess;
|
||||
@@ -171,33 +150,29 @@ namespace SteamTool.Core
|
||||
public OperationResult RemoveHosts(string ip, string domain)
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
//避免重复写入
|
||||
var dataLines = File.ReadAllLines(HostsPath, Encoding.Default).ToList().FindAll(s =>
|
||||
{
|
||||
var temp = s.Trim().Split(' ').ToList();
|
||||
temp = temp.FindAll(s => !string.IsNullOrEmpty(s));
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
//避免重复写入
|
||||
var dataLines = File.ReadAllLines(HostsPath, Encoding.Default).ToList().FindAll(s =>
|
||||
{
|
||||
var temp = s.Trim().Split(' ').ToList();
|
||||
temp = temp.FindAll(s => !string.IsNullOrEmpty(s));
|
||||
//一行内至少要有两列数据
|
||||
if (temp.Count >= 2)
|
||||
{
|
||||
if (!temp[0].StartsWith("#"))
|
||||
{
|
||||
if (!temp[0].StartsWith("#"))
|
||||
{
|
||||
return !(temp.Contains(ip) && temp.Contains(domain));
|
||||
}
|
||||
return !(temp.Contains(ip) && temp.Contains(domain));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
@@ -216,31 +191,29 @@ namespace SteamTool.Core
|
||||
public OperationResult RemoveHostsByTag()
|
||||
{
|
||||
var result = new OperationResult(OperationResultType.Error, Resources.Hosts_WirteError);
|
||||
if (!File.Exists(HostsPath))
|
||||
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
//避免重复写入
|
||||
var dataLines = File.ReadAllLines(HostsPath, Encoding.Default).ToList().FindAll(s =>
|
||||
{
|
||||
result.Message = "hosts file was not found";
|
||||
return result;
|
||||
}
|
||||
var temp = s.Trim().Split(' ').ToList();
|
||||
temp = temp.FindAll(s => !string.IsNullOrEmpty(s));
|
||||
//一行内至少要有两列数据
|
||||
if (temp.Count >= 2)
|
||||
{
|
||||
if (!temp[0].StartsWith("#"))
|
||||
{
|
||||
return !temp.Contains(Const.HOST_TAG);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
//操作前取消只读属性
|
||||
File.SetAttributes(HostsPath, FileAttributes.Normal);
|
||||
|
||||
var dataLines = File.ReadAllLines(HostsPath, Encoding.Default).ToList().FindAll(s =>
|
||||
{
|
||||
var temp = s.Trim().Split(' ').ToList().FindAll(w => !string.IsNullOrEmpty(w));
|
||||
//有效数据一行内至少要有两列数据
|
||||
if (temp.Count >= 2)
|
||||
{
|
||||
if (!temp[0].StartsWith("#"))
|
||||
{
|
||||
return !temp.Contains(Const.HOST_TAG);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
File.WriteAllLines(HostsPath, dataLines, Encoding.UTF8);
|
||||
File.WriteAllLines(HostsPath, dataLines);
|
||||
//File.SetAttributes(HostsPath, FileAttributes.ReadOnly);
|
||||
result.ResultType = OperationResultType.Success;
|
||||
result.Message = Resources.Hosts_UpdateSuccess;
|
||||
@@ -1,6 +1,5 @@
|
||||
using Newtonsoft.Json;
|
||||
using Ninject.Modules;
|
||||
using SteamTool.Core.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
@@ -16,51 +15,27 @@ namespace SteamTool.Core
|
||||
{
|
||||
private readonly HttpClient _client = new HttpClient();
|
||||
|
||||
public string UserAgent { get; set; } = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko";
|
||||
public string UserAgent { get; set; } = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36";
|
||||
|
||||
public string Accept { get; set; } = "application/json";
|
||||
|
||||
private async Task<string> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
private async Task<Stream> SendAsyncAsStream(HttpRequestMessage request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<string> Get(string url)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.Add("User-Agent", UserAgent);
|
||||
request.Headers.Add("Accept", Accept);
|
||||
return await SendAsync(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<string> Get(string url, Dictionary<string, string> keyValuePairs)
|
||||
{
|
||||
@@ -78,7 +53,20 @@ namespace SteamTool.Core
|
||||
{
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
return await SendAsync(request);
|
||||
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<Stream> GetStream(string url, Dictionary<string, string> keyValuePairs)
|
||||
{
|
||||
@@ -97,7 +85,18 @@ namespace SteamTool.Core
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
|
||||
return await SendAsyncAsStream(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> Post(string url, string content)
|
||||
@@ -108,7 +107,18 @@ namespace SteamTool.Core
|
||||
};
|
||||
request.Headers.Add("User-Agent", UserAgent);
|
||||
request.Headers.Add("Accept", Accept);
|
||||
return await SendAsync(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<string> Post(string url, Dictionary<string, string> keyValuePairs, string content)
|
||||
{
|
||||
@@ -129,7 +139,18 @@ namespace SteamTool.Core
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
|
||||
return await SendAsync(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public async Task<Stream> PostStream(string url, Dictionary<string, string> keyValuePairs, Stream content)
|
||||
{
|
||||
@@ -150,7 +171,18 @@ namespace SteamTool.Core
|
||||
request.Headers.Add("Accept", Accept);
|
||||
}
|
||||
|
||||
return await SendAsyncAsStream(request);
|
||||
|
||||
var response = await _client.SendAsync(request).ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,11 @@ namespace SteamTool.Core
|
||||
public SteamToolCore()
|
||||
{
|
||||
Kernel.Bind<ConfigService>().ToConstant(new ConfigService());
|
||||
Kernel.Bind<RegistryKeyService>().ToConstant(new RegistryKeyService());
|
||||
Kernel.Bind<RegistryKeyService>().To<RegistryKeyService>();
|
||||
Kernel.Bind<SteamToolService>().ToSelf();
|
||||
Kernel.Bind<VdfService>().To<VdfService>();
|
||||
Kernel.Bind<HttpServices>().ToConstant(new HttpServices());
|
||||
Kernel.Bind<HostsService>().ToConstant(new HostsService());
|
||||
Kernel.Bind<HostsService>().To<HostsService>();
|
||||
}
|
||||
|
||||
public TInterface Get<TInterface>()
|
||||
@@ -22,7 +22,7 @@ namespace SteamTool.Core.Properties {
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SteamTool.Core.Properties.Resources", typeof(Resources).Assembly);
|
||||
@@ -51,7 +51,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ namespace SteamTool.Core.Properties {
|
||||
///# Additionally, comments (such as these) may be inserted on individual
|
||||
///# lines or following th [字符串的其余部分被截断]"; 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string DefaultHosts {
|
||||
internal static string DefaultHosts {
|
||||
get {
|
||||
return ResourceManager.GetString("DefaultHosts", resourceCulture);
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 写入的Hosts已经存在 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_Exists {
|
||||
internal static string Hosts_Exists {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_Exists", resourceCulture);
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 读取Hosts时发生错误 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_ReadError {
|
||||
internal static string Hosts_ReadError {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_ReadError", resourceCulture);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 读取Hosts成功 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_ReadSuccess {
|
||||
internal static string Hosts_ReadSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_ReadSuccess", resourceCulture);
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 写入Hosts成功 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_UpdateSuccess {
|
||||
internal static string Hosts_UpdateSuccess {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_UpdateSuccess", resourceCulture);
|
||||
}
|
||||
@@ -119,7 +119,7 @@ namespace SteamTool.Core.Properties {
|
||||
/// <summary>
|
||||
/// 查找类似 写入Hosts时发生错误 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Hosts_WirteError {
|
||||
internal static string Hosts_WirteError {
|
||||
get {
|
||||
return ResourceManager.GetString("Hosts_WirteError", resourceCulture);
|
||||
}
|
||||
@@ -141,24 +141,10 @@ namespace SteamTool.Core.Properties {
|
||||
/// <param name="File" value="log\\" />
|
||||
/// <param name="AppendToFile" value="true" [字符串的其余部分被截断]"; 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string Log4netXml {
|
||||
internal static string Log4netXml {
|
||||
get {
|
||||
return ResourceManager.GetString("Log4netXml", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 echo "Steam++替换升级中..."
|
||||
///taskkill /f /im "{0}"
|
||||
///xcopy /y /c /h /r /s "{1}\*.*" "{2}"
|
||||
///rmdir /s /q "{1}"
|
||||
///"{3}"
|
||||
///del %0 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ProgramUpdateCmd {
|
||||
get {
|
||||
return ResourceManager.GetString("ProgramUpdateCmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,17 +175,12 @@
|
||||
<param name="RollingStyle" value="Date" />
|
||||
<layout type="log4net.Layout.PatternLayout,log4net">
|
||||
<param name="ConversionPattern" value="%d %-5p >> %m%n" />
|
||||
<!--输出头部并换行-->
|
||||
<!--<param name="Header" value="&#13;&#10;———————————————————&#13;&#10;" />
|
||||
<param name="Footer" value="&#13;&#10;———————————————————&#13;&#10;" />-->
|
||||
</layout>
|
||||
</appender>
|
||||
</log4net>
|
||||
</configuration></value>
|
||||
</data>
|
||||
<data name="ProgramUpdateCmd" xml:space="preserve">
|
||||
<value>echo "Steam++替换升级中..."
|
||||
taskkill /f /im "{0}"
|
||||
xcopy /y /c /h /r /s "{1}\*.*" "{2}"
|
||||
rmdir /s /q "{1}"
|
||||
"{3}"
|
||||
del %0</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -12,22 +12,14 @@
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Ninject" Version="3.3.4" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.1" />
|
||||
<PackageReference Include="System.Management" Version="5.0.0" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.9.1" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SteamTool.Model\SteamTool.Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Interop.IWshRuntimeLibrary">
|
||||
<HintPath>..\SteamTools\obj\x86\Debug\net472\Interop.IWshRuntimeLibrary.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
@@ -38,7 +30,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
@@ -37,11 +37,6 @@ namespace SteamTool.Core
|
||||
|
||||
public const string UserDataDirectory = @"\userdata";
|
||||
|
||||
/*
|
||||
Windows: ~\Steam\config\loginusers.vdf
|
||||
Linux: ~/.steam/steam/config/loginusers.vdf
|
||||
Mac: ~/Library/Application Support/Steam/config/loginusers.vdf
|
||||
*/
|
||||
public const string UserVdfPath = @"\config\loginusers.vdf";
|
||||
|
||||
private const string SteamRegistryPath = @"SOFTWARE\Valve\Steam";
|
||||
@@ -50,13 +45,15 @@ namespace SteamTool.Core
|
||||
{
|
||||
var steamExePath = registryKeyService.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamExe");
|
||||
var steamPath = registryKeyService.ReadRegistryKey(Registry.CurrentUser, SteamRegistryPath, "SteamPath");
|
||||
//steamExePath = Path.GetFullPath(steamExePath);
|
||||
//steamPath = Path.GetFullPath(steamPath);
|
||||
if (File.Exists(steamExePath))
|
||||
{
|
||||
SteamExePath = Path.GetFullPath(steamExePath);
|
||||
SteamExePath = steamExePath;
|
||||
}
|
||||
if (Directory.Exists(steamPath))
|
||||
{
|
||||
SteamPath = Path.GetFullPath(steamPath);
|
||||
SteamPath = steamPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +62,7 @@ namespace SteamTool.Core
|
||||
Process[] processes = Process.GetProcesses();
|
||||
foreach (Process process in processes)
|
||||
{
|
||||
if (process.ProcessName.Equals("steam", StringComparison.OrdinalIgnoreCase) || process.ProcessName.Equals("steamservice", StringComparison.OrdinalIgnoreCase) || process.ProcessName.Equals("steamwebhelper", StringComparison.OrdinalIgnoreCase))
|
||||
if (process.ProcessName.Equals("steam", StringComparison.OrdinalIgnoreCase) || process.ProcessName.Equals("steamService", StringComparison.OrdinalIgnoreCase) || process.ProcessName.Equals("steamwebhelper", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
@@ -93,7 +90,7 @@ namespace SteamTool.Core
|
||||
/// 获取所有记住登陆steam用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<SteamUser> GetRememberUserList()
|
||||
public List<SteamUser> GetAllUser()
|
||||
{
|
||||
var users = new List<SteamUser>();
|
||||
if (File.Exists(SteamPath + UserVdfPath))
|
||||
@@ -103,30 +100,28 @@ namespace SteamTool.Core
|
||||
foreach (var item in v.Value)
|
||||
{
|
||||
var i = item.Value;
|
||||
var user = new SteamUser(item.ToString())
|
||||
var user = new SteamUser
|
||||
{
|
||||
SteamId64 = Convert.ToInt64(item.Key.ToString()),
|
||||
AccountName = i.AccountName?.ToString(),
|
||||
SteamID = i.PersonaName?.ToString(),
|
||||
PersonaName = i.PersonaName?.ToString(),
|
||||
RememberPassword = Convert.ToBoolean(Convert.ToInt64(i.RememberPassword?.ToString())),
|
||||
Timestamp = Convert.ToInt64(i.Timestamp?.ToString())
|
||||
};
|
||||
user.LastLoginTime = user.Timestamp.ToDateTime();
|
||||
|
||||
//老版本Steam数据 小写mostrecent 支持
|
||||
user.MostRecent = i.mostrecent != null ?
|
||||
Convert.ToBoolean(Convert.ToByte(i.mostrecent.ToString())) :
|
||||
Convert.ToBoolean(Convert.ToByte(i.MostRecent.ToString()));
|
||||
|
||||
user.WantsOfflineMode = i.WantsOfflineMode != null ?
|
||||
Convert.ToBoolean(Convert.ToByte(i.WantsOfflineMode.ToString())) : false;
|
||||
|
||||
// 因为警告这个东西应该都不需要所以直接默认跳过好了
|
||||
user.SkipOfflineModeWarning = true;
|
||||
//user.SkipOfflineModeWarning = i.SkipOfflineModeWarning != null ?
|
||||
// Convert.ToBoolean(Convert.ToByte(i.SkipOfflineModeWarning.ToString())) : false;
|
||||
|
||||
user.LastLoginTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(user.Timestamp), TimeZoneInfo.Local);
|
||||
if (i.mostrecent != null)//老版本Steam数据 小写mostrecent 支持
|
||||
{
|
||||
user.MostRecent = Convert.ToBoolean(Convert.ToInt64(i.mostrecent.ToString()));
|
||||
}
|
||||
else if (i.MostRecent != null)
|
||||
{
|
||||
user.MostRecent = Convert.ToBoolean(Convert.ToInt64(i.MostRecent.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
user.MostRecent = false;
|
||||
}
|
||||
users.Add(user);
|
||||
}
|
||||
}
|
||||
@@ -157,11 +152,6 @@ namespace SteamTool.Core
|
||||
}
|
||||
|
||||
string json = File.ReadAllText(filepath, Encoding.UTF8);
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var apps = JsonConvert.DeserializeObject<SteamApps>(json);
|
||||
return apps.AppList.Apps;
|
||||
}
|
||||
@@ -202,10 +192,6 @@ namespace SteamTool.Core
|
||||
using TaskDefinition td = TaskService.Instance.NewTask();
|
||||
td.RegistrationInfo.Description = Name + "System Boot Run";
|
||||
td.Settings.Priority = System.Diagnostics.ProcessPriorityClass.Normal;
|
||||
td.Settings.ExecutionTimeLimit = new TimeSpan(0);
|
||||
td.Settings.AllowHardTerminate = false;
|
||||
td.Settings.StopIfGoingOnBatteries = false;
|
||||
td.Settings.DisallowStartIfOnBatteries = false;
|
||||
td.Triggers.Add(new LogonTrigger());
|
||||
td.Actions.Add(new ExecAction(Assembly.GetCallingAssembly().Location, "-minimized", Path.GetDirectoryName(Assembly.GetCallingAssembly().Location)));
|
||||
if (IsAdministrator)
|
||||
@@ -228,10 +214,5 @@ namespace SteamTool.Core
|
||||
Directory.Delete(temp, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSteamLocalUserData(SteamUser user)
|
||||
{
|
||||
vdfService.UpdateVdfValueByReplace(SteamPath + UserVdfPath, user.OriginVdfString, user.CurrentVdfString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Gameloop.Vdf;
|
||||
using Gameloop.Vdf.Linq;
|
||||
using SteamTool.Core.Common;
|
||||
|
||||
namespace SteamTool.Core
|
||||
{
|
||||
@@ -34,14 +33,6 @@ namespace SteamTool.Core
|
||||
return GetVdfModel(text);
|
||||
}
|
||||
|
||||
public void UpdateVdfValueByReplace(string path, string oldVaule, string newValue)
|
||||
{
|
||||
string text = File.ReadAllText(path, Encoding.UTF8);
|
||||
text = text.RemovePattern().Replace(
|
||||
oldVaule.RemovePattern().Replace(" ",string.Empty), newValue);
|
||||
//Debug.WriteLine(text);
|
||||
File.WriteAllText(path, text, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public void DeleteVdfValueByKey(string path, string key)
|
||||
{
|
||||
@@ -53,13 +44,5 @@ namespace SteamTool.Core
|
||||
//Debug.WriteLine(text);
|
||||
File.WriteAllText(path, text, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public void DeleteVdfValueByReplace(string path, string oldVaule)
|
||||
{
|
||||
string text = File.ReadAllText(path, Encoding.UTF8);
|
||||
text = text.Replace(oldVaule, string.Empty);
|
||||
//Debug.WriteLine(text);
|
||||
File.WriteAllText(path, text, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,11 @@ namespace SteamTool.Model
|
||||
public class Const
|
||||
{
|
||||
public const string APP_LIST_FILE = "apps.json";
|
||||
public const string AUTHDATA_FILE = "authenticators.dat";
|
||||
public const string SETTINGS_FILE = "settings.json";
|
||||
public const string SCRIPT_DIR = "scripts";
|
||||
public const string LOG_DIR = "log";
|
||||
public const string HOST_TAG = "#Steam++";
|
||||
public const string HOST_TAG = "#S302";
|
||||
|
||||
public const string REWARDMELIST_URL = "https://gitee.com/rmbgame/steam-tools_-data/raw/master/RewardRecord.json";
|
||||
public const string UPDATEHISTORY_URL = "https://gitee.com/rmbgame/steam-tools_-data/raw/master/UpdateHistory";
|
||||
//public const string REWARDMELIST_URL = "https://raw.githubusercontent.com/rmbadmin/SteamTools/develop/Data/RewardRecord.json";
|
||||
|
||||
public const string GITHUB_LATEST_RELEASEAPI_URL = "https://api.github.com/repos/rmbadmin/SteamTools/releases/latest";
|
||||
@@ -24,7 +21,6 @@ namespace SteamTool.Model
|
||||
public const string MY_WISHLIST_URL = "https://store.steampowered.com/wishlist/profiles/76561198289531723/";
|
||||
public const string MY_MINIPROFILE_URL = "https://steamcommunity.com/miniprofile/329265995";
|
||||
|
||||
public const string STEAM_LOGIN_URL = "https://steamcommunity.com/login/home/?goto=my/profile";
|
||||
public const string STEAM_BADGES_URL = "https://steamcommunity.com/profiles/{0}/badges/";
|
||||
public const string STEAMAPP_LIST_URL = "https://api.steampowered.com/ISteamApps/GetAppList/v2";
|
||||
public const string STORE_APP_URL = "https://store.steampowered.com/app/{0}";
|
||||
@@ -35,7 +31,6 @@ namespace SteamTool.Model
|
||||
public const string STEAM_MEDIA_URL = "https://steamcdn-a.akamaihd.net/steam/apps/{0}/movie_max.webm";
|
||||
public const string STEAM_VIDEO_URL = "https://store.steampowered.com/video/watch/{0}/";
|
||||
public const string STEAM_REGISTERKEY_URL = "https://store.steampowered.com/account/registerkey?key={0}";
|
||||
public const string STEAM_FRIENDMESSAGESLOG_URL = "https://help.steampowered.com/zh-cn/accountdata/GetFriendMessagesLog";
|
||||
|
||||
public const string STEAM_INSTALL_URL = "steam://install/{0}";
|
||||
public const string STEAM_RUNGAME_URL = "steam://rungameid/{0}";
|
||||
@@ -44,13 +39,5 @@ namespace SteamTool.Model
|
||||
|
||||
public const string STEAMDB_USERINFO_URL = "https://api.steamdb.ml/v1/users/{0}";
|
||||
public const string STEAMDB_APPINFO_URL = "https://api.steamdb.ml/v1/apps/{0}";
|
||||
|
||||
//https://steamcommunity.com/profiles/76561198289531723?xml=1
|
||||
public const string STEAM_USERINFO_XML_URL = "https://steamcommunity.com/profiles/{0}?xml=1";
|
||||
|
||||
|
||||
public const string STEAMCN_USERINFO_XML_URL = "https://my.steamchina.com/profiles/76561198289531723?xml=1";
|
||||
|
||||
public const string JOINQQGROUP_URL = "https://qm.qq.com/cgi-bin/qm/qr?k=EvP8VZ-ByGjMlbLvrj4x4YJ8C_bhTisG&authKey=toKraTwFoTlJKXSuBv7A87W0qKb0vSRrozVLXW0dVcvOmEP98T/t+MZK9qFi26fb&noverify=0";
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace SteamTool.Model
|
||||
public bool IsHidden { get; set; }
|
||||
public bool IsAchieved { get; set; }
|
||||
public bool IsChecked { get; set; }
|
||||
public long UnlockTimeUnix { get; set; }
|
||||
public int UnlockTimeUnix { get; set; }
|
||||
public DateTime UnlockTime { get; set; }
|
||||
public string IconUrl => IsAchieved ? string.Format(Const.STEAMAPP_ICON_URL, AppId, IconNormal) : string.Format(Const.STEAMAPP_ICON_URL, AppId, IconLocked);
|
||||
public int Permission { get; set; }
|
||||
@@ -18,8 +18,6 @@ namespace SteamTool.Model
|
||||
|
||||
public bool IsInstalled { get; set; }
|
||||
|
||||
public string InstalledDir { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Logo { get; set; }
|
||||
@@ -1,25 +1,15 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace SteamTool.Model
|
||||
{
|
||||
[XmlRoot("profile")]
|
||||
public class SteamUser
|
||||
{
|
||||
public SteamUser() { }
|
||||
|
||||
public SteamUser(string vdfstring)
|
||||
{
|
||||
OriginVdfString = vdfstring;
|
||||
}
|
||||
|
||||
public string SteamId3 { get; set; }
|
||||
public long SteamId3_Int => ((SteamId64 >> (ushort)0) & 0xFFFFFFFF);
|
||||
|
||||
public string SteamId32 { get; set; }
|
||||
public long SteamId32_Int { get; set; }
|
||||
|
||||
[XmlElement("steamID64")]
|
||||
public long SteamId64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -30,7 +20,6 @@ namespace SteamTool.Model
|
||||
/// <summary>
|
||||
/// 在线状态
|
||||
/// </summary>
|
||||
[XmlElement("onlineState")]
|
||||
public string OnlineState { get; set; }
|
||||
|
||||
public string IPCountry { get; set; }
|
||||
@@ -40,43 +29,28 @@ namespace SteamTool.Model
|
||||
/// friendsonly
|
||||
/// public
|
||||
/// </summary>
|
||||
[XmlElement("privacyState")]
|
||||
public string PrivacyState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 头像小图标
|
||||
/// 头像链接
|
||||
/// </summary>
|
||||
[XmlElement("avatarIcon")]
|
||||
public string AvatarIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 头像中等图片链接
|
||||
/// 头像链接
|
||||
/// </summary>
|
||||
[XmlElement("avatarMedium")]
|
||||
public string AvatarMedium { get; set; } = "/Resources/Asstes/avater.jpg";
|
||||
public string AvatarMedium { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 头像大图链接
|
||||
/// 头像链接
|
||||
/// </summary>
|
||||
[XmlElement("avatarFull")]
|
||||
public string AvatarFull { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
[XmlElement("steamID")]
|
||||
public string SteamID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public string SteamNickName => string.IsNullOrEmpty(SteamID) ? PersonaName : SteamID;
|
||||
|
||||
/// <summary>
|
||||
/// 从VDF读取到的用户名
|
||||
/// </summary>
|
||||
public string PersonaName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
@@ -107,37 +81,9 @@ namespace SteamTool.Model
|
||||
/// </summary>
|
||||
public bool MostRecent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 离线模式
|
||||
/// </summary>
|
||||
public bool WantsOfflineMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 忽略离线模式警告弹窗
|
||||
/// </summary>
|
||||
public bool SkipOfflineModeWarning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 来源VDF字符串
|
||||
/// </summary>
|
||||
public string OriginVdfString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 导出VDF配置字符串
|
||||
/// </summary>
|
||||
public string CurrentVdfString =>
|
||||
"\"" + SteamId64 + "\"\n{\n" +
|
||||
"\t\t\"AccountName\"\t\t\"" + AccountName + "\"\n" +
|
||||
"\t\t\"PersonaName\"\t\t\"" + PersonaName + "\"\n" +
|
||||
"\t\t\"RememberPassword\"\t\t\"" + Convert.ToByte(RememberPassword) + "\"\n" +
|
||||
"\t\t\"MostRecent\"\t\t\"" + Convert.ToByte(MostRecent) + "\"\n" +
|
||||
"\t\t\"WantsOfflineMode\"\t\t\"" + Convert.ToByte(WantsOfflineMode) + "\"\n" +
|
||||
"\t\t\"SkipOfflineModeWarning\"\t\t\"" + Convert.ToByte(SkipOfflineModeWarning) + "\"\n" +
|
||||
"\t\t\"Timestamp\"\t\t\"" + Timestamp + "\"\n}";
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using System.Text;
|
||||
|
||||
namespace SteamTool.Model.ToolModel
|
||||
{
|
||||
[Obsolete("use https://github.com/octokit/octokit.net")]
|
||||
public class GithubReleaseModel
|
||||
{
|
||||
public string id { get; set; }
|
||||
@@ -18,14 +17,13 @@ namespace SteamTool.Model.ToolModel
|
||||
public string body { get; set; }
|
||||
}
|
||||
|
||||
[Obsolete("use https://github.com/octokit/octokit.net")]
|
||||
public class Assets
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string url { get; set; }
|
||||
public string name { get; set; }
|
||||
public string content_type { get; set; }
|
||||
public long size { get; set; }
|
||||
public string size { get; set; }
|
||||
public string download_count { get; set; }
|
||||
public string updated_at { get; set; }
|
||||
public string browser_download_url { get; set; }
|
||||
@@ -9,10 +9,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.2">
|
||||
<PackageReference Include="coverlet.collector" Version="1.3.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -14,9 +14,6 @@ using Titanium.Web.Proxy.Http;
|
||||
using Titanium.Web.Proxy.Models;
|
||||
using SteamTool.Model;
|
||||
using System.Security.Cryptography;
|
||||
using Org.BouncyCastle.Asn1;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using Org.BouncyCastle.X509;
|
||||
|
||||
namespace SteamTool.Proxy
|
||||
{
|
||||
@@ -30,19 +27,13 @@ namespace SteamTool.Proxy
|
||||
public IReadOnlyCollection<ProxyDomainModel> ProxyDomains { get; set; }
|
||||
public List<ProxyScript> Scripts { get; set; }
|
||||
public bool IsEnableScript { get; set; }
|
||||
public bool IsOnlyWorkSteamBrowser { get; set; }
|
||||
public string CertificateName { get; }
|
||||
|
||||
public HttpProxy(IReadOnlyCollection<ProxyDomainModel> proxyDomains, string certificateName)
|
||||
{
|
||||
proxyServer.ThreadPoolWorkerThread = Environment.ProcessorCount * 8;
|
||||
ProxyDomains = proxyDomains;
|
||||
CertificateName = certificateName;
|
||||
//proxyServer.CertificateManager.PfxPassword = $"{CertificateName}";
|
||||
proxyServer.ThreadPoolWorkerThread = Environment.ProcessorCount * 8;
|
||||
proxyServer.CertificateManager.PfxFilePath = Path.Combine(AppContext.BaseDirectory, $@"{CertificateName}.Certificate.pfx");
|
||||
proxyServer.CertificateManager.RootCertificateIssuerName = $"{CertificateName} Certificate Authority";
|
||||
proxyServer.CertificateManager.RootCertificateName = $"{CertificateName} Certificate";
|
||||
proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.BouncyCastleFast;
|
||||
}
|
||||
|
||||
public bool ProxyRunning => proxyServer.ProxyRunning;
|
||||
@@ -74,9 +65,19 @@ namespace SteamTool.Proxy
|
||||
*/
|
||||
#endregion
|
||||
Debug.WriteLine("OnRequest " + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
Debug.WriteLine("OnRequest HTTP " + e.HttpClient.Request.HttpVersion);
|
||||
//Logger.Info("OnRequest" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
#endif
|
||||
Debug.WriteLine("OnRequest Authority " + e.HttpClient.Request.RequestUri.Authority);
|
||||
Logger.Info("OnRequest" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
#endif
|
||||
await Dns.GetHostAddressesAsync(e.HttpClient.Request.Host).ContinueWith(s =>
|
||||
{
|
||||
//部分运营商将奇怪的域名解析到127.0.0.1 再此排除这些不支持的代理域名
|
||||
if (IPAddress.IsLoopback(s.Result.FirstOrDefault())
|
||||
&& ProxyDomains.Count(w => w.IsEnable && w.Hosts.Contains(e.HttpClient.Request.Host)) == 0)
|
||||
{
|
||||
e.Ok($"URL : {e.HttpClient.Request.RequestUri.AbsoluteUri} \r\n not support proxy");
|
||||
return;
|
||||
}
|
||||
});
|
||||
foreach (var item in ProxyDomains)
|
||||
{
|
||||
if (!item.IsEnable)
|
||||
@@ -87,34 +88,22 @@ namespace SteamTool.Proxy
|
||||
{
|
||||
if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains(host))
|
||||
{
|
||||
if (e.HttpClient.Request.RequestUri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
e.Redirect(e.HttpClient.Request.RequestUri.AbsoluteUri.Remove(0, 4).Insert(0, "https"));
|
||||
return;
|
||||
}
|
||||
if (item.IsRedirect)
|
||||
{
|
||||
e.Redirect(e.HttpClient.Request.RequestUri.AbsoluteUri.Replace(e.HttpClient.Request.RequestUri.Host, item.ToDomain));
|
||||
return;
|
||||
}
|
||||
IPAddress ip = null;
|
||||
IPAddress iP = null;
|
||||
if (!string.IsNullOrEmpty(item.ProxyIPAddres))
|
||||
{
|
||||
ip = IPAddress.Parse(item.ProxyIPAddres);
|
||||
iP = IPAddress.Parse(item.ProxyIPAddres);
|
||||
}
|
||||
else
|
||||
{
|
||||
var iPs = await Dns.GetHostAddressesAsync(item.ToDomain);
|
||||
ip = iPs.FirstOrDefault();
|
||||
//Logger.Info("Proxy IP: " + iP);
|
||||
iP = iPs.FirstOrDefault();
|
||||
}
|
||||
if (ip != null)
|
||||
if (iP != null)
|
||||
{
|
||||
e.HttpClient.UpStreamEndPoint = new IPEndPoint(IPAddress.Parse(ip.ToString()), item.Port);
|
||||
e.HttpClient.UpStreamEndPoint = new IPEndPoint(IPAddress.Parse(iP.ToString()), item.Port);
|
||||
}
|
||||
if (e.HttpClient.ConnectRequest?.ClientHelloInfo != null)
|
||||
{
|
||||
//Logger.Info("ClientHelloInfo Info: " + e.HttpClient.ConnectRequest.ClientHelloInfo);
|
||||
if (!string.IsNullOrEmpty(item.ServerName))
|
||||
{
|
||||
var sni = e.HttpClient.ConnectRequest.ClientHelloInfo.Extensions["server_name"];
|
||||
@@ -126,48 +115,21 @@ namespace SteamTool.Proxy
|
||||
e.HttpClient.ConnectRequest.ClientHelloInfo.Extensions.Remove("server_name");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Dns.GetHostAddressesAsync(e.HttpClient.Request.Host).ContinueWith(s =>
|
||||
{
|
||||
//部分运营商将奇怪的域名解析到127.0.0.1 再此排除这些不支持的代理域名
|
||||
if (IPAddress.IsLoopback(s.Result.FirstOrDefault())
|
||||
&& ProxyDomains.Count(w => w.IsEnable && w.Hosts.Contains(e.HttpClient.Request.Host)) == 0)
|
||||
{
|
||||
e.Ok($"URL : {e.HttpClient.Request.RequestUri.AbsoluteUri} \r\n not support proxy");
|
||||
return;
|
||||
}
|
||||
Logger.Info("Steam++ IsLoopback OnRequest: " + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
});
|
||||
|
||||
//没有匹配到的结果直接返回不支持,避免出现Loopback死循环内存溢出
|
||||
//e.Ok($"URL : {e.HttpClient.Request.RequestUri.AbsoluteUri} {Environment.NewLine}not support proxy");
|
||||
return;
|
||||
}
|
||||
public async Task OnResponse(object sender, SessionEventArgs e)
|
||||
{
|
||||
// read response headers
|
||||
//var responseHeaders = e.HttpClient.Response.Headers;
|
||||
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
|
||||
#if DEBUG
|
||||
Debug.WriteLine("OnResponse" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
Logger.Info("OnResponse" + e.HttpClient.Request.RequestUri.AbsoluteUri);
|
||||
#endif
|
||||
if (IsEnableScript)
|
||||
{
|
||||
if (IsOnlyWorkSteamBrowser)
|
||||
{
|
||||
var ua = e.HttpClient.Request.Headers.GetHeaders("User-Agent");
|
||||
if (ua.Any())
|
||||
{
|
||||
if (!ua.First().Value.Contains("Valve Steam"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
foreach (var script in Scripts)
|
||||
{
|
||||
if (script.Enable)
|
||||
@@ -188,12 +150,12 @@ namespace SteamTool.Proxy
|
||||
if (e.HttpClient.Request.RequestUri.AbsoluteUri.IsWildcard(host))
|
||||
{
|
||||
var doc = await e.GetResponseBodyAsString();
|
||||
if (script.Require.Length > 0 || script.Grant.Length > 0)
|
||||
if (script.Require.Length > 0)
|
||||
{
|
||||
//var headIndex = doc.LastIndexOf("</head>", StringComparison.OrdinalIgnoreCase);
|
||||
//doc = doc.Insert(headIndex, "<meta http-equiv=\"Content-Security-Policy\" content=\"default - src 'self' data: gap: https://ssl.gstatic.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *\">");
|
||||
var t = e.HttpClient.Response.Headers.GetFirstHeader("Content-Security-Policy");
|
||||
if (!string.IsNullOrEmpty(t?.Value))
|
||||
if (!string.IsNullOrEmpty(t.Value))
|
||||
{
|
||||
//var tt = t.Value.Split(';');
|
||||
//for (var i = 0; i < tt.Length; i++)
|
||||
@@ -219,18 +181,16 @@ namespace SteamTool.Proxy
|
||||
foreach (var req in script.Require)
|
||||
{
|
||||
var headIndex = doc.LastIndexOf("</head>", StringComparison.OrdinalIgnoreCase);
|
||||
//<script type="text/javascript" src=""></script>
|
||||
//var result = await httpServices.Get(req);
|
||||
var temp1 = $"<script type=\"text/javascript\" src=\"{req}\"></script>\n";
|
||||
if (headIndex > -1)
|
||||
doc = doc.Insert(headIndex, temp1);
|
||||
doc = doc.Insert(headIndex, temp1);
|
||||
}
|
||||
}
|
||||
var index = doc.LastIndexOf("</body>", StringComparison.OrdinalIgnoreCase);
|
||||
var temp = $"<script type=\"text/javascript\">{script.@Content}</script>";
|
||||
if (index > -1)
|
||||
{
|
||||
doc = doc.Insert(index, temp);
|
||||
e.SetResponseBodyString(doc);
|
||||
}
|
||||
doc = doc.Insert(index, temp);
|
||||
e.SetResponseBodyString(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,23 +200,26 @@ namespace SteamTool.Proxy
|
||||
close:;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//// 允许重写默认的证书验证逻辑
|
||||
//public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
|
||||
//{
|
||||
// // 根据证书错误,设置IsValid为真/假
|
||||
// //if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
|
||||
// e.IsValid = true;
|
||||
// return Task.CompletedTask;
|
||||
//}
|
||||
// 允许重写默认的证书验证逻辑
|
||||
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
|
||||
{
|
||||
// 根据证书错误,设置IsValid为真/假
|
||||
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
|
||||
e.IsValid = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
//// 允许在相互身份验证期间重写默认客户端证书选择逻辑
|
||||
//public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
|
||||
//{
|
||||
// // set e.clientCertificate to override
|
||||
// return Task.CompletedTask;
|
||||
//}
|
||||
// 允许在相互身份验证期间重写默认客户端证书选择逻辑
|
||||
public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
|
||||
{
|
||||
// set e.clientCertificate to override
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool SetupCertificate()
|
||||
{
|
||||
@@ -265,20 +228,20 @@ namespace SteamTool.Proxy
|
||||
//proxyServer.CertificateManager
|
||||
// .CreateServerCertificate($"{Assembly.GetCallingAssembly().GetName().Name} Certificate")
|
||||
// .ContinueWith(c => proxyServer.CertificateManager.RootCertificate = c.Result);
|
||||
proxyServer.CertificateManager.PfxFilePath = Path.Combine(AppContext.BaseDirectory, $@"{CertificateName}.Certificate.pfx");
|
||||
//proxyServer.CertificateManager.PfxPassword = $"{CertificateName}";
|
||||
proxyServer.CertificateManager.RootCertificateIssuerName = CertificateName;
|
||||
proxyServer.CertificateManager.RootCertificateName = $"{CertificateName} Certificate";
|
||||
proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.DefaultWindows;
|
||||
|
||||
// 可选地设置证书引擎
|
||||
// 在Mono之下,只有BouncyCastle将得到支持
|
||||
//proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.DefaultWindows;
|
||||
//proxyServer.CertificateManager.SaveFakeCertificates = true;
|
||||
|
||||
var result = proxyServer.CertificateManager.CreateRootCertificate(true);
|
||||
if (!result)
|
||||
{
|
||||
Logger.Error("创建证书失败");
|
||||
return false;
|
||||
}
|
||||
proxyServer.CertificateManager.RootCertificate.SaveCerCertificateFile(Path.Combine(AppContext.BaseDirectory, $@"{CertificateName}.Certificate.cer"));
|
||||
//proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
|
||||
proxyServer.CertificateManager.SaveFakeCertificates = false;
|
||||
proxyServer.CertificateManager.CreateRootCertificate(true);
|
||||
proxyServer.CertificateManager.EnsureRootCertificate();
|
||||
//proxyServer.CertificateManager.TrustRootCertificate(true);
|
||||
|
||||
return IsCertificateInstalled(proxyServer.CertificateManager.RootCertificate);
|
||||
}
|
||||
|
||||
@@ -288,19 +251,22 @@ namespace SteamTool.Proxy
|
||||
return false;
|
||||
try
|
||||
{
|
||||
//using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
|
||||
//{
|
||||
// store.Open(OpenFlags.MaxAllowed);
|
||||
// var test = store.Certificates.Find(X509FindType.FindByIssuerName, CertificateName, true);
|
||||
// foreach (var item in test)
|
||||
// {
|
||||
// store.Remove(item);
|
||||
// }
|
||||
//}
|
||||
//proxyServer.CertificateManager.ClearRootCertificate();
|
||||
proxyServer.CertificateManager.RemoveTrustedRootCertificate();
|
||||
//proxyServer.CertificateManager.RemoveTrustedRootCertificateAsAdmin();
|
||||
//proxyServer.CertificateManager.CertificateStorage.Clear();
|
||||
using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
|
||||
{
|
||||
store.Open(OpenFlags.MaxAllowed);
|
||||
//var test = store.Certificates.Find(X509FindType.FindByIssuerName, "titan", true);
|
||||
var test = store.Certificates.Find(X509FindType.FindByIssuerName, CertificateName, true);
|
||||
foreach (var item in test)
|
||||
{
|
||||
store.Remove(item);
|
||||
}
|
||||
//if (store.Certificates.Contains(proxyServer.CertificateManager.RootCertificate))
|
||||
// store.Remove(proxyServer.CertificateManager.RootCertificate);
|
||||
}
|
||||
proxyServer.CertificateManager.ClearRootCertificate();
|
||||
proxyServer.CertificateManager.RemoveTrustedRootCertificate(true);
|
||||
proxyServer.CertificateManager.RemoveTrustedRootCertificateAsAdmin(true);
|
||||
proxyServer.CertificateManager.CertificateStorage.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -335,7 +301,7 @@ namespace SteamTool.Proxy
|
||||
return inUse;
|
||||
}
|
||||
|
||||
public bool StartProxy(bool IsProxyGOG = false, bool IsWindowsProxy = false)
|
||||
public bool StartProxy(bool IsProxyGOG = false)
|
||||
{
|
||||
if (!IsCertificateInstalled(proxyServer.CertificateManager.RootCertificate))
|
||||
{
|
||||
@@ -351,63 +317,48 @@ namespace SteamTool.Proxy
|
||||
}
|
||||
if (IsProxyGOG) { WirtePemCertificateToGoGSteamPlugins(); }
|
||||
|
||||
#region 写入Hosts
|
||||
var hosts = new List<(string, string)>();
|
||||
foreach (var proxyDomain in ProxyDomains)
|
||||
{
|
||||
if (proxyDomain.IsEnable)
|
||||
{
|
||||
foreach (var host in proxyDomain.Hosts)
|
||||
{
|
||||
hosts.Add((IPAddress.Loopback.ToString(), host));
|
||||
}
|
||||
}
|
||||
}
|
||||
hostsService.UpdateHosts(hosts);
|
||||
#endregion
|
||||
|
||||
#region 启动代理
|
||||
proxyServer.BeforeRequest += OnRequest;
|
||||
proxyServer.BeforeResponse += OnResponse;
|
||||
//proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
|
||||
//proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
|
||||
|
||||
//var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8888, true)
|
||||
//var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 443, true)
|
||||
//{
|
||||
// // 在所有https请求上使用自颁发的通用证书
|
||||
// // 通过不为每个启用http的域创建证书来优化性能
|
||||
// // 当代理客户端不需要证书信任时非常有用
|
||||
// //GenericCertificate = new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "genericcert.pfx"), "password")
|
||||
//};
|
||||
var explicitProxyEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 26501, true)
|
||||
//当接收到连接请求时触发
|
||||
//explicitEndPoint.BeforeTunnelConnectRequest += OnBeforeTunnelRequest;
|
||||
//explicit endpoint 是客户端知道代理存在的地方
|
||||
//因此,客户端以代理友好的方式发送请求
|
||||
//proxyServer.AddEndPoint(explicitEndPoint);
|
||||
// 透明endpoint 对于反向代理很有用(客户端不知道代理的存在)
|
||||
// 透明endpoint 通常需要一个网络路由器端口来转发HTTP(S)包或DNS
|
||||
// 发送数据到此endpoint
|
||||
var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 443, true)
|
||||
{
|
||||
// 通过不启用为每个http的域创建证书来优化性能
|
||||
//GenericCertificate = proxyServer.CertificateManager.RootCertificate
|
||||
//GenericCertificateName= "steamcommunity-a.akamaihd.net"
|
||||
};
|
||||
if (IsWindowsProxy)
|
||||
{
|
||||
//explicit endpoint 是客户端知道代理存在的地方
|
||||
proxyServer.AddEndPoint(explicitProxyEndPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
#region 写入Hosts
|
||||
var hosts = new List<(string, string)>();
|
||||
foreach (var proxyDomain in ProxyDomains)
|
||||
{
|
||||
if (proxyDomain.IsEnable)
|
||||
{
|
||||
foreach (var host in proxyDomain.Hosts)
|
||||
{
|
||||
if (host.Contains(" "))
|
||||
{
|
||||
var h = host.Split(' ');
|
||||
hosts.Add((h[0], h[1]));
|
||||
}
|
||||
else
|
||||
hosts.Add((IPAddress.Loopback.ToString(), host));
|
||||
}
|
||||
}
|
||||
}
|
||||
hostsService.UpdateHosts(hosts);
|
||||
#endregion
|
||||
proxyServer.AddEndPoint(new TransparentProxyEndPoint(IPAddress.Any, 443, true)
|
||||
{
|
||||
// 通过不启用为每个http的域创建证书来优化性能
|
||||
//GenericCertificate = proxyServer.CertificateManager.RootCertificate
|
||||
});
|
||||
proxyServer.AddEndPoint(new TransparentProxyEndPoint(IPAddress.Any, 80, false));
|
||||
}
|
||||
|
||||
proxyServer.ExceptionFunc = ((Exception exception) =>
|
||||
{
|
||||
Logger.Error(exception);
|
||||
});
|
||||
|
||||
proxyServer.AddEndPoint(transparentEndPoint);
|
||||
try
|
||||
{
|
||||
proxyServer.Start();
|
||||
@@ -417,14 +368,10 @@ namespace SteamTool.Proxy
|
||||
Logger.Error(ex);
|
||||
return false;
|
||||
}
|
||||
if (IsWindowsProxy)
|
||||
{
|
||||
proxyServer.SetAsSystemHttpProxy(explicitProxyEndPoint);
|
||||
proxyServer.SetAsSystemHttpsProxy(explicitProxyEndPoint);
|
||||
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 26501 };
|
||||
//proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 26501 };
|
||||
}
|
||||
//proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
|
||||
//proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
|
||||
#endregion
|
||||
|
||||
#if DEBUG
|
||||
foreach (var endPoint in proxyServer.ProxyEndPoints)
|
||||
Debug.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
|
||||
@@ -440,10 +387,8 @@ namespace SteamTool.Proxy
|
||||
{
|
||||
proxyServer.BeforeRequest -= OnRequest;
|
||||
proxyServer.BeforeResponse -= OnResponse;
|
||||
//proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
|
||||
//proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
|
||||
proxyServer.DisableSystemHttpProxy();
|
||||
proxyServer.DisableSystemHttpsProxy();
|
||||
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
|
||||
proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
|
||||
proxyServer.Stop();
|
||||
hostsService.RemoveHostsByTag();
|
||||
}
|
||||
@@ -467,7 +412,7 @@ namespace SteamTool.Proxy
|
||||
var s = file.Substring(Const.HOST_TAG, Const.HOST_TAG, true);
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
File.AppendAllText(certifi, Environment.NewLine + pem);
|
||||
File.AppendAllText(certifi, "\r\n" + pem);
|
||||
}
|
||||
else if (s.Trim() != pem.Trim())
|
||||
{
|
||||
@@ -9,6 +9,8 @@ namespace SteamTool.Proxy
|
||||
{
|
||||
public class ProxyDomainModel
|
||||
{
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 显示在UI上的名称
|
||||
/// </summary>
|
||||
@@ -17,7 +19,7 @@ namespace SteamTool.Proxy
|
||||
/// <summary>
|
||||
/// 代理域名合集
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> Domains { get; set; }
|
||||
public List<string> Domains { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 端口
|
||||
@@ -44,12 +46,7 @@ namespace SteamTool.Proxy
|
||||
/// </summary>
|
||||
public bool IsEnable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启用重定向
|
||||
/// </summary>
|
||||
public bool IsRedirect { get; set; }
|
||||
|
||||
public IReadOnlyCollection<string> Hosts { get; set; }
|
||||
public IEnumerable<string> Hosts { get; set; }
|
||||
|
||||
public DomainTag DomainTag { get; set; }
|
||||
}
|
||||
@@ -57,8 +54,14 @@ namespace SteamTool.Proxy
|
||||
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
|
||||
public enum DomainTag : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// steam社区
|
||||
/// </summary>
|
||||
[Description("steam社区")]
|
||||
SteamCommunity = 0,
|
||||
/// <summary>
|
||||
/// steam商店
|
||||
/// </summary>
|
||||
[Description("steam商店")]
|
||||
SteamStore = 1,
|
||||
[Description("steam部分图片修复")]
|
||||
@@ -69,29 +72,17 @@ namespace SteamTool.Proxy
|
||||
Discord = 4,
|
||||
[Description("Twitch直播")]
|
||||
Twitch = 5,
|
||||
[Description("Origin下载(Akamai)")]
|
||||
[Description("Origin下载加速(Akamai)")]
|
||||
OriginGameDownload = 6,
|
||||
[Description("GOG.COM")]
|
||||
GOG = 7,
|
||||
[Description("Uplay更新防劫持")]
|
||||
UplayUpdate = 7,
|
||||
[Description("Google(Recaptcha)")]
|
||||
GoogleCode = 8,
|
||||
[Description("Github图片及资源修复")]
|
||||
GithubContent = 9,
|
||||
[Description("GOG.COM")]
|
||||
GOG = 8,
|
||||
[Description("Github图片修复")]
|
||||
GithubContent = 9,
|
||||
[Description("Pixiv")]
|
||||
Pixiv = 10,
|
||||
[Description("Discord更新下载")]
|
||||
DiscordNet = 11,
|
||||
[Description("Uplay更新防劫持")]
|
||||
UplayUpdate = 12,
|
||||
[Description("Twitch聊天")]
|
||||
TwitchChat = 13,
|
||||
[Description("Github Gist")]
|
||||
GithubGist = 14,
|
||||
[Description("Pixiv图片")]
|
||||
PixivImage = 15,
|
||||
[Description("steam图片上传(聊天\\截图)")]
|
||||
SteamImageUpload = 16,
|
||||
[Description("steam更新")]
|
||||
SteamMeidia = 17,
|
||||
}
|
||||
}
|
||||
@@ -69,15 +69,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Discord图片及更新下载 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string DiscordNet {
|
||||
get {
|
||||
return ResourceManager.GetString("DiscordNet", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Github图片资源修复 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -87,15 +78,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Github Gist 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string GithubGist {
|
||||
get {
|
||||
return ResourceManager.GetString("GithubGist", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 GOG Galaxy 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -141,15 +123,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Pixiv图片 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string PixivImgae {
|
||||
get {
|
||||
return ResourceManager.GetString("PixivImgae", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Steam好友聊天 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -177,24 +150,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Steam图片上传(聊天\截图) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string SteamImageUpload {
|
||||
get {
|
||||
return ResourceManager.GetString("SteamImageUpload", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Steam更新 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string SteamMeidia {
|
||||
get {
|
||||
return ResourceManager.GetString("SteamMeidia", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Steam商店 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -213,15 +168,6 @@ namespace SteamTool.Proxy.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Twitch聊天 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TwitchChat {
|
||||
get {
|
||||
return ResourceManager.GetString("TwitchChat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Uplay更新 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -120,15 +120,9 @@
|
||||
<data name="Discord" xml:space="preserve">
|
||||
<value>Discord语音</value>
|
||||
</data>
|
||||
<data name="DiscordNet" xml:space="preserve">
|
||||
<value>Discord图片及更新下载</value>
|
||||
</data>
|
||||
<data name="GithubContent" xml:space="preserve">
|
||||
<value>Github图片资源修复</value>
|
||||
</data>
|
||||
<data name="GithubGist" xml:space="preserve">
|
||||
<value>Github Gist</value>
|
||||
</data>
|
||||
<data name="GOG" xml:space="preserve">
|
||||
<value>GOG Galaxy</value>
|
||||
</data>
|
||||
@@ -144,9 +138,6 @@
|
||||
<data name="Pixiv" xml:space="preserve">
|
||||
<value>Pixiv</value>
|
||||
</data>
|
||||
<data name="PixivImgae" xml:space="preserve">
|
||||
<value>Pixiv图片</value>
|
||||
</data>
|
||||
<data name="SteamChat" xml:space="preserve">
|
||||
<value>Steam好友聊天</value>
|
||||
</data>
|
||||
@@ -156,21 +147,12 @@
|
||||
<data name="SteamImage" xml:space="preserve">
|
||||
<value>Steam图片</value>
|
||||
</data>
|
||||
<data name="SteamImageUpload" xml:space="preserve">
|
||||
<value>Steam图片上传(聊天\截图)</value>
|
||||
</data>
|
||||
<data name="SteamMeidia" xml:space="preserve">
|
||||
<value>Steam更新</value>
|
||||
</data>
|
||||
<data name="SteamStore" xml:space="preserve">
|
||||
<value>Steam商店</value>
|
||||
</data>
|
||||
<data name="Twitch" xml:space="preserve">
|
||||
<value>Twitch直播</value>
|
||||
</data>
|
||||
<data name="TwitchChat" xml:space="preserve">
|
||||
<value>Twitch聊天</value>
|
||||
</data>
|
||||
<data name="UplayUpdate" xml:space="preserve">
|
||||
<value>Uplay更新</value>
|
||||
</data>
|
||||