Compare commits

...
37 Commits
Author SHA1 Message Date
lxy829 42464db1bc 🎨 ResxXamlBinding 2023-12-12 10:29:41 +08:00
RMBGAME 9e4c4836c0 🚧 Misc 2023-11-16 20:47:38 +08:00
RMBGAME d34a5d3347 🚧 改进主页数据加载逻辑 2023-11-16 20:47:38 +08:00
RMBGAME fd119ae0e1 ✨ 账号切换数据改为启动时加载避免等待 2023-11-16 20:47:37 +08:00
Mossimo 3d59c24dec 🐛 Misc 移除调试代码。添加注释。 2023-11-16 17:16:34 +08:00
Mossimo ea315a9b44 🐛 修复版本迁移导致的脚本失效。 2023-11-16 17:06:45 +08:00
RMBGAME d5592b4bc2 🐛 fix 2023-11-15 19:48:57 +08:00
RMBGAME 2df32c67d0 🐛 fix 2023-11-15 19:28:16 +08:00
RMBGAME fdaf795202 🐛 fix 2023-11-15 14:49:09 +08:00
john.jiang a7572ecd50 🎨 asf 路径修改启动问题修复 2023-11-15 14:43:05 +08:00
RMBGAME d433d60d29 ⬆️ Submodules 2023-11-15 14:42:54 +08:00
RMBGAME 3fe8f931a2 ✨ 令牌搜索功能 2023-11-15 14:42:21 +08:00
john.jiang a69d97fcdb 🎨 ASF fixs 2023-11-14 19:09:47 +08:00
RMBGAME aa63e3ab9f 🐛 fix UserIdleInfo 2023-11-13 19:15:20 +08:00
RMBGAME a56fc36380 fix Steam令牌某些情况下闪退问题 2023-11-13 16:08:43 +08:00
john.jiang 4c5428be5f 🎨 fixs 2023-11-11 19:54:59 +08:00
john.jiang e6953e50b8 🎨 idle fixs 2023-11-11 19:53:53 +08:00
ff m a961f68242 🐛 TrimStart(Constants.UrlSchemes.Login) -> Substring(Constants.UrlSchemes.Login.Length, loginUrl.Length - Constants.UrlSchemes.Login.Length) 2023-11-11 16:43:46 +08:00
ff m 72f301d526 🐛 MacOS Substring内存溢出 改用 TrimStart 已测试通过 2023-11-11 16:07:31 +08:00
john.jiang 2b18e03098 🎨 fixs 2023-11-10 18:36:35 +08:00
john.jiang 9fffe8458a 🎨 ASF Support 2023-11-10 18:32:20 +08:00
RMBGAME ea7282f290 🚧 Misc 2023-11-10 18:13:04 +08:00
RMBGAME 3714710dd8 fix 加速列表加载缓存数据时丢失选中状态问题 2023-11-04 21:27:25 +08:00
RMBGAME 00c228abd8 🐛 fix CI 2023-11-02 17:40:25 +08:00
Mossimo 54fc1e3593 ✨ Mac 支持 UrlSchemes 协议登录 2023-10-28 19:15:46 +08:00
RMBGAME d2d04a37cf ⬆️ Bump 2023-10-24 18:59:41 +08:00
RMBGAME 0600a5ca9f 🚧 Misc 2023-10-24 18:59:41 +08:00
Mossimo 01dc528e21 🐛 修改 install.sh -> online_install.sh 避免用户理解错误 添加 init_desktop.sh 添加桌面 desktop 文件 2023-10-19 17:48:27 +08:00
ff m 9014198aeb 🐛 修复 VDF Json 读取问题 2023-10-19 12:55:44 +08:00
RMBGAME bfd6b82a95 🚧 🐛 修复账号切换某些情况下切换后仍需要登录问题 2023-10-18 19:48:06 +08:00
RMBGAME a6a45e5324 🚧 Misc 2023-10-13 19:46:08 +08:00
RMBGAME 0ed0416f69 🐛 Fix 421 Misdirected Request 2023-10-06 21:12:10 +08:00
RMBGAME ce6fd9a4d6 ✨ 账号切换 Steam 离线模式启动 2023-10-06 18:33:40 +08:00
Aigio Liu 7f06198bb6 ⬆️ DirectoryPackages 2023-09-25 14:15:25 +08:00
Aigio Liu 8202df3a65 🚧 Misc 2023-09-25 13:40:19 +08:00
Aigio Liu 5033261a64 ⬆️ DirectoryPackages 2023-09-25 13:27:41 +08:00
Aigio Liu 191c957a23 🚧 3.0.0-rc.3 2023-09-25 13:21:55 +08:00
168 changed files with 5763 additions and 2381 deletions
+23
View File
@@ -216,6 +216,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steam++", "src\BD.WTTS.Clie
EndProject
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "BD.WTTS.Client.AppHost.Bridge.Package", "src\BD.WTTS.Client.AppHost.Bridge.Package\BD.WTTS.Client.AppHost.Bridge.Package.wapproj", "{27795C79-2994-4514-8656-BA34FCEE8901}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchiSteamFarm", "ref\ArchiSteamFarm\ArchiSteamFarm\ArchiSteamFarm.csproj", "{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1260,6 +1262,26 @@ Global
{27795C79-2994-4514-8656-BA34FCEE8901}.Release|x86.ActiveCfg = Release|x86
{27795C79-2994-4514-8656-BA34FCEE8901}.Release|x86.Build.0 = Release|x86
{27795C79-2994-4514-8656-BA34FCEE8901}.Release|x86.Deploy.0 = Release|x86
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|ARM.Build.0 = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|ARM64.Build.0 = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|x64.ActiveCfg = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|x64.Build.0 = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|x86.ActiveCfg = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Debug|x86.Build.0 = Debug|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|Any CPU.Build.0 = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|ARM.ActiveCfg = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|ARM.Build.0 = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|ARM64.ActiveCfg = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|ARM64.Build.0 = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|x64.ActiveCfg = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|x64.Build.0 = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|x86.ActiveCfg = Release|Any CPU
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1326,6 +1348,7 @@ Global
{35676E23-8E54-4785-A75B-98EB0650596B} = {99D97BD4-3F51-48BC-8BD2-647F6D268BB0}
{26D8AA24-2ACE-4167-BC2B-117B9BB76AA4} = {5541B406-4A9A-4AC8-AA77-CE14E133DB80}
{27795C79-2994-4514-8656-BA34FCEE8901} = {5541B406-4A9A-4AC8-AA77-CE14E133DB80}
{8C34FCD5-8C7F-4B10-A970-7DEA8A61F56B} = {99D97BD4-3F51-48BC-8BD2-647F6D268BB0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {567F588F-E95C-4286-8B30-DB3B2200221C}
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
#添加桌面文件
run_path=$(dirname "$0")
base_path="$(dirname "$run_path")"
exec_name="Steam++"
rm -rf "$HOME/Desktop/Watt Toolkit.desktop" 2>/dev/null
echo "#!/usr/bin/env xdg-open
[Desktop Entry]
Name=Watt Toolkit
Exec=$base_path/$exec_name.sh
Icon=$base_path/Icons/Watt-Toolkit.png
Terminal=false
Type=Application
StartupNotify=false" >"$HOME/Desktop/Watt Toolkit.desktop"
chmod +x "$HOME/Desktop/Watt Toolkit.desktop"
exit 0
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Name=°²×° Watt Toolkit
Exec=curl https://steampp.net/pkg/latest/steamdeck/install.sh | bash -s --
Icon=steamdeck-gaming-return
Terminal=true
Type=Application
StartupNotify=false
+1 -1
View File
@@ -21,7 +21,7 @@ public static partial class AssemblyInfo
/// <summary>
/// RC 版本号,范围 0~9,只增不减
/// </summary>
const string ver_for_rc = "2";
const string ver_for_rc = "3";
public const string FileVersion = $"{Version2}.{ver_for_rc}0{ver_for_preview}.0";
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BD.WTTS.Converters;
public sealed class StringOmitConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string s = value.ToString();
int leng;
if (int.TryParse(parameter.ToString(), out leng))
{
if (s.Length <= leng)
return s;
else
return s[..leng] + "...";
}
else
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
+1
View File
@@ -18,6 +18,7 @@
<spp:StringEqualsConverter x:Key="StringEqualsConverter" />
<spp:EnumLocalizationNameConverter x:Key="EnumLocalizationNameConverter" />
<spp:VisbleStringConverter x:Key="VisbleStringConverter" />
<spp:StringOmitConverter x:Key="StringOmitConverter" />
</Application.Resources>
<Application.Styles>
<sty:FluentAvaloniaTheme PreferSystemTheme="False" PreferUserAccentColor="True" />
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ms="https://steampp.net/settings"
xmlns:sys="using:System"
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="https://steampp.net/services"
x:CompileBindings="True">
@@ -16,7 +17,7 @@
<!-- AutoCompleteBox -->
<Style Selector="AutoCompleteBox.Search /template/ TextBox#PART_TextBox">
<Setter Property="Watermark" Value="{ReflectionBinding Path=Res.Search, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Setter Property="Watermark" Value="{ReflectionBinding Path=Res.Search, Mode=OneWay, Source={x:Static spp:Strings.Search}}" />
<Setter Property="InnerRightContent">
<Template>
<StackPanel Orientation="Horizontal">
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
<Design.PreviewWith>
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:FluentAvalonia.UI.Controls"
x:CompileBindings="True">
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
<Design.PreviewWith>
@@ -29,7 +29,7 @@
Stretch="UniformToFill" />
<TextBlock
HorizontalAlignment="Center"
Text="{Binding Path=Res.GameList_IsLoading, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.GameList_IsLoading, Mode=OneWay, Source={x:Static spp:Strings.GameList_IsLoading}}"
Theme="{StaticResource SubtitleTextBlockStyle}" />
<TextBlock
HorizontalAlignment="Center"
@@ -51,7 +51,7 @@
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{ReflectionBinding Path=Res.CustomFilePicker_Tip,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}"
Source={x:Static spp:Strings.CustomFilePicker_Tip}}"
TextAlignment="Center"
Theme="{StaticResource BodyStrongTextBlockStyle}" />
</StackPanel>
@@ -76,4 +76,4 @@
<Style Selector="spp|CustomFilePicker:pointerover /template/ Panel#FilePickerTipBorder">
<Setter Property="IsVisible" Value="True" />
</Style>
</Styles>
</Styles>
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
<Design.PreviewWith>
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:FluentAvalonia.UI.Controls"
x:CompileBindings="True">
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
<Design.PreviewWith>
@@ -17,10 +17,7 @@
Classes="Circle"
Value="60" />
<ProgressBar Width="150" IsIndeterminate="True" />
<ProgressBar
Width="150"
Classes="notransitions"
Value="50" />
<ProgressBar Width="150" Value="50" />
<TextBlock Text="Material Progress" />
<ProgressBar
@@ -31,7 +28,6 @@
<ProgressBar
Width="150"
Height="5"
Classes="Material notransitions"
Value="50" />
</StackPanel>
</Border>
@@ -58,7 +54,7 @@
<Setter Property="Width" Value="{DynamicResource ProgressBarMinHeight}" />
</Style>
<Style Selector="ProgressBar:not(.notransitions) /template/ Border#PART_Indicator">
<Style Selector="ProgressBar[IsIndeterminate=False] /template/ Border#PART_Indicator">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition
@@ -86,4 +86,4 @@
</Transitions>
</Setter>
</Style>
</Styles>
</Styles>
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
<Design.PreviewWith>
@@ -1,5 +1,6 @@
<ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:FluentAvalonia.UI.Controls"
x:CompileBindings="True">
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
<Design.PreviewWith>
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
@@ -1,5 +1,6 @@
<ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ms="https://steampp.net/settings"
x:CompileBindings="True">
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="https://steampp.net/services"
xmlns:ui="using:FluentAvalonia.UI.Controls"
@@ -27,26 +28,26 @@
<MenuItem
x:Name="TextBoxContextMenuCutItem"
Command="{Binding $parent[TextBox].Cut}"
Header="{Binding Path=Res.Cut, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Cut, Mode=OneWay, Source={x:Static spp:Strings.Cut}}"
InputGesture="{x:Static TextBox.CutGesture}"
IsEnabled="{Binding $parent[TextBox].CanCut}" />
<MenuItem
x:Name="TextBoxContextMenuCopyItem"
Command="{Binding $parent[TextBox].Copy}"
Header="{Binding Path=Res.Copy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Copy, Mode=OneWay, Source={x:Static spp:Strings.Copy}}"
InputGesture="{x:Static TextBox.CopyGesture}"
IsEnabled="{Binding $parent[TextBox].CanCopy}" />
<MenuItem
x:Name="TextBoxContextMenuPasteItem"
Command="{Binding $parent[TextBox].Paste}"
Header="{Binding Path=Res.Paste, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Paste, Mode=OneWay, Source={x:Static spp:Strings.Paste}}"
InputGesture="{x:Static TextBox.PasteGesture}"
IsEnabled="{Binding $parent[TextBox].CanPaste}" />
<MenuItem Header="-" />
<MenuItem
x:Name="TextBoxContextMenuSelectAllItem"
Command="{Binding $parent[TextBox].SelectAll}"
Header="{Binding Path=Res.SelectAll, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.SelectAll, Mode=OneWay, Source={x:Static spp:Strings.SelectAll}}"
InputGesture="Ctrl + A"
IsEnabled="{Binding $parent[TextBox].Text, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
</MenuFlyout>
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="https://steampp.net/services"
xmlns:ui="using:FluentAvalonia.UI.Controls"
@@ -296,8 +297,8 @@
</Styles.Resources>
<Style Selector="ToggleSwitch">
<Setter Property="OffContent" Value="{ReflectionBinding Path=Res.Off, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Setter Property="OnContent" Value="{ReflectionBinding Path=Res.On, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Setter Property="OffContent" Value="{ReflectionBinding Path=Res.Off, Mode=OneWay, Source={x:Static spp:Strings.Off}}" />
<Setter Property="OnContent" Value="{ReflectionBinding Path=Res.On, Mode=OneWay, Source={x:Static spp:Strings.On}}" />
<Setter Property="MinWidth" Value="0" />
</Style>
@@ -1,5 +1,6 @@
<Styles
x:Class="BD.WTTS.UI.Styling.CustomTheme"
xmlns:spp="https://steampp.net/ui"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:CompileBindings="True">
@@ -1,5 +1,6 @@
<ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="using:System"
xmlns:ui="https://steampp.net/ui"
@@ -1,5 +1,6 @@
<ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:FluentAvalonia.UI.Controls"
x:CompileBindings="True">
@@ -1,5 +1,6 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="using:System"
xmlns:ui="using:FluentAvalonia.UI.Controls"
@@ -1,4 +1,6 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Add Resources Here -->
<LinearGradientBrush x:Key="HeaderOpacityMaskBrush" StartPoint="0,0" EndPoint="0%,100%">
<GradientStop Offset="0" Color="#FF000000" />
@@ -1,4 +1,6 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Add Resources Here -->
<LinearGradientBrush x:Key="HeaderOpacityMaskBrush" StartPoint="0,0" EndPoint="0%,100%">
<GradientStop Color="#00000000" />
@@ -1,4 +1,6 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:spp="https://steampp.net/ui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Add Resources Here -->
<LinearGradientBrush x:Key="HeaderOpacityMaskBrush" StartPoint="0,0" EndPoint="0%,100%">
<GradientStop Offset="0" Color="#FF000000" />
@@ -97,27 +97,27 @@
Content="&#xE921;"
Theme="{StaticResource FA_SystemCaptionButton}"
ToolTip.Placement="Bottom"
ToolTip.Tip="{Binding Path=Res.Minimize, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
ToolTip.Tip="{Binding Path=Res.Minimize, Mode=OneWay, Source={x:Static spp:Strings.Minimize}}" />
<Button
Name="MaxRestoreButton"
Content="&#xE922;"
Theme="{StaticResource FA_SystemCaptionButton}"
ToolTip.Placement="Bottom"
ToolTip.Tip="{Binding Path=Res.Maximize, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
ToolTip.Tip="{Binding Path=Res.Maximize, Mode=OneWay, Source={x:Static spp:Strings.Maximize}}" />
<Button
Name="CloseButton"
Classes="Close"
Content="{StaticResource Close}"
Theme="{StaticResource FA_SystemCaptionButton}"
ToolTip.Placement="Bottom"
ToolTip.Tip="{Binding Path=Res.Close, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
ToolTip.Tip="{Binding Path=Res.Close, Mode=OneWay, Source={x:Static spp:Strings.Close}}" />
</StackPanel>
</ControlTemplate>
</Setter>
<Style Selector="^:maximized /template/ Button#MaxRestoreButton">
<Setter Property="Content" Value="&#xE923;" />
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Restore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Restore, Mode=OneWay, Source={x:Static spp:Strings.Restore}}" />
</Style>
</ControlTheme>
@@ -41,7 +41,7 @@
Foreground="White"
Text="{ReflectionBinding Path=Res.Advertisement,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
Source={x:Static spp:Strings.Advertisement}}" />
<Button
Name="CloseAdBtn"
HorizontalAlignment="Right"
@@ -49,7 +49,7 @@
Background="Transparent"
Content="{ReflectionBinding Path=Res.Advertisement_Close,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}"
Source={x:Static spp:Strings.Advertisement_Close}}"
Cursor="Hand"
Foreground="White" />
</Panel>
@@ -8,8 +8,8 @@ public class OptionsDisplayItem : TemplatedControl
public static readonly StyledProperty<string> DescriptionProperty =
AvaloniaProperty.Register<OptionsDisplayItem, string>(nameof(Description));
public static readonly StyledProperty<FAIconElement> IconProperty =
AvaloniaProperty.Register<OptionsDisplayItem, FAIconElement>(nameof(Icon));
public static readonly StyledProperty<object?> IconProperty =
AvaloniaProperty.Register<OptionsDisplayItem, object?>(nameof(Icon));
public static readonly StyledProperty<bool> NavigatesProperty =
AvaloniaProperty.Register<OptionsDisplayItem, bool>(nameof(Navigates));
@@ -41,7 +41,7 @@ public class OptionsDisplayItem : TemplatedControl
set => SetValue(DescriptionProperty, value);
}
public FAIconElement Icon
public object? Icon
{
get => GetValue(IconProperty);
set => SetValue(IconProperty, value);
@@ -1,5 +1,6 @@
<UserControl
x:Class="BD.WTTS.UI.Views.Controls.SplashScreen"
xmlns:spp="https://steampp.net/ui"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -15,9 +15,7 @@
<StackPanel Spacing="{StaticResource SettingsSpacing}">
<TextBlock
Margin="0,0,0,5"
Text="{ReflectionBinding Path=Res.About,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.About, Mode=OneWay, Source={x:Static spp:Strings.About}}"
Theme="{StaticResource SubtitleTextBlockStyle}" />
<spp:OptionsDisplayItem
@@ -140,9 +138,7 @@
Margin="12,4,0,0"
VerticalAlignment="Center"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{ReflectionBinding Path=Res.Link,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Link, Mode=OneWay, Source={x:Static spp:Strings.Link}}" />
<ItemsRepeater Margin="0,0,6,6" ItemsSource="{Binding Hyperlinks, Mode=OneTime}">
<ItemsRepeater.Layout>
<UniformGridLayout MinItemWidth="166" />
@@ -165,9 +161,7 @@
Margin="12,4,0,0"
VerticalAlignment="Center"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{ReflectionBinding Path=Res.OpenSourceLibrary,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.OpenSourceLibrary, Mode=OneWay, Source={x:Static spp:Strings.OpenSourceLibrary}}" />
<ItemsRepeater Margin="0,0,6,6" ItemsSource="{Binding OSL, Mode=OneTime}">
<ItemsRepeater.Layout>
<UniformGridLayout MinItemWidth="290" />
@@ -122,7 +122,7 @@
<TextBox
Name="CommandTextBox"
Grid.Row="1"
Watermark="{Binding Path=Res.Debug_InstructionPlaceholder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Watermark="{Binding Path=Res.Debug_InstructionPlaceholder, Mode=OneWay, Source={x:Static spp:Strings.Debug_InstructionPlaceholder}}" />
</Grid>
</Border>
</spp:PageBase>
@@ -1,5 +1,6 @@
<UserControl
x:Class="BD.WTTS.UI.Views.Pages.ErrorPage"
xmlns:spp="https://steampp.net/ui"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -15,12 +16,12 @@
VerticalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Path=Res.Error_PageFault, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
Text="{Binding Path=Res.Error_PageFault, Mode=OneWay, Source={x:Static spp:Strings.Error_PageFault}}" />-->
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Path=Res.NewFeatureDevelopment, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.NewFeatureDevelopment, Mode=OneWay, Source={x:Static spp:Strings.NewFeatureDevelopment}}" />
</Panel>
</UserControl>
</UserControl>
@@ -73,7 +73,7 @@
MaxWidth="1080"
Spacing="20">
<spp:Snackbar
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static spp:Strings.Notice_Tray_Title}}"
VerticalAlignment="Top"
IsClosable="False"
IsOpen="True"
@@ -81,7 +81,7 @@
Message="当前使用的程序版本为测试版本,可能会有各种各样不可言明的 Bug,遇到问题请理性反馈,谢谢。"
Severity="Warning" />
<spp:Snackbar
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static spp:Strings.Notice_Tray_Title}}"
VerticalAlignment="Top"
IsClosable="False"
IsOpen="True"
@@ -119,7 +119,7 @@
Theme="{StaticResource SubtitleTextBlockStyle}" />
</WrapPanel>
<TextBlock VerticalAlignment="Center" Theme="{StaticResource SubtitleTextBlockStyle}">
<Run Text="{Binding Path=Res.Welcome_User_, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Run Text="{Binding Path=Res.Welcome_User_, Mode=OneWay, Source={x:Static spp:Strings.Welcome_User_}}" />
</TextBlock>
</WrapPanel>
@@ -7,7 +7,7 @@ public partial class HomePage : ReactiveUserControl<HomePageViewModel>
public HomePage()
{
InitializeComponent();
this.SetViewModel<HomePageViewModel>(false);
this.SetViewModel<HomePageViewModel>(true);
//ShopsScrollViewer.PointerWheelChanged += ShopsScrollViewer_PointerWheelChanged;
}
@@ -212,13 +212,13 @@
<TextBlock
VerticalAlignment="Center"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}}"
Text="{Binding Path=Res.User_UnLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.User_UnLogin, Mode=OneWay, Source={x:Static spp:Strings.User_UnLogin}}" />
<StackPanel VerticalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
<TextBlock Text="{Binding Path=User.NickName, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
<Border VerticalAlignment="Center" IsVisible="{Binding Path=User.UserType, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource EnumEqualValueConverter}, ConverterParameter={x:Static enum:UserType.Sponsor}}">
<TextBlock FontSize="10" Text="{Binding Path=Res.User_Sponsor, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}❤️{0}}" />
<TextBlock FontSize="10" Text="{Binding Path=Res.User_Sponsor, Mode=OneWay, Source={x:Static spp:Strings.User_Sponsor}, StringFormat={}❤️{0}}" />
</Border>
</StackPanel>
@@ -243,14 +243,14 @@
<Button
Margin="5,0"
Command="{Binding Path=SignInAsync, Mode=OneWay, Source={x:Static s:UserService.Current}}"
Content="{Binding Path=Res.Sign, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Sign, Mode=OneWay, Source={x:Static spp:Strings.Sign}}"
FontSize="10"
IsVisible="{Binding Path=!User.IsSignIn, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
<Button
Margin="5,0"
FontSize="10"
IsVisible="{Binding Path=User.IsSignIn, Mode=OneWay, Source={x:Static s:UserService.Current}}"
ToolTip.Tip="{Binding Path=Res.Signed, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
ToolTip.Tip="{Binding Path=Res.Signed, Mode=OneWay, Source={x:Static spp:Strings.Signed}}">
<Button.Content>
<MultiBinding StringFormat="{}{0} : {1}">
<CompiledBinding Path="Res.User_EngineOil" Source="{x:Static s:ResourceService.Current}" />
@@ -266,24 +266,24 @@
<MenuItem
Command="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
CommandParameter="https://steampp.net/account/center/info"
Header="{Binding Path=Res.PersonalCenter, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.PersonalCenter, Mode=OneWay, Source={x:Static spp:Strings.PersonalCenter}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<!--<MenuItem Header="{Binding Path=Res.MyOrder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />-->
<!--<MenuItem Header="{Binding Path=Res.MyOrder, Mode=OneWay, Source={x:Static spp:Strings.MyOrder}}" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />-->
<MenuItem
Command="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
CommandParameter="https://steampp.net/account/center/bind"
Header="{Binding Path=Res.BindThirdPartyAccount, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.BindThirdPartyAccount, Mode=OneWay, Source={x:Static spp:Strings.BindThirdPartyAccount}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem
Command="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
CommandParameter="https://steampp.net/account/center/safe"
Header="{Binding Path=Res.PersonalSafe, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.PersonalSafe, Mode=OneWay, Source={x:Static spp:Strings.PersonalSafe}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem
Command="{Binding Path=SignOut, Mode=OneWay, Source={x:Static s:UserService.Current}}"
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
Header="{Binding Path=Res.SignOut, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.SignOut, Mode=OneWay, Source={x:Static spp:Strings.SignOut}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<!--<MenuItem
Command="{Binding Path=ShowWindow,
@@ -330,7 +330,7 @@
</Style>
<Style Selector="Grid#PaneToggleButtonGrid">
<Setter Property="Margin" Value="5 5 0 0" />
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Back, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Back, Mode=OneWay, Source={x:Static spp:Strings.Back}}" />
<Setter Property="ToolTip.Placement" Value="Bottom" />
</Style>
</ui:NavigationView.Styles>
@@ -26,11 +26,11 @@
CornerRadius="{StaticResource ControlCornerRadius}"
ItemContainerTheme="{StaticResource TabItemStyle}"
Theme="{StaticResource TabStripStyle}">
<TabStripItem Content="{Binding Path=Res.Settings_General, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Tag="Settings_General" />
<TabStripItem Content="{Binding Path=Res.Settings_UI, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Tag="Settings_UI" />
<TabStripItem Content="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Tag="Settings_Plugin" />
<TabStripItem Content="{Binding Path=Res.Settings_Steam, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Tag="Settings_Steam" />
<TabStripItem Content="{Binding Path=Res.About, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Tag="AboutPage" />
<TabStripItem Content="{Binding Path=Res.Settings_General, Mode=OneWay, Source={x:Static spp:Strings.Settings_General}}" Tag="Settings_General" />
<TabStripItem Content="{Binding Path=Res.Settings_UI, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI}}" Tag="Settings_UI" />
<TabStripItem Content="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static spp:Strings.Settings_Plugin}}" Tag="Settings_Plugin" />
<TabStripItem Content="{Binding Path=Res.Settings_Steam, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam}}" Tag="Settings_Steam" />
<TabStripItem Content="{Binding Path=Res.About, Mode=OneWay, Source={x:Static spp:Strings.About}}" Tag="AboutPage" />
</TabStrip>
</spp:PageBase.TabContent>
<!--<spp:PageBase.ActionContent>
@@ -17,9 +17,9 @@
<StackPanel Spacing="{StaticResource SettingsSpacing}">
<TextBlock
Margin="0,0,0,10"
Text="{Binding Path=Res.Settings_General, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.Settings_General, Mode=OneWay, Source={x:Static spp:Strings.Settings_General}}"
Theme="{StaticResource SubtitleTextBlockStyle}" />
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_StartupAutoRun, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Header="{Binding Path=Res.Settings_General_StartAutoRun, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_StartupAutoRun, Mode=OneWay, Source={x:Static spp:Strings.Settings_StartupAutoRun}}" Header="{Binding Path=Res.Settings_General_StartAutoRun, Mode=OneWay, Source={x:Static spp:Strings.Settings_StartupAutoRun}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE770;" />
</spp:OptionsDisplayItem.Icon>
@@ -28,7 +28,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_StartupNoShowMainWindow, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Header="{Binding Path=Res.Settings_General_RuningMinimize, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_StartupNoShowMainWindow, Mode=OneWay, Source={x:Static spp:Strings.Settings_StartupNoShowMainWindow}}" Header="{Binding Path=Res.Settings_General_RuningMinimize, Mode=OneWay, Source={x:Static spp:Strings.Settings_StartupNoShowMainWindow}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xEA6C;" />
</spp:OptionsDisplayItem.Icon>
@@ -37,7 +37,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_General_EnableTrayIcon_Tip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Header="{Binding Path=Res.Settings_General_EnableTrayIcon, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_General_EnableTrayIcon_Tip, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_EnableTrayIcon_Tip}}" Header="{Binding Path=Res.Settings_General_EnableTrayIcon, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_EnableTrayIcon_Tip}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE9E9;" />
</spp:OptionsDisplayItem.Icon>
@@ -46,7 +46,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_General_AutoCheckUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_General_AutoCheckUpdate, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_AutoCheckUpdate}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE895;" />
</spp:OptionsDisplayItem.Icon>
@@ -55,7 +55,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_General_UpdateChannel, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{spp:OnPlatform OfficialRelease}">
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_General_UpdateChannel, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_UpdateChannel}}" IsVisible="{spp:OnPlatform OfficialRelease}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE71D;" />
</spp:OptionsDisplayItem.Icon>
@@ -75,7 +75,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_RestartEffective, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Header="{Binding Path=Res.Settings_General_UseGPURendering, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_RestartEffective, Mode=OneWay, Source={x:Static spp:Strings.Settings_RestartEffective}}" Header="{Binding Path=Res.Settings_General_UseGPURendering, Mode=OneWay, Source={x:Static spp:Strings.Settings_RestartEffective}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE950;" />
</spp:OptionsDisplayItem.Icon>
@@ -94,7 +94,7 @@
</spp:OptionsDisplayItem>
<ui:SettingsExpander
Header="{Binding Path=Res.Settings_General_StorageSpace, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_General_StorageSpace, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_StorageSpace}}"
IconSource="Globe"
IsClickEnabled="False"
IsExpanded="True">
@@ -105,23 +105,23 @@
<Button
Command="{Binding OpenFolder_Click}"
CommandParameter="{x:Null}"
Content="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter=App}" />
Content="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_BrowseCustomFolder}, Converter={StaticResource StringFormatConverter}, ConverterParameter=App}" />
</ui:SettingsExpander.Footer>
<ui:SettingsExpanderItem
ActionIconSource="Open"
Command="{Binding OpenFolder_Click}"
CommandParameter="AppData"
Content="{Binding Path=Res.Settings_General_AppData, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Settings_General_AppData, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_AppData}}"
IsClickEnabled="True"
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter=AppData}" />
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_BrowseCustomFolder}, Converter={StaticResource StringFormatConverter}, ConverterParameter=AppData}" />
<ui:SettingsExpanderItem
ActionIconSource="Open"
Command="{Binding OpenFolder_Click}"
CommandParameter="Cache"
Content="{Binding Path=Res.Settings_General_CacheData, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Settings_General_CacheData, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_CacheData}}"
IsClickEnabled="True"
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter=Cache}">
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_BrowseCustomFolder}, Converter={StaticResource StringFormatConverter}, ConverterParameter=Cache}">
<ui:SettingsExpanderItem.Footer>
<TextBlock
Name="CacheSize"
@@ -134,9 +134,9 @@
ActionIconSource="Open"
Command="{Binding OpenFolder_Click}"
CommandParameter="Logs"
Content="{Binding Path=Res.Settings_General_LogsData, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Settings_General_LogsData, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_LogsData}}"
IsClickEnabled="True"
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter=Logs}">
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static spp:Strings.Settings_General_BrowseCustomFolder}, Converter={StaticResource StringFormatConverter}, ConverterParameter=Logs}">
<ui:SettingsExpanderItem.Footer>
<TextBlock
Name="LogSize"
@@ -16,10 +16,10 @@
<StackPanel Spacing="{StaticResource SettingsSpacing}">
<TextBlock
Margin="0,0,0,10"
Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static spp:Strings.Settings_Plugin}}"
Theme="{StaticResource SubtitleTextBlockStyle}" />
<ui:SettingsExpander Description="{Binding Path=Res.Plugin_OnlyLoadOfficePlugins, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Header="{Binding Path=Res.Plugin_SafeMode, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SettingsExpander Description="{Binding Path=Res.Plugin_OnlyLoadOfficePlugins, Mode=OneWay, Source={x:Static spp:Strings.Plugin_OnlyLoadOfficePlugins}}" Header="{Binding Path=Res.Plugin_SafeMode, Mode=OneWay, Source={x:Static spp:Strings.Plugin_OnlyLoadOfficePlugins}}">
<ui:SettingsExpander.IconSource>
<ui:FontIconSource FontFamily="Segoe Fluent Icons" Glyph="{StaticResource DefenderApp}" />
</ui:SettingsExpander.IconSource>
@@ -35,14 +35,14 @@
<CompiledBinding
Mode="OneWay"
Path="Res.Plugin_InstalledPlugins"
Source="{x:Static s:ResourceService.Current}" />
Source="{x:Static spp:Strings.Plugin_InstalledPlugins}" />
<CompiledBinding
Mode="OneWay"
Path="MainWindow2.PluginCount"
Source="{x:Static s:IViewModelManager.Instance}" />
Source="{x:Static spp:Strings.IViewModelManager.Instance}" />
</MultiBinding>
</TextBlock.Text>
<!--<Run Text="{Binding Path=Res.Plugin_InstalledPlugins, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<!--<Run Text="{Binding Path=Res.Plugin_InstalledPlugins, Mode=OneWay, Source={x:Static spp:Strings.Plugin_InstalledPlugins}}" />
<Run Text="(" />
<Run Text="{Binding MainWindow2.PluginCount, Mode=OneWay, Source={x:Static s:IViewModelManager.Instance}}" />
<Run Text=")" />-->
@@ -73,14 +73,14 @@
<ui:SettingsExpanderItem
ActionIconSource="Refresh"
Command="{Binding $parent[spp:Settings_Plugin].((spp:SettingsPageViewModel)DataContext).CheckUpdate_Click}"
Content="{Binding Path=Res.CheckUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.CheckUpdate, Mode=OneWay, Source={x:Static spp:Strings.CheckUpdate}}"
IsClickEnabled="True">
<ui:SettingsExpanderItem.Description>
<MultiBinding StringFormat="{}{0}{1}">
<CompiledBinding
Mode="OneWay"
Path="Res.Plugin_Version"
Source="{x:Static s:ResourceService.Current}" />
Source="{x:Static spp:Strings.Plugin_Version}" />
<CompiledBinding Mode="OneWay" Path="Data.Version" />
</MultiBinding>
</ui:SettingsExpanderItem.Description>
@@ -89,19 +89,19 @@
ActionIconSource="Open"
Command="{Binding $parent[spp:Settings_Plugin].((spp:SettingsPageViewModel)DataContext).OpenPluginDirectory_Click}"
CommandParameter="{Binding Data.AssemblyLocation}"
Content="{Binding Path=Res.Plugin_OpenFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Plugin_OpenFolder, Mode=OneWay, Source={x:Static spp:Strings.Plugin_OpenFolder}}"
IsClickEnabled="True" />
<ui:SettingsExpanderItem
ActionIconSource="Open"
Command="{Binding $parent[spp:Settings_Plugin].((spp:SettingsPageViewModel)DataContext).OpenPluginCacheDirectory_Click}"
CommandParameter="{Binding Data.CacheDirectory}"
Content="{Binding Path=Res.Plugin_OpenCacheFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Plugin_OpenCacheFolder, Mode=OneWay, Source={x:Static spp:Strings.Plugin_OpenCacheFolder}}"
IsClickEnabled="True" />
<ui:SettingsExpanderItem
ActionIconSource="Delete"
Command="{Binding $parent[spp:Settings_Plugin].((spp:SettingsPageViewModel)DataContext).DeletePlugin_Click}"
CommandParameter="{Binding Data}"
Content="{Binding Path=Res.Plugin_Delete, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Plugin_Delete, Mode=OneWay, Source={x:Static spp:Strings.Plugin_Delete}}"
IsClickEnabled="True" />
</ui:SettingsExpander>
</DataTemplate>
@@ -111,17 +111,17 @@
<!--<ui:SettingsExpander
Description="{Binding Path=Res.Plugin_ManageAllInstalledPlugins,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}"
Source={x:Static spp:Strings.Plugin_ManageAllInstalledPlugins}}"
Header="{Binding Path=Res.Plugin_InstalledPlugins,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}"
Source={x:Static spp:Strings.Plugin_InstalledPlugins}}"
IsExpanded="True"
ItemsSource="{Binding Plugins}">
<ui:SettingsExpander.IconSource>
<ui:SymbolIconSource Symbol="Calculator" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<Button Content="{Binding Path=Res.Plugin_OpenFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Content="{Binding Path=Res.Plugin_OpenFolder, Mode=OneWay, Source={x:Static spp:Strings.Plugin_OpenFolder}}" />
</ui:SettingsExpander.Footer>
<ui:SettingsExpander.ItemTemplate>
<DataTemplate>
@@ -148,21 +148,21 @@
Theme="{StaticResource TransparentButton}">
<DropDownButton.Flyout>
<MenuFlyout>
<MenuItem Command="{Binding Path=DataContext.CheckUpdate_Click, RelativeSource={RelativeSource AncestorType=spp:Settings_Plugin}}" Header="{Binding Path=Res.CheckUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<MenuItem Command="{Binding Path=DataContext.CheckUpdate_Click, RelativeSource={RelativeSource AncestorType=spp:Settings_Plugin}}" Header="{Binding Path=Res.CheckUpdate, Mode=OneWay, Source={x:Static spp:Strings.CheckUpdate}}" />
<MenuItem
Command="{Binding Path=DataContext.OpenPluginDirectory_Click,
RelativeSource={RelativeSource AncestorType=spp:Settings_Plugin}}"
CommandParameter="{Binding Data.AssemblyLocation}"
Header="{Binding Path=Res.Plugin_OpenFolder,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
Source={x:Static spp:Strings.Plugin_OpenFolder}}" />
<MenuItem
Command="{Binding Path=DataContext.OpenPluginCacheDirectory_Click,
RelativeSource={RelativeSource AncestorType=spp:Settings_Plugin}}"
CommandParameter="{Binding Data.CacheDirectory}"
Header="{Binding Path=Res.Plugin_OpenCacheFolder,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
Source={x:Static spp:Strings.Plugin_OpenCacheFolder}}" />
<MenuItem Header="-" />
<MenuItem
Command="{Binding Path=DataContext.DeletePlugin_Click,
@@ -170,7 +170,7 @@
CommandParameter="{Binding Data}"
Header="{Binding Path=Res.Plugin_Delete,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
Source={x:Static spp:Strings.Plugin_Delete}}" />
</MenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
@@ -16,18 +16,19 @@
<StackPanel Spacing="{StaticResource SettingsSpacing}">
<TextBlock
Margin="0,0,0,10"
Text="{Binding Path=Res.Settings_Steam, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.Settings_Steam, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam}}"
Theme="{StaticResource SubtitleTextBlockStyle}" />
<spp:OptionsDisplayItem Description="{Binding Source={x:Static ms:SteamSettings.SteamProgramPath}, Path=Value, Mode=OneWay}" Header="{Binding Path=Res.Settings_Steam_Path, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Source={x:Static ms:SteamSettings.SteamProgramPath}, Path=Value, Mode=OneWay}"
Header="{Binding Path=Res.Settings_Steam_Path, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_Path}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE756;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<Button Command="{Binding SelectSteamProgramLocation}" Content="{Binding Path=Res.LocalAuth_ExportAuth_SelectPath, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{Binding SelectSteamProgramLocation}" Content="{Binding Path=Res.LocalAuth_ExportAuth_SelectPath, Mode=OneWay, Source={x:Static spp:Strings.LocalAuth_ExportAuth_SelectPath}}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_Steam_AutoRunSteam, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_Steam_AutoRunSteam, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_AutoRunSteam}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE770;" />
</spp:OptionsDisplayItem.Icon>
@@ -37,8 +38,8 @@
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem
Description="{Binding Path=Res.Settings_Steam_AdminRunPrompt, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_Steam_RuningAdmin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Description="{Binding Path=Res.Settings_Steam_AdminRunPrompt, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_AdminRunPrompt}}"
Header="{Binding Path=Res.Settings_Steam_RuningAdmin, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_RuningAdmin}}"
IsVisible="{spp:OnPlatform Windows}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE7EF;" />
@@ -48,7 +49,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_Steam_RuningMinimize, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_Steam_RuningMinimize, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_RuningMinimize}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE9E9;" />
</spp:OptionsDisplayItem.Icon>
@@ -57,7 +58,8 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_Steam_NoAutoUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Header="{Binding Path=Res.Settings_Steam_NotCheckUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_Steam_NoAutoUpdate, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_NoAutoUpdate}}"
Header="{Binding Path=Res.Settings_Steam_NotCheckUpdate, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_NotCheckUpdate}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE895;" />
</spp:OptionsDisplayItem.Icon>
@@ -66,7 +68,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_Steam_RunSteamChina, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_Steam_RunSteamChina, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_RunSteamChina}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE9E9;" />
</spp:OptionsDisplayItem.Icon>
@@ -75,7 +77,8 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_Steam_AutoPopupMessage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Header="{Binding Path=Res.Settings_Steam_CheckSteamRuningNotification, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Path=Res.Settings_Steam_AutoPopupMessage, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_AutoPopupMessage}}"
Header="{Binding Path=Res.Settings_Steam_CheckSteamRuningNotification, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_CheckSteamRuningNotification}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE7E7;" />
</spp:OptionsDisplayItem.Icon>
@@ -84,12 +87,13 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Description="{Binding Source={x:Static ms:SteamSettings.SteamStratParameter}, Path=Value, Mode=OneWay}" Header="{Binding Path=Res.Settings_Steam_SteamStratParameter, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:OptionsDisplayItem Description="{Binding Source={x:Static ms:SteamSettings.SteamStratParameter}, Path=Value, Mode=OneWay}"
Header="{Binding Path=Res.Settings_Steam_SteamStratParameter, Mode=OneWay, Source={x:Static spp:Strings.Settings_Steam_SteamStratParameter}}">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE756;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<Button Command="{Binding EditSteamParameter}" Content="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{Binding EditSteamParameter}" Content="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static spp:Strings.Edit}}" />
</spp:OptionsDisplayItem.ActionButton>
<!--<spp:OptionsDisplayItem.Content>
<TextBox Classes="clearButton"
@@ -19,13 +19,13 @@
<StackPanel Spacing="{StaticResource SettingsSpacing}">
<TextBlock
Margin="0,0,0,10"
Text="{Binding Path=Res.Settings_UI, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.Settings_UI, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI}}"
Theme="{StaticResource SubtitleTextBlockStyle}" />
<Border IsEnabled="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
<ui:SettingsExpander
Description="{Binding Path=Res.Advertisement_NoShow_Tip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Advertisement_NoShow, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Description="{Binding Path=Res.Advertisement_NoShow_Tip, Mode=OneWay, Source={x:Static spp:Strings.Advertisement_NoShow_Tip}}"
Header="{Binding Path=Res.Advertisement_NoShow, Mode=OneWay, Source={x:Static spp:Strings.Advertisement_NoShow}}"
IsEnabled="{Binding Path=User.UserType, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource EnumEqualValueConverter}, ConverterParameter={x:Static enum:UserType.Sponsor}}">
<ui:SettingsExpander.IconSource>
<ui:FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{StaticResource HeartBroken}" />
@@ -37,8 +37,8 @@
</Border>
<spp:OptionsDisplayItem
Description="{Binding Path=Res.Settings_UI_ThemeStyle, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_Theme, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Description="{Binding Path=Res.Settings_UI_ThemeStyle, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_ThemeStyle}}"
Header="{Binding Path=Res.Settings_Theme, Mode=OneWay, Source={x:Static spp:Strings.Settings_Theme}}"
Icon="DarkTheme">
<spp:OptionsDisplayItem.ActionButton>
<ComboBox SelectedItem="{Binding Source={x:Static ms:UISettings.Theme}, Path=Value, Mode=TwoWay, Converter={StaticResource EnumConverter}, ConverterParameter={x:Type spp:AppTheme}}">
@@ -56,17 +56,17 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem
Description="{Binding Path=Res.Settings_UI_AccentColor, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Description="{Binding Path=Res.Settings_UI_AccentColor, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_AccentColor}}"
Expands="True"
Header="{Binding Path=Res.Settings_ThemeAccent, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_ThemeAccent, Mode=OneWay, Source={x:Static spp:Strings.Settings_ThemeAccent}}"
Icon="ColorLine"
IsExpanded="{Binding Path=IsChecked, ElementName=AccentCustom, Mode=OneWay}">
<spp:OptionsDisplayItem.ActionButton>
<StackPanel Orientation="Horizontal" Spacing="10">
<RadioButton Content="{Binding Path=Res.Settings_UI_SystemDefault, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsChecked="{Binding Source={x:Static ms:UISettings.UseSystemThemeAccent}, Path=Value, Mode=TwoWay}" />
<RadioButton Content="{Binding Path=Res.Settings_UI_SystemDefault, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_SystemDefault}}" IsChecked="{Binding Source={x:Static ms:UISettings.UseSystemThemeAccent}, Path=Value, Mode=TwoWay}" />
<RadioButton
x:Name="AccentCustom"
Content="{Binding Path=Res.Settings_UI_Custom, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Settings_UI_Custom, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_Custom}}"
IsChecked="{Binding Source={x:Static ms:UISettings.UseSystemThemeAccent}, Path=!Value, Mode=OneWay}" />
</StackPanel>
</spp:OptionsDisplayItem.ActionButton>
@@ -76,7 +76,7 @@
<TextBlock
VerticalAlignment="Center"
DockPanel.Dock="Left"
Text="{Binding Path=Res.Settings_UI_PreviewColor, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Settings_UI_PreviewColor, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_PreviewColor}}" />
<Grid
HorizontalAlignment="Left"
@@ -252,7 +252,7 @@
<TextBlock
VerticalAlignment="Center"
DockPanel.Dock="Left"
Text="{Binding Path=Res.Settings_UI_CustomColor, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Settings_UI_CustomColor, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_CustomColor}}" />
<ui:ColorPickerButton
DockPanel.Dock="Right"
@@ -270,8 +270,8 @@
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem
Description="{Binding Path=Res.Settings_UI_DefaultLanguage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_Language, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Description="{Binding Path=Res.Settings_UI_DefaultLanguage, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_DefaultLanguage}}"
Header="{Binding Path=Res.Settings_Language, Mode=OneWay, Source={x:Static spp:Strings.Settings_Language}}"
Navigates="False">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xF2B7;" />
@@ -291,8 +291,8 @@
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem
Description="{Binding Path=Res.Settings_UI_DefaultFonts, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_Fonts, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Description="{Binding Path=Res.Settings_UI_DefaultFonts, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_DefaultFonts}}"
Header="{Binding Path=Res.Settings_Fonts, Mode=OneWay, Source={x:Static spp:Strings.Settings_Fonts}}"
Icon="Font"
Navigates="False">
<spp:OptionsDisplayItem.ActionButton>
@@ -314,8 +314,8 @@
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem
Description="{Binding Path=Res.Settings_UI_WindowBackgroundStyle, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_UI_WindowTransparencyMaterial, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Description="{Binding Path=Res.Settings_UI_WindowBackgroundStyle, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_WindowBackgroundStyle}}"
Header="{Binding Path=Res.Settings_UI_WindowTransparencyMaterial, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_WindowTransparencyMaterial}}"
Navigates="False">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xF4A5;" />
@@ -336,7 +336,7 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_UI_BackgroundOpacity, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Navigates="False">
<spp:OptionsDisplayItem Header="{Binding Path=Res.Settings_UI_BackgroundOpacity, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_BackgroundOpacity}}" Navigates="False">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE706;" />
</spp:OptionsDisplayItem.Icon>
@@ -351,7 +351,7 @@
<spp:OptionsDisplayItem
Expands="True"
Header="{Binding Path=Res.Settings_UI_CustomBackgroundImage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Header="{Binding Path=Res.Settings_UI_CustomBackgroundImage, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_CustomBackgroundImage}}"
IsExpanded="{Binding Source={x:Static ms:UISettings.WindowBackgroundCustomImage}, Path=Value, Mode=OneWay}"
Navigates="False">
<spp:OptionsDisplayItem.Icon>
@@ -367,7 +367,7 @@
<TextBlock
VerticalAlignment="Center"
DockPanel.Dock="Left"
Text="{Binding Path=Res.PreviewPictures, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.PreviewPictures, Mode=OneWay, Source={x:Static spp:Strings.PreviewPictures}}" />
<Image
Width="250"
@@ -389,7 +389,7 @@
<TextBlock
VerticalAlignment="Center"
DockPanel.Dock="Left"
Text="{Binding Path=Res.Settings_UI_BackgroundImageOpacity, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Settings_UI_BackgroundImageOpacity, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_BackgroundImageOpacity}}" />
<Slider
MinWidth="250"
DockPanel.Dock="Right"
@@ -408,11 +408,11 @@
<TextBlock
VerticalAlignment="Center"
DockPanel.Dock="Left"
Text="{Binding Path=Res.SelectPicture, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.SelectPicture, Mode=OneWay, Source={x:Static spp:Strings.SelectPicture}}" />
<Button
MinWidth="150"
Command="{Binding SelectImage_Click}"
Content="{Binding Path=Res.Settings_UI_SelectImage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Settings_UI_SelectImage, Mode=OneWay, Source={x:Static spp:Strings.Settings_UI_SelectImage}}"
DockPanel.Dock="Right" />
</DockPanel>
@@ -425,7 +425,7 @@
<TextBlock
VerticalAlignment="Center"
DockPanel.Dock="Left"
Text="{Binding Path=Res.PictureZoom, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.PictureZoom, Mode=OneWay, Source={x:Static spp:Strings.PictureZoom}}" />
<ComboBox
MinWidth="150"
DockPanel.Dock="Right"
@@ -45,7 +45,7 @@
<StackPanel Classes="Mid">
<TextBlock
HorizontalAlignment="Left"
Text="{Binding Path=Res.User_FastLoginTip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.User_FastLoginTip, Mode=OneWay, Source={x:Static spp:Strings.User_FastLoginTip}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<ItemsControl Background="Transparent" ItemsSource="{Binding ExternalLoginChannels}">
<ItemsControl.Styles>
@@ -107,7 +107,7 @@
MaxLength="{x:Static com:PhoneNumberHelper.ChineseMainlandPhoneNumberLength}"
Text="{Binding PhoneNumber}"
TextAlignment="Left"
Watermark="{Binding Path=Res.User_Phone, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Watermark="{Binding Path=Res.User_Phone, Mode=OneWay, Source={x:Static spp:Strings.User_Phone}}" />
<Border HorizontalAlignment="Left" Cursor="Hand">
<DropDownButton
Margin="0"
@@ -139,7 +139,7 @@
VerticalContentAlignment="Center"
MaxLength="{x:Static columns:MaxLengths.SMS_CAPTCHA}"
Text="{Binding SmsCode}"
Watermark="{Binding Path=Res.User_SMSCode, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Watermark="{Binding Path=Res.User_SMSCode, Mode=OneWay, Source={x:Static spp:Strings.User_SMSCode}}" />
<Border
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
@@ -162,22 +162,22 @@
HorizontalContentAlignment="Center"
Classes="accent"
Command="{Binding Submit}"
Content="{Binding Path=Res.LoginAndRegister, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.LoginAndRegister, Mode=OneWay, Source={x:Static spp:Strings.LoginAndRegister}}" />
</StackPanel>
<!-- 2 加载中状态 -->
<StackPanel Classes="Mid">
<TextBlock Text="{Binding Path=Res.User_WaitFastLoginTip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" TextWrapping="Wrap" />
<TextBlock Text="{Binding Path=Res.User_WaitFastLoginTip, Mode=OneWay, Source={x:Static spp:Strings.User_WaitFastLoginTip}}" TextWrapping="Wrap" />
<WrapPanel Margin="0,20" HorizontalAlignment="Center">
<TextBlock Text="{Binding Path=Res.User_LoginUnexpectedPrompt, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{Binding Path=Res.User_LoginUnexpectedPrompt, Mode=OneWay, Source={x:Static spp:Strings.User_LoginUnexpectedPrompt}}" Theme="{StaticResource CaptionTextBlockStyle}" />
<ui:HyperlinkButton
Margin="5,0"
Padding="0"
HorizontalContentAlignment="Center"
Command="{Binding ManualLogin}">
<TextBlock Text="{Binding Path=Res.Login_ManualLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{Binding Path=Res.Login_ManualLogin, Mode=OneWay, Source={x:Static spp:Strings.Login_ManualLogin}}" Theme="{StaticResource CaptionTextBlockStyle}" />
</ui:HyperlinkButton>
</WrapPanel>
@@ -197,7 +197,7 @@
<Button
HorizontalContentAlignment="Center"
Command="{Binding ChangeState}"
Content="{Binding Path=Res.Cancel, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Cancel, Mode=OneWay, Source={x:Static spp:Strings.Cancel}}"
Theme="{StaticResource TransparentButton}">
<Button.CommandParameter>
<x:Int16>0</x:Int16>
@@ -207,7 +207,7 @@
HorizontalContentAlignment="Center"
Command="{Binding ChooseChannel}"
CommandParameter="{Binding CurrentSelectChannel}"
Content="{Binding Path=Res.Retry, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Retry, Mode=OneWay, Source={x:Static spp:Strings.Retry}}"
Theme="{StaticResource TransparentButton}" />
</StackPanel>
</StackPanel>
@@ -220,7 +220,7 @@
HorizontalAlignment="Center">
<TextBlock
FontSize="12"
Text="{Binding Path=Res.User_RegisterAgreed, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.User_RegisterAgreed, Mode=OneWay, Source={x:Static spp:Strings.User_RegisterAgreed}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<ui:HyperlinkButton
Margin="5,0"
@@ -228,16 +228,16 @@
Command="{Binding OpenHyperlink}"
CommandParameter="Agreement"
Cursor="Hand">
<TextBlock Text="{Binding Path=Res.User_Agreement, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{Binding Path=Res.User_Agreement, Mode=OneWay, Source={x:Static spp:Strings.User_Agreement}}" Theme="{StaticResource CaptionTextBlockStyle}" />
</ui:HyperlinkButton>
<TextBlock Text="{Binding Path=Res.And, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{Binding Path=Res.And, Mode=OneWay, Source={x:Static spp:Strings.And}}" Theme="{StaticResource CaptionTextBlockStyle}" />
<ui:HyperlinkButton
Margin="5,0"
Padding="0"
Command="{Binding OpenHyperlink}"
CommandParameter="Privacy"
Cursor="Hand">
<TextBlock Text="{Binding Path=Res.User_Privacy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Text="{Binding Path=Res.User_Privacy, Mode=OneWay, Source={x:Static spp:Strings.User_Privacy}}" Theme="{StaticResource CaptionTextBlockStyle}" />
</ui:HyperlinkButton>
</WrapPanel>
@@ -255,7 +255,7 @@
<Button.CommandParameter>
<x:Int16>0</x:Int16>
</Button.CommandParameter>
<TextBlock Text="{Binding Path=Res.User_FastLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.User_FastLogin, Mode=OneWay, Source={x:Static spp:Strings.User_FastLogin}}" />
</Button>
<Separator
Height="18"
@@ -272,7 +272,7 @@
<Button.CommandParameter>
<x:Int16>1</x:Int16>
</Button.CommandParameter>
<TextBlock Text="{Binding Path=Res.User_PhoneLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.User_PhoneLogin, Mode=OneWay, Source={x:Static spp:Strings.User_PhoneLogin}}" />
</Button>
</StackPanel>
</Grid>
@@ -45,6 +45,18 @@ public partial class LoginOrRegisterPage : ReactiveUserControl<LoginOrRegisterWi
this.ViewModel.Close();
}
}
Application.Current!.UrlsOpened += Current_UrlsOpened;
}
private async void Current_UrlsOpened(object? sender, UrlOpenedEventArgs e)
{
var loginUrl = e.Urls.Where(x => x.StartsWith(Constants.UrlSchemes.Login)).FirstOrDefault();
if (loginUrl != null)
{
//var token = loginUrl.TrimStart(Constants.UrlSchemes.Login);
var token = loginUrl.Substring(Constants.UrlSchemes.Login.Length, loginUrl.Length - Constants.UrlSchemes.Login.Length);
await ThirdPartyLoginHelper.OnMessageAsync(token);
}
}
protected override void OnDataContextChanged(EventArgs e)
@@ -59,6 +71,7 @@ public partial class LoginOrRegisterPage : ReactiveUserControl<LoginOrRegisterWi
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
Application.Current!.UrlsOpened -= Current_UrlsOpened;
base.OnDetachedFromVisualTree(e);
if (DataContext is LoginOrRegisterWindowViewModel vm)
{
@@ -22,7 +22,7 @@
<DockPanel>
<!--<Button
Margin="0,0,0,5"
Content="{Binding Path=Res.Notice_AllMarkHasRead,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"
Content="{Binding Path=Res.Notice_AllMarkHasRead,Mode=OneWay,Source={x:Static spp:Strings.Notice_AllMarkHasRead}}"
Cursor="Hand"
DockPanel.Dock="Top"
Theme="{StaticResource HyperlinkButton}" />-->
@@ -52,6 +52,7 @@ public sealed partial class MainWindow : ReactiveAppWindow<MainWindowViewModel>
}
Task2.InBackground(async () =>
{
IViewModelManager.Instance.Get<HomePageViewModel>().GetServerContent();
await AdvertiseService.Current.RefrshAdvertiseAsync();
NoticeService.Current.GetNewsAsync();
});
@@ -32,9 +32,9 @@ sealed class ReverseProxyHttpClientHandler : DelegatingHandler
var tlsSniValue = domainConfig.GetTlsSniPattern().WithDomain(uri.Host).WithRandom();
request.SetRequestContext(new RequestContext(isHttps, tlsSniValue));
//// 设置请求头 host,修改协议为 http
//request.Headers.Host = uri.Host;
//request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
// 设置请求头 host,修改协议为 http
request.Headers.Host = uri.Host;
request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
if (domainConfig.Timeout != null)
{
@@ -89,7 +89,7 @@ sealed partial class HttpReverseProxyMiddleware
var forwarderRequestConfig = new ForwarderRequestConfig()
{
Version = context.Request.Protocol.StartsWith("HTTP/2.0") ? System.Net.HttpVersion.Version20 : System.Net.HttpVersion.Version11,
Version = context.Request.Protocol.StartsWith("HTTP/2") ? System.Net.HttpVersion.Version20 : System.Net.HttpVersion.Version11,
};
var error = await httpForwarder.SendAsync(context, destinationPrefix, httpClient, forwarderRequestConfig, HttpTransformer.Empty);
@@ -47,17 +47,8 @@ internal sealed class ScriptRepository : Repository<Script, int>, IScriptReposit
const string sql_ =
$"{SQLStrings.Update}[{Script.TableName}] " +
$"set [{Script.ColumnName_CachePath}] = '{{0}}' " +
$"where [{Script.ColumnName_CachePath}] = {{1}}";
$"where [{Script.ColumnName_Id}] = {{1}}";
var sql = string.Format(sql_, item.CachePath, item.LocalId);
//var sql =
// SQLStrings.Update +
// "[" + Script.TableName + "]" +
// " set " +
// "[" + Script.ColumnName_Enable + "]" +
// $" = {item.Enable}" +
// " where " +
// "[" + Script.ColumnName_Id + "]" +
// $" = {item.LocalId}";
var r = await dbConnection.ExecuteAsync(sql);
return r;
}, cancellationToken: cancellationToken).ConfigureAwait(false);
@@ -366,15 +366,28 @@ public sealed class ScriptManager : GeneralHttpClientFactory, IScriptManager
static IApiRsp OK_Script_DeleteSuccess() => ApiRspHelper.Ok(AppResources.Script_DeleteSuccess);
}
/// <summary>
/// 版本升级 检查数据库 数据 是否正确 不正确删除重新打包缓存文件
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
public async Task<IEnumerable<ScriptDTO>> CheckFiles(IEnumerable<ScriptDTO> list)
{
var scripts = list.ToList();
foreach (var item in list)
{
//检查缓存文件夹如果不是 IScriptManager.DirName_Build 替换成 IScriptManager.DirName_Build 开头
if (!item.CachePath.StartsWith($"{IScriptManager.DirName_Build}/"))
if (!item.CachePath.StartsWith($"{IScriptManager.DirName_Build}{Path.DirectorySeparatorChar}"))
{
item.CachePath = Path.Combine(IScriptManager.DirName_Build, item.FileName!);
var cachePath = Path.Combine(Plugin.Instance.CacheDirectory, item.CachePath);
if (File.Exists(cachePath))
{
File.Delete(cachePath);
}
await TryReadFileAsync(item, true);
//清理 脚本内容 后续 IPC 传递 插件进程读取。
item.Content = string.Empty;
await scriptRepository.SaveScriptCachePathAsync(item, default);
}
if (!CheckFile(item))
@@ -405,8 +418,9 @@ public sealed class ScriptManager : GeneralHttpClientFactory, IScriptManager
/// 修改为仅尝试判断文件是否存在
/// </summary>
/// <param name="item"></param>
/// <param name="isReadContent"></param>
/// <returns></returns>
public async Task<ScriptDTO> TryReadFileAsync(ScriptDTO item)
public async Task<ScriptDTO> TryReadFileAsync(ScriptDTO item, bool isReadContent = false)
{
var cachePath = Path.Combine(Plugin.Instance.CacheDirectory, item.CachePath);
if (File.Exists(cachePath))
@@ -419,7 +433,10 @@ public sealed class ScriptManager : GeneralHttpClientFactory, IScriptManager
var infoPath = Path.Combine(Plugin.Instance.AppDataDirectory, item.FilePath);
if (File.Exists(infoPath))
{
//item.Content = File.ReadAllText(infoPath);
if (isReadContent)
{
item.Content = File.ReadAllText(infoPath);
}
if (!await BuildScriptAsync(item, fileInfo, item.IsCompile))
{
toast.Show(ToastIcon.Error, AppResources.Script_ReadFileError_.Format(item.Name));
@@ -11,7 +11,21 @@ partial class ProxyService
/// 启动代理服务
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
async Task<OperateProxyServiceResult> StartProxyServiceAsync()
{
try
{
var result = await StartProxyServiceCoreAsync();
return result;
}
catch (Exception ex)
{
return ex;
}
}
async Task<OperateProxyServiceResult> StartProxyServiceCoreAsync()
{
IReadOnlyCollection<AccelerateProjectDTO>? proxyDomains = EnableProxyDomains;
@@ -262,7 +276,21 @@ partial class ProxyService
/// <summary>
/// 停止代理服务
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
async Task<OperateProxyServiceResult> StopProxyServiceAsync(bool isExit = false)
{
try
{
var result = await StopProxyServiceCoreAsync(isExit);
return result;
}
catch (Exception ex)
{
return ex;
}
}
async Task<OperateProxyServiceResult> StopProxyServiceCoreAsync(bool isExit)
{
bool callSet = true;
#if WINDOWS
@@ -322,10 +350,12 @@ partial class ProxyService
/// 操作代理服务返回结果
/// </summary>
/// <param name="Error"></param>
readonly record struct OperateProxyServiceResult(string? Error)
readonly record struct OperateProxyServiceResult(string? Error, Exception? Exception = null)
{
public static implicit operator OperateProxyServiceResult(string error) => new(error);
public static implicit operator OperateProxyServiceResult(Exception exception) => new(null, exception);
/// <summary>
/// 将返回结果使用 Toast 显示并且返回当前应设置的代理状态
/// </summary>
@@ -339,6 +369,11 @@ partial class ProxyService
Toast.Show(ToastIcon.Success, Strings.CommunityFix_StartProxySuccess);
proxyStatus = true;
}
else if (Exception != null)
{
Exception.LogAndShowT(nameof(ProxyService));
proxyStatus = false;
}
else
{
Toast.Show(ToastIcon.Error, Error);
@@ -356,6 +391,11 @@ partial class ProxyService
Toast.Show(ToastIcon.Error, Error);
proxyStatus = true;
}
else if (Exception != null)
{
Exception.LogAndShowT(nameof(ProxyService));
proxyStatus = true;
}
return proxyStatus;
}
}
@@ -330,23 +330,24 @@ public sealed partial class ProxyService
#endif
if (result.IsSuccess)
{
if (ProxySettings.SupportProxyServicesStatus.Value.Any_Nullable() && result.Content.Any_Nullable())
{
var items = result.Content!.SelectMany(s => s.Items!);
foreach (var item in items)
{
if (ProxySettings.SupportProxyServicesStatus.Value.Contains(item.Id.ToString()))
{
item.ThreeStateEnable = true;
}
}
}
ProxyDomains.AddOrUpdate(result.Content!);
}
LoadOrSaveLocalAccelerate();
if (ProxySettings.SupportProxyServicesStatus.Value.Any_Nullable() && ProxyDomains.Items.Any_Nullable())
{
var items = ProxyDomains.Items!.SelectMany(s => s.Items!);
var enableItems = ProxySettings.SupportProxyServicesStatus.Value;
foreach (var item in items)
{
if (enableItems.Contains(item.Id.ToString()))
{
item.ThreeStateEnable = true;
}
}
}
//if (IsLoadImage && ProxyDomains.Items.Any_Nullable())
//{
// foreach (var item in ProxyDomains.Items)
@@ -24,7 +24,7 @@
Margin="0,5"
HorizontalAlignment="Center"
Foreground="DodgerBlue"
Text="{Binding Path=Res.UploadFlow, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.UploadFlow, Mode=OneWay, Source={x:Static spp:Strings.UploadFlow}}"
Theme="{StaticResource BodyStrongTextBlockStyle}" />
<TextBlock
x:Name="textBlockRead"
@@ -37,7 +37,7 @@
Margin="0,5"
HorizontalAlignment="Center"
Foreground="IndianRed"
Text="{Binding Path=Res.DownloadFlow, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Text="{Binding Path=Res.DownloadFlow, Mode=OneWay, Source={x:Static spp:Strings.DownloadFlow}}"
Theme="{StaticResource BodyStrongTextBlockStyle}" />
<TextBlock
x:Name="textBlockWrite"
@@ -56,7 +56,7 @@
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding Path=Res.CommunityFix_StartProxy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.CommunityFix_StartProxy, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_StartProxy}}" />
</Panel>
</Button>
@@ -77,7 +77,7 @@
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<!--<Run Text="{Binding Path=Res.CommunityFix_StopProxy,Source={x:Static s:ResourceService.Current}},Mode=OneWay}" />
<!--<Run Text="{Binding Path=Res.CommunityFix_StopProxy,Source={x:Static spp:Strings.CommunityFix_StopProxy}},Mode=OneWay}" />
<Run Text=" " />
<Run Text="{Binding Path=AccelerateTime,Source={x:Static s:ProxyService.Current}},Mode=OneWay,Converter={StaticResource StringFormatConverter}}" />-->
<TextBlock.Text>
@@ -85,7 +85,7 @@
<CompiledBinding
Mode="OneWay"
Path="Res.CommunityFix_StopProxy"
Source="{x:Static s:ResourceService.Current}}" />
Source="{x:Static spp:Strings.CommunityFix_StopProxy}}" />
<CompiledBinding
Converter="{StaticResource StringFormatConverter}"
Mode="OneWay"
@@ -123,7 +123,7 @@
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static spp:Strings.Refresh}}" />
</Panel>
</Button>
@@ -143,7 +143,7 @@
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding Path=Res.CommunityFix_MenuName, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.CommunityFix_MenuName, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_MenuName}}" />
</Panel>
</ToggleButton>
@@ -164,7 +164,7 @@
</DataTemplate>
</ui:MenuFlyoutSubItem.ItemTemplate>
</ui:MenuFlyoutSubItem>-->
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static spp:Strings.Settings_Plugin}}" />-->
<!--<ui:MenuFlyoutSeparator />-->
<!--<ui:MenuFlyoutItem Text="安装证书" />
<ui:MenuFlyoutItem Text="移除证书" />
@@ -180,7 +180,7 @@
<CompiledBinding
Mode="OneWay"
Path="Res.Plugin_Version"
Source="{x:Static s:ResourceService.Current}" />
Source="{x:Static spp:Strings.Plugin_Version}" />
<CompiledBinding
Mode="OneWay"
Path="Version"
@@ -188,9 +188,9 @@
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static spp:Strings.ViewInStore}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static spp:Strings.UseHelp}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static spp:Strings.AboutThisPlugin}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
@@ -198,7 +198,7 @@
</spp:PageBase.ActionContent>
<spp:PageBase.PaneContent>
<StackPanel Margin="15,20" Spacing="10">
<TextBlock Text="{Binding Path=Res.CommunityFix_ProxyMode, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Theme="{StaticResource BodyStrongTextBlockStyle}" />
<TextBlock Text="{Binding Path=Res.CommunityFix_ProxyMode, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ProxyMode}}" Theme="{StaticResource BodyStrongTextBlockStyle}" />
<TabStrip
Background="{DynamicResource ControlOnImageFillColorDefaultBrush}"
@@ -215,14 +215,14 @@
</TabStrip.ItemTemplate>
</TabStrip>
<TextBlock Text="{Binding Path=Res.CommunityFix_MenuName, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" Theme="{StaticResource BodyStrongTextBlockStyle}" />
<TextBlock Text="{Binding Path=Res.CommunityFix_MenuName, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_MenuName}}" Theme="{StaticResource BodyStrongTextBlockStyle}" />
<DockPanel>
<ToggleSwitch
VerticalAlignment="Center"
Classes="NoContent"
IsChecked="{Binding Source={x:Static s:ProxyService.Current}, Path=IsEnableScript, Mode=TwoWay}"
OffContent="{Binding Path=Res.CommunityFix_EnableScriptService, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
OnContent="{Binding Path=Res.CommunityFix_EnableScriptService, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
OffContent="{Binding Path=Res.CommunityFix_EnableScriptService, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_EnableScriptService}}"
OnContent="{Binding Path=Res.CommunityFix_EnableScriptService, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_EnableScriptService}}" />
<!--<WrapPanel>-->
<TextBlock
@@ -247,7 +247,7 @@
VerticalAlignment="Center"
Command="{Binding Source={x:Static s:ProxyService.Current}, Path=RefreshIpv6Support, Mode=OneWay}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
ToolTip.Tip="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static spp:Strings.Refresh}}">
<ui:SymbolIcon Symbol="Refresh" />
</Button>
</WrapPanel>-->
@@ -256,8 +256,8 @@
VerticalAlignment="Center"
Classes="NoContent"
IsChecked="{Binding Source={x:Static ms:ProxySettings.ProgramStartupRunProxy}, Path=Value, Mode=TwoWay}"
OffContent="{Binding Path=Res.CommunityFix_AutoRunProxy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
OnContent="{Binding Path=Res.CommunityFix_AutoRunProxy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
OffContent="{Binding Path=Res.CommunityFix_AutoRunProxy, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_AutoRunProxy}}"
OnContent="{Binding Path=Res.CommunityFix_AutoRunProxy, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_AutoRunProxy}}" />
<!--<StackPanel Orientation="Horizontal" Spacing="4">
<ToggleButton
@@ -265,7 +265,7 @@
Theme="{StaticResource TransparentToggleButton}"
ToolTip.Tip="{Binding Path=Res.GameList_OnlyInstalled,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}">
Source={x:Static spp:Strings.GameList_OnlyInstalled}}">
<Viewbox Width="20" Height="20">
<ui:SymbolIcon Symbol="Play" />
</Viewbox>
@@ -276,7 +276,7 @@
Theme="{StaticResource TransparentToggleButton}"
ToolTip.Tip="{Binding Path=Res.GameList_OnlyCloudArchive,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}">
Source={x:Static spp:Strings.GameList_OnlyCloudArchive}}">
<Viewbox Width="20" Height="20">
<ui:SymbolIcon Symbol="CloudSyncComplete" />
</Viewbox>
@@ -286,37 +286,37 @@
<Button
HorizontalAlignment="Stretch"
Command="{Binding ProxySettingsCommand}"
Content="{Binding Path=Res.CommunityFix_ProxySettings, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.CommunityFix_ProxySettings, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ProxySettings}}" />
<Separator Margin="0,8" Classes="Horizontal" />
<Button
HorizontalAlignment="Stretch"
Command="{Binding SetupCertificateCommand}"
Content="{Binding Path=Res.CommunityFix_SetupCertificate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.CommunityFix_SetupCertificate, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_SetupCertificate}}" />
<Button
HorizontalAlignment="Stretch"
Command="{Binding DeleteCertificateCommand}"
Content="{Binding Path=Res.CommunityFix_DeleteCertificate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.CommunityFix_DeleteCertificate, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_DeleteCertificate}}" />
<Button
HorizontalAlignment="Stretch"
Command="{Binding OpenCertificateDirCommand}"
Content="{Binding Path=Res.CommunityFix_OpenCertificateDir, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.CommunityFix_OpenCertificateDir, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_OpenCertificateDir}}" />
<Separator Margin="0,8" Classes="Horizontal" />
<Button
HorizontalAlignment="Stretch"
Command="{Binding EditHostsFileCommand}"
Content="{Binding Path=Res.CommunityFix_EditHostsFile, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.CommunityFix_EditHostsFile, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_EditHostsFile}}" />
<Button
HorizontalAlignment="Stretch"
Command="{Binding ResetHostsFileCommand}"
Content="{Binding Path=Res.CommunityFix_ResetHostsFile, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.CommunityFix_ResetHostsFile, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ResetHostsFile}}" />
<Button
HorizontalAlignment="Stretch"
Command="{Binding OpenHostsDirCommand}"
Content="{Binding Path=Res.CommunityFix_OpenHostsDir, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Content="{Binding Path=Res.CommunityFix_OpenHostsDir, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_OpenHostsDir}}" />
<!--<ui:FAComboBox ItemsSource="{Binding Source={x:Static ms:ProxySettings.ProxyModes}, Path=., Mode=OneWay}" SelectedItem="{Binding Source={x:Static ms:ProxySettings.ProxyMode}, Path=Value, Mode=TwoWay}">
<ui:FAComboBox.ItemTemplate>
@@ -358,7 +358,8 @@
</ia:DataTriggerBehavior>
</i:Interaction.Behaviors>-->
<spp:ContentLoader IsLoading="{Binding Source={x:Static s:ProxyService.Current}, Path=!!!ProxyDomainsList.Count, Mode=OneWay}" NoResultMessage="{Binding Path=Res.CommunityFix_ItemsEmpty, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<spp:ContentLoader IsLoading="{Binding Source={x:Static s:ProxyService.Current}, Path=!!!ProxyDomainsList.Count, Mode=OneWay}"
NoResultMessage="{Binding Path=Res.CommunityFix_ItemsEmpty, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ItemsEmpty}}">
<Grid ColumnDefinitions="*,Auto">
<ItemsControl ItemsSource="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyDomainsList, Mode=OneWay}">
<ItemsControl.ItemsPanel>
@@ -435,8 +436,8 @@
x:Name="Tabs"
ItemContainerTheme="{StaticResource TabItemStyle}"
Theme="{StaticResource TabStripStyle}">
<TabStripItem Content="{Binding Path=Res.CommunityFix_FlowStatistics, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TabStripItem Content="{Binding Path=Res.CommunityFix_Services, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TabStripItem Content="{Binding Path=Res.CommunityFix_FlowStatistics, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_FlowStatistics}}" />
<TabStripItem Content="{Binding Path=Res.CommunityFix_Services, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_Services}}" />
</TabStrip>
<StackPanel
Grid.Row="0"
@@ -450,7 +451,7 @@
</Style>
</StackPanel.Styles>
<!--<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<TextBlock FontSize="26" Text="{Binding Path=Res.CommunityFix_AlreadyProxy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock FontSize="26" Text="{Binding Path=Res.CommunityFix_AlreadyProxy, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_AlreadyProxy}}" />
<TextBlock
Margin="5,0,0,0"
FontSize="26"
@@ -459,12 +460,12 @@
StringFormat={}{0:hh\\:mm\\:ss}}" />
</StackPanel>-->
<TextBlock>
<Run Text="{Binding Path=Res.CommunityFix_ProxyMode, StringFormat={}{0}:, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Run Text="{Binding Path=Res.CommunityFix_ProxyMode, StringFormat={}{0}:, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ProxyMode, StringFormat={}{0}:}}" />
<Run Text="{Binding Source={x:Static ms:ProxySettings.ProxyMode}, Path=Value, Mode=OneWay, Converter={StaticResource EnumLocalizationNameConverter}}" />
<Run Text=" " />
<Run Text="{Binding Path=Res.CommunityFix_EnableScriptService, StringFormat={}{0}:, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Run Text="{Binding Path=Res.CommunityFix_EnableScriptService, StringFormat={}{0}:, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_EnableScriptService, StringFormat={}{0}:}}" />
<Run Text="{Binding Source={x:Static s:ProxyService.Current}, Path=IsEnableScript, Converter={StaticResource StringFormatConverter}}" />
</TextBlock>
</StackPanel>
@@ -487,7 +488,7 @@
Margin="0,0,0,10"
DockPanel.Dock="Top"
FontSize="16"
Text="{Binding Path=Res.CommunityFix_AccelerationsEnable, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.CommunityFix_AccelerationsEnable, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_AccelerationsEnable}}" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding EnableProxyDomains, Source={x:Static s:ProxyService.Current}}">
<ItemsControl.ItemTemplate>
@@ -514,7 +515,7 @@
Margin="0,0,0,10"
DockPanel.Dock="Top"
FontSize="16"
Text="{Binding Path=Res.CommunityFix_ScriptsEnable, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.CommunityFix_ScriptsEnable, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ScriptsEnable}}" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding EnableProxyScripts^, Source={x:Static s:ProxyService.Current}}">
<ItemsControl.ItemTemplate>
@@ -30,12 +30,12 @@
</On>
</OnFormFactor>
</TabStrip.ItemsPanel>
<TabStripItem Content="{Binding Path=Res.CommunityFix, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<TabStripItem Content="{Binding Path=Res.CommunityFix, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix}}">
<TabStripItem.Tag>
<x:Type TypeName="spp:AcceleratorPage" />
</TabStripItem.Tag>
</TabStripItem>
<TabStripItem Content="{Binding Path=Res.ScriptConfig, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<TabStripItem Content="{Binding Path=Res.ScriptConfig, Mode=OneWay, Source={x:Static spp:Strings.ScriptConfig}}">
<TabStripItem.Tag>
<x:Type TypeName="spp:ScriptPage" />
</TabStripItem.Tag>
@@ -14,10 +14,10 @@
x:DataType="spp:ProxySettingsWindowViewModel"
mc:Ignorable="d">
<StackPanel Margin="{StaticResource PageMargin}" Spacing="{StaticResource SettingsSpacing}">
<ui:SettingsExpander Header="{Binding Path=Res.CommunityFix_ProxySettings, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsExpanded="True">
<ui:SettingsExpander Header="{Binding Path=Res.CommunityFix_ProxySettings, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ProxySettings}}" IsExpanded="True">
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.Settings_Proxy_SystemProxyIp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.Settings_Proxy_SystemProxyIp, Mode=OneWay, Source={x:Static spp:Strings.Settings_Proxy_SystemProxyIp}}" />
<ui:SettingsExpanderItem.Footer>
<ui:FAComboBox
IsEditable="True"
@@ -27,7 +27,7 @@
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.Settings_Proxy_CustomDNS, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.Settings_Proxy_CustomDNS, Mode=OneWay, Source={x:Static spp:Strings.Settings_Proxy_CustomDNS}}" />
<ui:SettingsExpanderItem.Footer>
<ui:FAComboBox
IsEditable="True"
@@ -48,35 +48,35 @@
<Button Content="{Binding Path=Res.Refresh,Mode=OneWay,Source={x:Static resx:R.Current}}"></Button>
</DockPanel>-->
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.CommunityFix_AutoRunProxy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.CommunityFix_AutoRunProxy, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_AutoRunProxy}}" />
<ui:SettingsExpanderItem.Footer>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.ProgramStartupRunProxy}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.CommunityFix_ProxyOnlyOnScript, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.CommunityFix_ProxyOnlyOnScript, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_ProxyOnlyOnScript}}" />
<ui:SettingsExpanderItem.Footer>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.OnlyEnableProxyScript}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.CommunityFix_EnableHttpProxyToHttps, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.CommunityFix_EnableHttpProxyToHttps, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_EnableHttpProxyToHttps}}" />
<ui:SettingsExpanderItem.Footer>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.EnableHttpProxyToHttps}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.CommunityFix_EnableUseDoh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.CommunityFix_EnableUseDoh, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_EnableUseDoh}}" />
<ui:SettingsExpanderItem.Footer>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ProxySettings.UseDoh}, Path=Value, Mode=TwoWay}" />
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem>
<TextBlock Text="{Binding Path=Res.CommunityFix_CustomDohAddres, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.CommunityFix_CustomDohAddres, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_CustomDohAddres}}" />
<ui:SettingsExpanderItem.Footer>
<ui:FAComboBox
IsEditable="True"
@@ -8,7 +8,7 @@
xmlns:s="https://steampp.net/services"
xmlns:spp="https://steampp.net/ui"
xmlns:ui="using:FluentAvalonia.UI.Controls"
Title="{Binding Path=Res.ScriptConfig, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Title="{Binding Path=Res.ScriptConfig, Mode=OneWay, Source={x:Static spp:Strings.ScriptConfig}}"
d:DesignHeight="450"
d:DesignWidth="800"
x:CompileBindings="True"
@@ -37,7 +37,7 @@
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding Path=Res.ScriptStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.ScriptStore, Mode=OneWay, Source={x:Static spp:Strings.ScriptStore}}" />
</Panel>
</Button>
<Separator
@@ -64,7 +64,7 @@
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding Path=Res.Import, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Import, Mode=OneWay, Source={x:Static spp:Strings.Import}}" />
</Panel>
</Button>
<Button
@@ -83,7 +83,7 @@
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static spp:Strings.Refresh}}" />
</Panel>
</Button>
<DropDownButton
@@ -96,10 +96,10 @@
Theme="{StaticResource TransparentButton}">
<DropDownButton.Flyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.Script_Settings, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.Script_Settings, Mode=OneWay, Source={x:Static spp:Strings.Script_Settings}}" />-->
<!--<ui:MenuFlyoutSeparator />-->
<ui:ToggleMenuFlyoutItem IsChecked="{Binding Source={x:Static ms:ProxySettings.IsAutoCheckScriptUpdate}, Path=Value, Mode=TwoWay}" Text="{Binding Path=Res.Script_AutoUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:ToggleMenuFlyoutItem IsChecked="{Binding Source={x:Static ms:ProxySettings.IsOnlyWorkSteamBrowser}, Path=Value, Mode=TwoWay}" Text="{Binding Path=Res.CommunityFix_OnlySteamBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:ToggleMenuFlyoutItem IsChecked="{Binding Source={x:Static ms:ProxySettings.IsAutoCheckScriptUpdate}, Path=Value, Mode=TwoWay}" Text="{Binding Path=Res.Script_AutoUpdate, Mode=OneWay, Source={x:Static spp:Strings.Script_AutoUpdate}}" />
<ui:ToggleMenuFlyoutItem IsChecked="{Binding Source={x:Static ms:ProxySettings.IsOnlyWorkSteamBrowser}, Path=Value, Mode=TwoWay}" Text="{Binding Path=Res.CommunityFix_OnlySteamBrowser, Mode=OneWay, Source={x:Static spp:Strings.CommunityFix_OnlySteamBrowser}}" />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem IsEnabled="False">
<ui:MenuFlyoutItem.Text>
@@ -107,7 +107,7 @@
<CompiledBinding
Mode="OneWay"
Path="Res.Plugin_Version"
Source="{x:Static s:ResourceService.Current}" />
Source="{x:Static spp:Strings.Plugin_Version}" />
<CompiledBinding
Mode="OneWay"
Path="Version"
@@ -115,9 +115,9 @@
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static spp:Strings.ViewInStore}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static spp:Strings.UseHelp}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static spp:Strings.AboutThisPlugin}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
@@ -160,7 +160,7 @@
Command="{Binding $parent[spp:PageBase].((spp:ScriptPageViewModel)DataContext).EditScriptItemCommand}"
CommandParameter="{Binding}"
Theme="{DynamicResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Script_Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
ToolTip.Tip="{Binding Path=Res.Script_Edit, Mode=OneWay, Source={x:Static spp:Strings.Script_Edit}}">
<Viewbox Width="20" Height="20">
<ui:SymbolIcon Symbol="CodeHTML" />
</Viewbox>
@@ -169,7 +169,7 @@
Command="{Binding $parent[spp:PageBase].((spp:ScriptPageViewModel)DataContext).RefreshScriptItemCommand}"
CommandParameter="{Binding}"
Theme="{DynamicResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Script_Refresh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
ToolTip.Tip="{Binding Path=Res.Script_Refresh, Mode=OneWay, Source={x:Static spp:Strings.Script_Refresh}}">
<Viewbox Width="20" Height="20">
<ui:SymbolIcon Symbol="Refresh" />
</Viewbox>
@@ -183,17 +183,17 @@
CommandParameter="{Binding}"
Text="{Binding Path=Res.Script_Edit,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
Source={x:Static spp:Strings.Script_Edit}}" />
<ui:MenuFlyoutItem
Command="{Binding $parent[spp:PageBase].((spp:ScriptPageViewModel)DataContext).RefreshScriptItemButton}"
CommandParameter="{Binding}"
Text="{Binding Path=Res.Script_Refresh,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />-->
Source={x:Static spp:Strings.Script_Refresh}}" />-->
<ui:MenuFlyoutItem
Command="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
CommandParameter="{Binding SourceLink}"
Text="{Binding Path=Res.Script_Home, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Script_Home, Mode=OneWay, Source={x:Static spp:Strings.Script_Home}}" />
<ui:MenuFlyoutItem
Command="{Binding $parent[spp:PageBase].((spp:ScriptPageViewModel)DataContext).DownloadScriptItemCommand}"
CommandParameter="{Binding}"
@@ -203,7 +203,7 @@
<CompiledBinding
Mode="OneWay"
Path="Res.Script_Update"
Source="{x:Static s:ResourceService.Current}" />
Source="{x:Static spp:Strings.Script_Update}" />
<CompiledBinding Mode="OneWay" Path="NewVersion" />
</MultiBinding>
</ui:MenuFlyoutItem.Text>
@@ -213,7 +213,7 @@
Command="{Binding $parent[spp:PageBase].((spp:ScriptPageViewModel)DataContext).DeleteScriptItemCommand}"
CommandParameter="{Binding}"
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
Text="{Binding Path=Res.Delete, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Delete, Mode=OneWay, Source={x:Static spp:Strings.Delete}}" />
</ui:FAMenuFlyout>
</spp:AppItem.MoreFlyout>
<spp:AppItem.Image>
@@ -230,7 +230,7 @@
Classes="Circle"
IsIndeterminate="True"
IsVisible="{Binding IsLoading}"
ToolTip.Tip="{Binding Path=Res.Script_ImportLoading, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
ToolTip.Tip="{Binding Path=Res.Script_ImportLoading, Mode=OneWay, Source={x:Static spp:Strings.Script_ImportLoading}}" />
</Panel>
</spp:AppItem.Image>
</spp:AppItem>
@@ -43,7 +43,7 @@
<spp:AppItem.Status>
<Panel>
<Border Classes="Status" IsVisible="{Binding IsExist}">
<TextBlock Text="{Binding Path=Res.Installed, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Text="{Binding Path=Res.Installed, Mode=OneWay, Source={x:Static spp:Strings.Installed}}" />
</Border>
<Button
Classes="accent"
@@ -69,7 +69,7 @@
<ui:MenuFlyoutItem
Command="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
CommandParameter="{Binding SourceLink}"
Text="{Binding Path=Res.Script_Home, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Text="{Binding Path=Res.Script_Home, Mode=OneWay, Source={x:Static spp:Strings.Script_Home}}" />
</ui:FAMenuFlyout>
</spp:AppItem.MoreFlyout>
<spp:AppItem.Image>
@@ -86,4 +86,4 @@
</ItemsRepeater>
</spp:ContentLoader>
</StackPanel>
</UserControl>
</UserControl>
@@ -72,9 +72,10 @@
<ItemGroup>
<!-- ArchiSteamFarm WebAPI Models -->
<Compile Include="..\..\ref\ArchiSteamFarm\ArchiSteamFarm\IPC\Requests\*.cs">
<LinkBase>_\ArchiSteamFarm\IPC\Requests</LinkBase>
</Compile>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="_\ArchiSteamFarm\Localization\Strings.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
@@ -100,6 +101,9 @@
<Compile Include="..\..\ref\ArchiSteamFarm\ArchiSteamFarm\Steam\Data\Confirmation.cs">
<LinkBase>_\ArchiSteamFarm\Steam\Data</LinkBase>
</Compile>
<Compile Include="..\..\ref\ArchiSteamFarm\ArchiSteamFarm\Steam\Cards\Game.cs">
<LinkBase>_\ArchiSteamFarm\Steam\Cards</LinkBase>
</Compile>
</ItemGroup>
<ItemGroup>
@@ -125,6 +129,14 @@
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="UI\Views\Pages\ArchiSteamFarmPage.axaml.cs">
<DependentUpon>ArchiSteamFarmPage.axaml</DependentUpon>
</Compile>
<Compile Update="_\ArchiSteamFarm\Localization\Strings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
@@ -0,0 +1,14 @@
namespace BD.WTTS.Enums;
public enum ActionItem
{
StartOrStop = 1,
AddBot,
Refresh,
OpenWebConsole,
Wiki,
Repo,
ConfigGenerator,
WebAddBot,
WebConfig,
}
@@ -0,0 +1,19 @@
namespace BD.WTTS.Enums;
public enum EPathFolder : byte
{
/// <inheritdoc cref="SharedInfo.HomeDirectory"/>
ASF,
/// <inheritdoc cref="SharedInfo.ConfigDirectory"/>
Config,
/// <inheritdoc cref="SharedInfo.PluginsDirectory"/>
Plugin,
/// <inheritdoc cref="SharedInfo.WebsiteDirectory"/>
WWW,
/// <inheritdoc cref="SharedInfo.LogFileDirectory"/>
Logs,
}
@@ -0,0 +1,17 @@
namespace BD.WTTS;
public static class EPathFolderExtensions
{
public static string GetFolderPath(this EPathFolder folderASFPath)
{
string folderASFPathValue = folderASFPath switch
{
EPathFolder.Config => SharedInfo.DirCreateByNotExists(Path.Combine(SharedInfo.HomeDirectory, SharedInfo.ConfigDirectory)),
EPathFolder.Plugin => SharedInfo.DirCreateByNotExists(Path.Combine(SharedInfo.HomeDirectory, SharedInfo.PluginsDirectory)),
EPathFolder.WWW => Path.Combine(SharedInfo.HomeDirectory, SharedInfo.WebsiteDirectory),
EPathFolder.Logs => Path.Combine(SharedInfo.HomeDirectory, SharedInfo.ArchivalLogsDirectory),
EPathFolder.ASF or _ => SharedInfo.HomeDirectory,
};
return folderASFPathValue;
}
}
@@ -14,9 +14,10 @@ public static partial class ServiceCollectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IServiceCollection AddArchiSteamFarmService(this IServiceCollection services)
{
//services.AddSingleton<IArchiSteamFarmService, ArchiSteamFarmServiceImpl>();
//services.AddSingleton<IArchiSteamFarmHelperService>(s => s.GetRequiredService<IArchiSteamFarmService>());
services.AddSingleton<IArchiSteamFarmWebApiService, ArchiSteamFarmWebApiServiceImpl>();
services.AddSingleton<IArchiSteamFarmService, ArchiSteamFarmServiceImpl>();
//services.AddSingleton<IArchiSteamFarmHelperService>(s => s.GetRequiredService<IArchiSteamFarmService>());
return services;
}
@@ -1,3 +1,5 @@
using BD.WTTS.Properties;
using BD.WTTS.UI.Views.Pages;
using CreateHttpHandlerArgs = System.ValueTuple<
bool,
System.Net.DecompressionMethods,
@@ -18,13 +20,16 @@ public sealed class Plugin : PluginBase<Plugin>, IPlugin
public override string Name => moduleName;
public sealed override object? Icon => Resources.asf;
public override IEnumerable<MenuTabItemViewModel>? GetMenuTabItems()
{
yield return new MenuTabItemViewModel(this, "ArchiSteamFarmPlus")
{
PageType = null,
PageType = typeof(ArchiSteamFarmPage),
IsResourceGet = true,
IconKey = "GameConsole",
//IconKey = "GameConsole",
IconKey = Icon
};
}
@@ -97,4 +102,9 @@ public sealed class Plugin : PluginBase<Plugin>, IPlugin
{
}
public override IEnumerable<(Action<IServiceCollection>? @delegate, bool isInvalid, string name)>? GetConfiguration(bool directoryExists)
{
yield return GetConfiguration<ASFSettings_>(directoryExists);
}
}
@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace BD.WTTS.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BD.WTTS.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] asf {
get {
object obj = ResourceManager.GetObject("asf", resourceCulture);
return ((byte[])(obj));
}
}
}
}
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="asf" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\UI\Assets\asf.ico;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>
@@ -1,336 +1,553 @@
//#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
//using ASFStrings = ArchiSteamFarm.Localization.Strings;
//using AppResources = BD.WTTS.Client.Resources.Strings;
//using ASFNLogManager = ArchiSteamFarm.LogManager;
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
using ASFStrings = ArchiSteamFarm.Localization.Strings;
using AppResources = BD.WTTS.Client.Resources.Strings;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Storage;
using ArchiSteamFarm.IPC;
using Newtonsoft.Json.Linq;
//// ReSharper disable once CheckNamespace
//namespace BD.WTTS.Services.Implementation;
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services.Implementation;
//public partial class ArchiSteamFarmServiceImpl : ReactiveObject, IArchiSteamFarmService, IIoc
//{
// const string TAG = "ArchiSteamFarmS";
public partial class ArchiSteamFarmServiceImpl : ReactiveObject, IArchiSteamFarmService
{
const string TAG = "ArchiSteamFarmS";
// public ArchiSteamFarmServiceImpl()
// {
// ArchiSteamFarmLibrary.Init(this, IOPath.AppDataDirectory, IApplication.LogDirPathASF);
// }
private readonly AsyncLock @lock = new AsyncLock();
// public event Action<string>? OnConsoleWirteLine;
private bool isFirstStart = true;
// public TaskCompletionSource<string>? ReadLineTask { get; set; }
private bool _IsReadPasswordLine;
// private readonly AsyncLock @lock = new AsyncLock();
private HttpClient _httpClient = new();
// bool _IsReadPasswordLine;
protected readonly IArchiSteamFarmWebApiService webApiService = IArchiSteamFarmWebApiService.Instance;
// public bool IsReadPasswordLine
// {
// get => _IsReadPasswordLine;
// set => this.RaiseAndSetIfChanged(ref _IsReadPasswordLine, value);
// }
public ArchiSteamFarmServiceImpl()
{
}
// public DateTimeOffset? StartTime { get; set; }
public Process? ASFProcess { get; set; }
// public Version CurrentVersion => SharedInfo.Version;
public event Action<string>? OnConsoleWirteLine;
// private bool isFirstStart = true;
public TaskCompletionSource<string>? ReadLineTask { get; set; }
// T IIoc.GetRequiredService<T>() where T : class => Ioc.Get<T>();
public bool IsReadPasswordLine
{
get => _IsReadPasswordLine;
set => this.RaiseAndSetIfChanged(ref _IsReadPasswordLine, value);
}
// T? IIoc.GetService<T>() where T : class => Ioc.Get_Nullable<T>();
public DateTimeOffset? StartTime { get; set; }
// static void InitCoreLoggers()
// {
// IApplication.LogDirPathASF = ASFPathHelper.GetLogDirectory(IApplication.LogDirPathASF);
// if (ASFNLogManager.Configuration != null) return;
// LoggingConfiguration config = new();
// FileTarget fileTarget = new("File")
// {
// ArchiveFileName = ASFPathHelper.GetNLogArchiveFileName(IApplication.LogDirPathASF),
// ArchiveNumbering = ArchiveNumberingMode.Rolling,
// ArchiveOldFileOnStartup = true,
// CleanupFileName = false,
// ConcurrentWrites = false,
// DeleteOldFileOnStartup = true,
// FileName = ASFPathHelper.GetNLogFileName(IApplication.LogDirPathASF),
// Layout = ASFPathHelper.NLogGeneralLayout,
// ArchiveAboveSize = 10485760,
// MaxArchiveFiles = 10,
// MaxArchiveDays = 7,
// };
// //IApplication.InitializeTarget(config, fileTarget, NLogLevel.Debug);
// var historyTarget = new HistoryTarget("History")
// {
// Layout = ASFPathHelper.NLogGeneralLayout,
// MaxCount = 20,
// };
// //IApplication.InitializeTarget(config, historyTarget, NLogLevel.Debug);
// ASFNLogManager.Configuration = config;
// }
public Version CurrentVersion => SharedInfo.Version;
// public async Task<bool> StartAsync(string[]? args = null)
// {
// try
// {
// StartTime = DateTimeOffset.Now;
public void ShellMessageInput(string data)
{
using (var sw = ASFProcess.StandardInput)
{
sw.WriteLine(data);
sw.Flush();
}
}
// if (isFirstStart)
// {
// InitCoreLoggers();
public async Task<bool> StartAsync(string[]? args = null)
{
try
{
var isOk = false;
StartTime = DateTimeOffset.Now;
if (isFirstStart)
{
await ReadEncryptionKeyAsync();
isFirstStart = false;
}
// InitHistoryLogger();
if (!await StartProcess() && !isFirstStart)
{
await StopAsync().ConfigureAwait(false);
}
else
{
isOk = true;
ASF.IsReady = true;
}
return isOk;
}
catch (Exception e)
{
e.LogAndShowT(TAG, msg: "ASF Start Fail.");
await StopAsync().ConfigureAwait(false);
return false;
}
}
// ArchiSteamFarm.NLog.Logging.GetUserInputFunc = async (bool isPassword) =>
// {
// using (await @lock.LockAsync())
// {
// ReadLineTask = new(TaskCreationOptions.AttachedToParent);
// IsReadPasswordLine = isPassword;
//#if NET6_0_OR_GREATER
// var result = await ReadLineTask.Task.WaitAsync(TimeSpan.FromSeconds(60));
//#else
// var result = await ReadLineTask.Task;
//#endif
// if (IsReadPasswordLine)
// IsReadPasswordLine = false;
// ReadLineTask = null;
// return result;
// }
// };
#region StartProcess
// await ReadEncryptionKeyAsync();
private async Task<bool> StartProcess()
{
var isStartSuccess = false;
try
{
if (ASFProcess != null || string.IsNullOrEmpty(SharedInfo.ASFExecuteFilePath))
{
Toast.Show(ToastIcon.Error, BDStrings.ASF_SelectASFExePath);
return isStartSuccess;
}
// await Program.Init(args).ConfigureAwait(false);
// isFirstStart = false;
// }
// else
// {
// if (!await Program.InitASF().ConfigureAwait(false))
// {
// await StopAsync().ConfigureAwait(false);
// return false;
// }
// }
// return true;
// }
// catch (Exception e)
// {
// e.LogAndShowT(TAG, msg: "ASF Start Fail.");
// await StopAsync().ConfigureAwait(false);
// return false;
// }
// }
if (ASFSettings.CheckArchiSteamFarmExe && !await CheckFileConsistence()) // 检查文件是否被篡改
{
Toast.Show(ToastIcon.Error, BDStrings.ASF_ExecuteFileUnsafe);
return isStartSuccess;
}
// public async Task StopAsync()
// {
// StartTime = null;
// ReadLineTask?.TrySetResult("");
// await Program.InitShutdownSequence();
// }
webApiService.SetIPCUrl(GetIPCUrl()); // 设置 IPC 接口地址
// async Task IArchiSteamFarmHelperService.Restart()
// {
// Toast.Show(AppResources.ASF_Restarting, ToastLength.Short);
KillASFProcess(); // 杀死未关闭的 ASF 进程
// var s = ASFService.Current;
// if (s.IsASFRuning)
// {
// await s.StopASFCoreAsync(false);
// }
// await s.InitASFCoreAsync(false);
var options = new ProcessStartInfo(SharedInfo.ASFExecuteFilePath);
options.CreateNoWindow = true;
options.UseShellExecute = false;
options.RedirectStandardOutput = true;
options.RedirectStandardInput = true;
options.RedirectStandardError = true;
if (!string.IsNullOrEmpty(EncryptionKey))
{
options.ArgumentList.Add("--CRYPTKEY");
options.ArgumentList.Add(EncryptionKey);
}
ASFProcess = Process.Start(options);
// Toast.Show(AppResources.ASF_Restarted, ToastLength.Short);
// }
ASFService.Current.ConsoleLogText = string.Empty;
ThreadPool.QueueUserWorkItem(ReadOutPutData);
AppDomain.CurrentDomain.ProcessExit += ExitHandler;
AppDomain.CurrentDomain.UnhandledException += ExitHandler;
// LogLevel IArchiSteamFarmHelperService.MinimumLevel => IApplication.LoggerMinLevel;
ASFProcess!.ErrorDataReceived += new DataReceivedEventHandler(ExitHandler);
ASFProcess.BeginErrorReadLine();
while (!SocketHelper.IsUsePort(CurrentIPCPortValue))
{
Thread.Sleep(1000);
continue;
}
isStartSuccess = true;
}
catch (Exception)
{
}
return isStartSuccess;
}
// private void InitHistoryLogger()
// {
// ArchiSteamFarm.NLog.Logging.InitHistoryLogger();
private void KillASFProcess()
{
var processName = Path.GetFileNameWithoutExtension(SharedInfo.ASFExecuteFilePath);
var process_list = Process.GetProcessesByName(processName);
if (process_list.Any())
process_list.ForEach(x =>
{
if (Path.GetFullPath(x.MainModule?.FileName ?? string.Empty) == Path.GetFullPath(SharedInfo.ASFExecuteFilePath))
{
x.Kill(); x.Dispose();
}
});
}
// HistoryTarget? historyTarget = ArchiSteamFarm.LogManager.Configuration.AllTargets.OfType<HistoryTarget>().FirstOrDefault();
private async Task<bool> CheckFileConsistence()
{
var isConsistence = false;
if (File.Exists(SharedInfo.ASFExecuteFilePath))
{
try
{
var versionInfo = FileVersionInfo.GetVersionInfo(SharedInfo.ASFExecuteFilePath);
// if (historyTarget != null)
// historyTarget.NewHistoryEntry += (object? sender, HistoryTarget.NewHistoryEntryArgs newHistoryEntryArgs) =>
// {
// OnConsoleWirteLine?.Invoke(newHistoryEntryArgs.Message);
// };
// }
var fileName = "ASF-win-x64.zip";
var downloadUrl = $"https://github.com/JustArchiNET/ArchiSteamFarm/releases/download/{versionInfo.FileVersion}/{fileName}";
var savePath = Path.Combine(Plugin.Instance.AppDataDirectory, $"ASF-{versionInfo.FileVersion}", fileName);
var destDir = Path.GetDirectoryName(savePath)!;
// public async Task<string?> ExecuteCommandAsync(string command)
// {
// Bot? targetBot = Bot.Bots?.OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).FirstOrDefault();
var online_asfPath = Path.Combine(destDir, Path.GetFileName(SharedInfo.ASFExecuteFilePath));
// ASF.ArchiLogger.LogGenericInfo(command);
if (!File.Exists(online_asfPath))
{
Directory.CreateDirectory(destDir);
if (await DownloadASFRelease(downloadUrl, savePath))
ZipFile.ExtractToDirectory(savePath, destDir);
}
if (File.Exists(online_asfPath) && CalculateFileHash(SharedInfo.ASFExecuteFilePath) == CalculateFileHash(online_asfPath))
isConsistence = true;
}
catch (Exception)
{
}
// if (targetBot == null)
// {
// ASF.ArchiLogger.LogGenericWarning(ASFStrings.ErrorNoBotsDefined);
// return null;
// }
}
return isConsistence;
// ASF.ArchiLogger.LogGenericInfo(ASFStrings.Executing);
string CalculateFileHash(string filePath)
{
using (var hasher = SHA256.Create())
using (var stream = new BufferedStream(File.OpenRead(filePath), 1200000))
{
byte[] hashBytes = hasher.ComputeHash(stream);
return BitConverter.ToString(hashBytes).Replace("-", "");
}
}
// ulong steamOwnerID = ASF.GlobalConfig?.SteamOwnerID ?? GlobalConfig.DefaultSteamOwnerID;
async Task<bool> DownloadASFRelease(string downloadUrl, string savePath)
{
try
{
HttpResponseMessage response = await _httpClient.GetAsync(downloadUrl);
// var access = targetBot.GetAccess(steamOwnerID);
// string? response = await targetBot.Commands.Response(access, command, steamOwnerID);
// Ensure a successful response before proceeding
response.EnsureSuccessStatusCode();
// if (!string.IsNullOrEmpty(response))
// ASF.ArchiLogger.LogGenericInfo(response);
// return response;
// }
using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
await response.Content.CopyToAsync(fileStream);
}
}
catch (Exception ex)
{
return false;
}
return true;
}
}
// public IReadOnlyDictionary<string, Bot>? GetReadOnlyAllBots()
// {
// var bots = Bot.Bots;
// //if (bots is not null)
// // foreach (var bot in bots.Values)
// // {
// // bot.AvatarUrl = GetAvatarUrl(bot);
// // }
// return bots;
// }
private void ExitHandler(object? sender, EventArgs eventArgs)
{
ASFService.Current.StopASFAsync().GetAwaiter().GetResult();
}
// public async void SaveBot(Bot bot)
// {
// //var bot = Bot.GetBot(botName);
// string filePath = Bot.GetFilePath(bot.BotName, Bot.EFileType.Config);
// bool result = await BotConfig.Write(filePath, bot.BotConfig).ConfigureAwait(false);
// }
private async void ReadOutPutData(object? obj)
{
using (StreamReader sr = ASFProcess.StandardOutput)
{
int readResult;
char ch;
StringBuilder sb = new();
var len = string.Empty;
// public GlobalConfig? GetGlobalConfig()
// {
// return ASF.GlobalConfig;
// }
while (true)
{
var readAsync = Task.Run(sr.Read);
// public async void SaveGlobalConfig(GlobalConfig config)
// {
// string filePath = ASF.GetFilePath(ASF.EFileType.Config);
// bool result = await GlobalConfig.Write(filePath, config).ConfigureAwait(false);
// if (result)
// {
// Toast.Show("SaveGlobalConfig " + result);
// }
// }
await Task.WhenAny(readAsync, Task.Delay(TimeSpan.FromSeconds(3)));
// string IPCRootUrl
// {
// get
// {
// string? value = null;
// var a = ArchiSteamFarm.IPC.ArchiKestrel.ServerAddresses;
// var loopback = IPAddress.Loopback.ToString();
// if (a != null)
// {
// value = a.FirstOrDefault(x => x.Contains(loopback) && x.StartsWith(String2.Prefix_HTTP))
// ?? a.FirstOrDefault(x => x.Contains(loopback))
// ?? a.FirstOrDefault();
// }
// if (string.IsNullOrWhiteSpace(value))
// {
// value = $"http://{loopback}:{CurrentIPCPortValue}";
// }
// return value;
// }
// }
if (!readAsync.IsCompleted)
{
if (sb.Length > 0)
{
len = sb.ToString();
OnConsoleWirteLine?.Invoke(len);
sb.Clear();
}
}
// public string GetIPCUrl()
// {
// var defaultUrl = IPCRootUrl;
// string absoluteConfigDirectory = Path.Combine(ASFPathHelper.AppDataDirectory, SharedInfo.ConfigDirectory);
// string customConfigPath = Path.Combine(absoluteConfigDirectory, SharedInfo.IPCConfigFile);
// if (File.Exists(customConfigPath))
// {
// var configRoot = new ConfigurationBuilder().SetBasePath(absoluteConfigDirectory).AddJsonFile(SharedInfo.IPCConfigFile, false, true).Build();
// var urlSection = configRoot.GetSection("Url").Value;
// try
// {
// var url = new Uri(urlSection!);
// if (IPAddress.Any.ToString() == url.Host)
// {
// return defaultUrl;
// }
// else
// {
// return url.AbsolutePath;
// }
// }
// catch
// {
// return defaultUrl;
// }
// }
// else
// {
// return defaultUrl;
// }
// }
if ((readResult = await readAsync) != -1)
{
ch = (char)readResult;
sb.Append(ch);
// public int CurrentIPCPortValue { get; set; }
// Note the following common line feed chars:
// \n - UNIX \r\n - DOS \r - Mac
if (ch == '\r' || ch == '\n')
{
readResult = sr.Read();
if (readResult != -1)
{
ch = (char)readResult;
if (ch == '\n')
{
sb.Append(ch);
len = sb.ToString();
OnConsoleWirteLine?.Invoke(len);
sb.Clear();
}
else
{
len = sb.ToString();
OnConsoleWirteLine?.Invoke(len);
sb.Clear();
sb.Append(ch);
}
}
}
}
}
}
}
#endregion
// //public static IEnumerable<HttpMessageHandler> GetAllHandlers()
// //{
// // // 动态更改运行中的代理设置,遍历 ASF 中的 HttpClientHandler 设置新的 Proxy
// // var asf_handler = ASF.WebBrowser?.HttpClientHandler;
// // if (asf_handler != null) yield return asf_handler;
// // var bots = Bot.BotsReadOnly?.Values;
// // if (bots != null)
// // {
// // foreach (var bot in bots)
// // {
// // var bot_handler = bot.ArchiWebHandler.WebBrowser.HttpClientHandler;
// // if (bot_handler != null) yield return bot_handler;
// // }
// // }
// //}
public async Task StopAsync()
{
StartTime = null;
ASF.IsReady &= false;
ReadLineTask?.TrySetResult("");
if (ASFProcess != null)
{
await Task.WhenAny(webApiService.ASF.Exit(), Task.Delay(TimeSpan.FromSeconds(3)));
ASFProcess.Kill();
ASFProcess.Dispose();
ASFProcess = null;
}
}
// string EncryptionKey
// {
// set => ArchiCryptoHelper.SetEncryptionKey(value);
// }
public async Task Restart()
{
Toast.Show(ToastIcon.Info, AppResources.ASF_Restarting, ToastLength.Short);
// const string ASF_CRYPTKEY = "ASF_CRYPTKEY";
// const string ASF_CRYPTKEY_DEF_VALUE = nameof(ArchiSteamFarm);
var s = ASFService.Current;
if (s.IsASFRuning)
{
await s.StopASFCoreAsync(false);
}
await s.InitASFCoreAsync(false);
// public async Task SetEncryptionKeyAsync()
// {
// var result = await TextBoxWindowViewModel.ShowDialogAsync(new TextBoxWindowViewModel
// {
// Title = AppResources.ASF_SetCryptKey,
// Placeholder = ASF_CRYPTKEY,
// InputType = TextBoxWindowViewModel.TextBoxInputType.Password,
// });
// var isUseDefaultCryptKey = string.IsNullOrEmpty(result) || result == ASF_CRYPTKEY_DEF_VALUE;
// if (!isUseDefaultCryptKey)
// {
// await ISecureStorage.Instance.SetAsync(ASF_CRYPTKEY, result);
// }
// else
// {
// await ISecureStorage.Instance.RemoveAsync(ASF_CRYPTKEY);
// result = ASF_CRYPTKEY_DEF_VALUE;
// }
// ArchiCryptoHelper.SetEncryptionKey(result!);
// }
Toast.Show(ToastIcon.Success, AppResources.ASF_Restarted, ToastLength.Short);
}
// /// <summary>
// /// 尝试读取已保存的自定义密钥并应用
// /// </summary>
// /// <returns></returns>
// static async Task ReadEncryptionKeyAsync()
// {
// if (!ArchiCryptoHelper.HasDefaultCryptKey)
// {
// // 当前运行中已设置了自定义密钥,则跳过
// return;
// }
// var result = await ISecureStorage.Instance.GetAsync(ASF_CRYPTKEY);
// if (!string.IsNullOrEmpty(result))
// {
// ArchiCryptoHelper.SetEncryptionKey(result);
// }
// }
//}
LogLevel MinimumLevel => IApplication.LoggerMinLevel;
//#endif
public async Task<string?> ExecuteCommandAsync(string command)
{
(var isSuccess, _) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return string.Empty;
var request = new CommandRequest(command);
var r = await webApiService.Command.CommandPost(request);
return r.IsSuccess ? r.Content : null;
}
public async Task<IReadOnlyDictionary<string, Bot>?> GetReadOnlyAllBots()
{
(var isSuccess, var rsp) = VerifyApiReady<IReadOnlyDictionary<string, Bot>?>();
if (!isSuccess) return rsp.Content;
var r = await webApiService.Bot.BotGet("asf");
var bots = r.Content;
//if (bots is not null)
// foreach (var bot in bots.Values)
// {
// bot.AvatarUrl = GetAvatarUrl(bot);
// }
return bots;
}
public async Task<bool> SaveBot(string botName, BotConfig botConfig)
{
(var isSuccess, _) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return isSuccess;
var request = new BotRequest() { BotConfig = botConfig };
var r = await webApiService.Bot.BotPost(botName, request);
return r.IsSuccess;
}
public async Task<GlobalConfig?> GetGlobalConfig()
{
(var isSuccess, _) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return null;
var r = await webApiService.ASF.Get();
return r.IsSuccess ? r.Content.GlobalConfig : null;
}
public async Task<bool> SaveGlobalConfig(GlobalConfig config)
{
(var isSuccess, _) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return isSuccess;
var request = new ASFRequest() { GlobalConfig = config };
var r = await webApiService.ASF.Post(request);
if (r.IsSuccess)
Toast.Show(ToastIcon.Info, "SaveGlobalConfig " + r.IsSuccess);
return r.IsSuccess;
}
string IPCRootUrl { get => $"http://{IPAddress.Loopback}:{CurrentIPCPortValue}"; }
public string GetIPCUrl()
{
var ipcUrl = new Uri(IPCRootUrl);
string absoluteConfigDirectory = Path.Combine(SharedInfo.HomeDirectory, SharedInfo.ConfigDirectory);
string customConfigPath = Path.Combine(absoluteConfigDirectory, SharedInfo.IPCConfigFile);
if (File.Exists(customConfigPath))
{
var jsonObj = JObject.Parse(File.ReadAllText(customConfigPath));
var urlSection = jsonObj.SelectToken("Kestrel.Endpoints.Http.Url")?.Value<string>();
try
{
var url = new Uri(urlSection!);
if (url.AbsoluteUri != ipcUrl.AbsoluteUri)
{
jsonObj["Kestrel"]!["Endpoints"]!["Http"]!["Url"] = ipcUrl.OriginalString;
File.WriteAllText(customConfigPath, jsonObj.ToString());
}
}
catch
{
}
}
else
{
var iPCConfig = new IPCConfig(ipcUrl);
File.WriteAllText(customConfigPath, System.Text.Json.JsonSerializer.Serialize(iPCConfig));
}
CurrentIPCPortValue = ipcUrl.Port;
return ipcUrl.OriginalString;
}
private int _currentIpcPortValue;
public int CurrentIPCPortValue
{
get
{
if (_currentIpcPortValue == default)
{
_currentIpcPortValue = ASFSettings.IPCPortId.Value;
if (ASFSettings.IPCPortOccupiedRandom.Value)
{
if (SocketHelper.IsUsePort(_currentIpcPortValue))
{
_currentIpcPortValue = SocketHelper.GetRandomUnusedPort(IPAddress.Loopback);
return _currentIpcPortValue;
}
}
}
return _currentIpcPortValue;
}
set
{
_currentIpcPortValue = value;
}
}
const string ASF_CRYPTKEY = "ASF_CRYPTKEY";
const string ASF_CRYPTKEY_DEF_VALUE = nameof(ArchiSteamFarm);
public string? EncryptionKey { get; set; }
public async Task SetEncryptionKeyAsync()
{
var result = await TextBoxWindowViewModel.ShowDialogAsync(new TextBoxWindowViewModel
{
Title = AppResources.ASF_SetCryptKey,
Placeholder = ASF_CRYPTKEY,
InputType = TextBoxWindowViewModel.TextBoxInputType.Password,
});
var isUseDefaultCryptKey = string.IsNullOrEmpty(result) || result == ASF_CRYPTKEY_DEF_VALUE;
if (!isUseDefaultCryptKey)
{
await ISecureStorage.Instance.SetAsync(ASF_CRYPTKEY, result);
}
else
{
await ISecureStorage.Instance.RemoveAsync(ASF_CRYPTKEY);
result = ASF_CRYPTKEY_DEF_VALUE;
}
EncryptionKey = result;
}
/// <summary>
/// 尝试读取已保存的自定义密钥并应用
/// </summary>
/// <returns></returns>
private async Task ReadEncryptionKeyAsync()
{
if (!string.IsNullOrEmpty(EncryptionKey))
{
// 当前运行中已设置了自定义密钥,则跳过
return;
}
var result = await ISecureStorage.Instance.GetAsync(ASF_CRYPTKEY);
if (!string.IsNullOrEmpty(result))
EncryptionKey = result;
}
public async Task<IApiRsp> BotResumeAsync(string botNames, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return rsp;
return await webApiService.Bot.ResumePost(botNames, cancellationToken);
}
public async Task<IApiRsp> BotPauseAsync(string botNames, BotPauseRequest request, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return rsp;
return await webApiService.Bot.PausePost(botNames, request, cancellationToken);
}
public async Task<IApiRsp> BotStopAsync(string botNames, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return rsp;
return await webApiService.Bot.StopPost(botNames, cancellationToken);
}
public async Task<IApiRsp> BotStartAsync(string botNames, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return rsp;
return await webApiService.Bot.StartPost(botNames, cancellationToken);
}
public async Task<IApiRsp<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>> BotGetUsedAndUnusedKeysAsync(string botNames, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>();
if (!isSuccess) return rsp;
return await webApiService.Bot.GamesToRedeemInBackgroundGet(botNames, cancellationToken);
}
public async Task<IApiRsp> BotRedeemKeyAsync(string botNames, BotGamesToRedeemInBackgroundRequest request, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return rsp;
return await webApiService.Bot.GamesToRedeemInBackgroundPost(botNames, request, cancellationToken);
}
public async Task<IApiRsp> BotResetRedeemedKeysRecordAsync(string botNames, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return rsp;
return await webApiService.Bot.GamesToRedeemInBackgroundDelete(botNames, cancellationToken);
}
public async Task<IApiRsp> BotDeleteAsync(string botNames, CancellationToken cancellationToken = default)
{
(var isSuccess, var rsp) = VerifyApiReady<GenericResponse>();
if (!isSuccess) return rsp;
return await webApiService.Bot.BotDelete(botNames, cancellationToken);
}
private (bool isSuccess, IApiRsp<TResponseBody?>? apiRsp) VerifyApiReady<TResponseBody>()
{
string? ipc_error = null;
if (!ASFService.Current.IsASFRuning)
{
ipc_error = BDStrings.ASF_RequirRunASF;
}
else if (!ASF.IsReady)
{
// IPC 未启动前无法获取正确的端口号,会导致拼接的 URL 值不正确
ipc_error = BDStrings.ASF_IPCIsReadyFalse;
}
if (ipc_error != null)
{
Toast.Show(ToastIcon.Error, ipc_error);
return (false, ApiRspHelper.Fail<TResponseBody>(ipc_error));
}
return (true, null);
}
}
#endif
@@ -1,3 +1,4 @@
using ArchiSteamFarm.Steam;
using static BD.WTTS.Services.IArchiSteamFarmWebApiService;
namespace BD.WTTS.Services.Implementation;
@@ -32,11 +33,11 @@ partial class ArchiSteamFarmWebApiServiceImpl // ApiControllers
return result!;
}
public async Task<IApiRsp> Hash(
public async Task<IApiRsp<string>> Hash(
ASFHashRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<ASFHashRequest, Void>(
var result = await SendAsync<ASFHashRequest, string>(
"Api/ASF/Hash",
HttpMethod.Post,
reqBody: request,
@@ -79,13 +80,12 @@ partial class ArchiSteamFarmWebApiServiceImpl // ApiControllers
}
public async Task<IApiRsp<string?>> Update(
UpdateRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<UpdateRequest, string>(
var result = await SendAsync<Void, string>(
"Api/ASF/Update",
HttpMethod.Post,
reqBody: request,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
@@ -96,11 +96,185 @@ partial class ArchiSteamFarmWebApiServiceImpl // ApiControllers
#region BotController
public async Task<IApiRsp> BotDelete(
string botNames,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<Void, Void>(
$"Api/Bot/{botNames}",
HttpMethod.Delete,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp<IReadOnlyDictionary<string, Bot>>> BotGet(
string botNames,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<Void, IReadOnlyDictionary<string, Bot>>(
$"Api/Bot/{botNames}",
HttpMethod.Get,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> BotPost(
string botNames,
BotRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<BotRequest, Void>(
$"Api/Bot/{botNames}",
HttpMethod.Post,
reqBody: request,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> GamesToRedeemInBackgroundDelete(
string botNames,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<Void, Void>(
$"Api/Bot/{botNames}/GamesToRedeemInBackground",
HttpMethod.Delete,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>> GamesToRedeemInBackgroundGet(
string botNames,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<Void, IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>(
$"Api/Bot/{botNames}/GamesToRedeemInBackground",
HttpMethod.Get,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> GamesToRedeemInBackgroundPost(
string botNames,
BotGamesToRedeemInBackgroundRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<BotGamesToRedeemInBackgroundRequest, Void>(
$"Api/Bot/{botNames}/GamesToRedeemInBackground",
HttpMethod.Post,
reqBody: request,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> InputPost(
string botNames,
BotInputRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<BotInputRequest, Void>(
$"Api/Bot/{botNames}/Input",
HttpMethod.Post,
reqBody: request,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> PausePost(
string botNames,
BotPauseRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<BotPauseRequest, Void>(
$"Api/Bot/{botNames}/Pause",
HttpMethod.Post,
reqBody: request,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> RedeemPost(
string botNames,
BotRedeemRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<BotRedeemRequest, Void>(
$"Api/Bot/{botNames}/Redeem",
HttpMethod.Post,
reqBody: request,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> RenamePost(
string botNames,
BotRenameRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<BotRenameRequest, Void>(
$"Api/Bot/{botNames}/Rename",
HttpMethod.Post,
reqBody: request,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> ResumePost(
string botNames,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<Void, Void>(
$"Api/Bot/{botNames}/Resume",
HttpMethod.Post,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> StartPost(
string botNames,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<Void, Void>(
$"Api/Bot/{botNames}/Start",
HttpMethod.Post,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
public async Task<IApiRsp> StopPost(
string botNames,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<Void, Void>(
$"Api/Bot/{botNames}/Stop",
HttpMethod.Post,
reqBody: default,
cancellationToken: cancellationToken);
return result!;
}
#endregion
public ICommandClient Command => this;
#region CommandController
public async Task<IApiRsp<string>> CommandPost(
CommandRequest request,
CancellationToken cancellationToken = default)
{
var result = await SendAsync<CommandRequest, string>(
$"Api/Command",
HttpMethod.Post,
reqBody: request,
cancellationToken: cancellationToken);
return result!;
}
#endregion
}
@@ -1,4 +1,3 @@
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
using Newtonsoft.Json;
namespace BD.WTTS.Services.Implementation;
@@ -12,7 +11,7 @@ partial class ArchiSteamFarmWebApiServiceImpl // JsonSerializer(Newtonsoft.Json)
// https://github.com/BeyondDimension/ArchiSteamFarm/blob/v5.4.9.3/ArchiSteamFarm/IPC/Startup.cs#L350-L364
// https://github.com/dotnet/aspnetcore/blob/v7.0.11/src/Mvc/Mvc.NewtonsoftJson/src/JsonSerializerSettingsProvider.cs
var settings = JsonSerializerSettingsProvider.CreateSerializerSettings();
var settings = new JsonSerializerSettings();
// Fix default contract resolver to use original names and not a camel case
settings.ContractResolver = new DefaultContractResolver();
@@ -23,13 +22,13 @@ partial class ArchiSteamFarmWebApiServiceImpl // JsonSerializer(Newtonsoft.Json)
HttpContent Serialize(object? value, Type? objectType = null)
{
var stream = new MemoryStream();
using var streamWriter = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true);
using var streamWriter = new StreamWriter(stream, new UTF8Encoding(false), leaveOpen: true);
using var jsonWriter = new JsonTextWriter(streamWriter);
serializer.Serialize(jsonWriter, value, objectType);
jsonWriter.Flush();
stream.Seek(0, SeekOrigin.Begin);
var content = new StreamContent(stream);
content.Headers.TryAddWithoutValidation(
"Content-Type",
"application/json;charset=utf-8");
content.Headers.TryAddWithoutValidation("Content-Type", "application/json;charset=utf-8");
return content;
}
@@ -3,11 +3,11 @@ namespace BD.WTTS.Services.Implementation;
partial class ArchiSteamFarmWebApiServiceImpl // SendAsync(HttpClient)
: GeneralHttpClientFactory
{
[Obsolete("TODO: 更改为设置项的默认值")]
const string DefaultBaseAddress = "http://localhost:1242";
[Obsolete("TODO: 更改为从设置项中读取")]
string BaseAddress => "";
string BaseAddress = string.Empty;
public void SetIPCUrl(string ipcUrl) => BaseAddress = ipcUrl;
HttpClient CreateClient()
{
@@ -24,7 +24,8 @@ partial class ArchiSteamFarmWebApiServiceImpl // SendAsync(HttpClient)
}
var client = CreateClient($"{TAG}_{baseAddressStr}", HttpHandlerCategory.Default);
client.BaseAddress = baseAddress;
if (client.BaseAddress == null || !client.BaseAddress.Equals(baseAddress))
client.BaseAddress = baseAddress;
return client;
}
@@ -35,11 +36,11 @@ partial class ArchiSteamFarmWebApiServiceImpl // SendAsync(HttpClient)
bool isShowResponseErrorMessage = true,
string? errorAppendText = null,
CancellationToken cancellationToken = default)
where TRequestBody : notnull
where TResponseBody : notnull
where TRequestBody : class
where TResponseBody : class
{
var result = await SendCoreAsync();
if (isShowResponseErrorMessage)
if (!result.IsSuccess && isShowResponseErrorMessage)
{
conn_helper.ShowResponseErrorMessage(requestUrl, result, errorAppendText);
}
@@ -57,9 +58,6 @@ partial class ArchiSteamFarmWebApiServiceImpl // SendAsync(HttpClient)
try
{
var reqContent = Serialize(reqBody, typeRequestBody);
#if DEBUG
var reqContentJsonStr = await reqContent.ReadAsStringAsync(cancellationToken);
#endif
req.Content = reqContent;
}
catch (Exception ex)
@@ -106,7 +104,7 @@ partial class ArchiSteamFarmWebApiServiceImpl // SendAsync(HttpClient)
}
}
struct Void
class Void
{
// https://github.com/dotnet/runtime/blob/v7.0.11/src/libraries/System.Private.CoreLib/src/System/Void.cs
}
@@ -1,98 +1,122 @@
//#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
//// ReSharper disable once CheckNamespace
// ReSharper disable once CheckNamespace
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Storage;
using ArchiSteamFarm.Storage;
//namespace BD.WTTS.Services;
namespace BD.WTTS.Services;
///// <summary>
///// ArchiSteamFarm 服务
///// </summary>
//public partial interface IArchiSteamFarmService : IArchiSteamFarmHelperService
//{
// static new IArchiSteamFarmService Instance => Ioc.Get<IArchiSteamFarmService>();
/// <summary>
/// ArchiSteamFarm 服务
/// </summary>
public partial interface IArchiSteamFarmService
{
static IArchiSteamFarmService Instance => Ioc.Get<IArchiSteamFarmService>();
// event Action<string>? OnConsoleWirteLine;
event Action<string>? OnConsoleWirteLine;
// TaskCompletionSource<string>? ReadLineTask { get; }
TaskCompletionSource<string>? ReadLineTask { get; }
// bool IsReadPasswordLine { get; }
bool IsReadPasswordLine { get; }
// DateTimeOffset? StartTime { get; }
Process? ASFProcess { get; }
// Version CurrentVersion { get; }
DateTimeOffset? StartTime { get; }
// /// <summary>
// /// 启动 ArchiSteamFarm
// /// </summary>
// /// <param name="args"></param>
// Task<bool> StartAsync(string[]? args = null);
Version CurrentVersion { get; }
// Task StopAsync();
/// <summary>
/// 控制台输入
/// </summary>
/// <param name="data"></param>
void ShellMessageInput(string data);
// /// <summary>
// /// 执行 ArchiSteamFarm 指令
// /// </summary>
// /// <param name="command"></param>
// Task<string?> ExecuteCommandAsync(string command);
/// <summary>
/// 启动 ArchiSteamFarm
/// </summary>
/// <param name="args"></param>
Task<bool> StartAsync(string[]? args = null);
// /// <summary>
// /// 获取 IPC 地址
// /// </summary>
// /// <returns></returns>
// string GetIPCUrl();
Task StopAsync();
// /// <summary>
// /// 获取 Bot 只读集合
// /// </summary>
// /// <returns></returns>
// IReadOnlyDictionary<string, Bot>? GetReadOnlyAllBots();
/// <summary>
/// 执行 ArchiSteamFarm 指令
/// </summary>
/// <param name="command"></param>
Task<string?> ExecuteCommandAsync(string command);
// GlobalConfig? GetGlobalConfig();
/// <summary>
/// 获取 IPC 地址
/// </summary>
/// <returns></returns>
string GetIPCUrl();
// async void CommandSubmit(string? command)
// {
// if (string.IsNullOrEmpty(command))
// return;
/// <summary>
/// 获取 Bot 只读集合
/// </summary>
/// <returns></returns>
Task<IReadOnlyDictionary<string, Bot>?> GetReadOnlyAllBots();
// if (ReadLineTask is null)
// {
// if (command[0] == '!')
// {
// command = command.Remove(0, 1);
// }
// await ExecuteCommandAsync(command);
// }
// else
// {
// ReadLineTask.TrySetResult(command);
// }
// }
Task<GlobalConfig?> GetGlobalConfig();
// int CurrentIPCPortValue { get; protected set; }
Task<bool> SaveBot(string botName, BotConfig botConfig);
// int IArchiSteamFarmHelperService.IPCPortValue
// {
// get
// {
// CurrentIPCPortValue = ASFSettings.IPCPortId.Value;
// if (CurrentIPCPortValue == default) CurrentIPCPortValue = ASFSettings.DefaultIPCPortIdValue;
// if (ASFSettings.IPCPortOccupiedRandom.Value)
// {
// if (SocketHelper.IsUsePort(CurrentIPCPortValue))
// {
// CurrentIPCPortValue = SocketHelper.GetRandomUnusedPort(IPAddress.Loopback);
// return CurrentIPCPortValue;
// }
// }
// return CurrentIPCPortValue;
// }
// }
Task<bool> SaveGlobalConfig(GlobalConfig config);
// /// <summary>
// /// 使用弹窗密码框输入自定义密钥并设置与保存
// /// </summary>
// /// <returns></returns>
// Task SetEncryptionKeyAsync();
//}
async void CommandSubmit(string? command)
{
if (string.IsNullOrEmpty(command))
return;
//#endif
if (ReadLineTask is null)
{
if (command[0] == '!')
{
command = command.Remove(0, 1);
}
await ExecuteCommandAsync(command);
}
else
{
ReadLineTask.TrySetResult(command);
}
}
int CurrentIPCPortValue { get; }
/// <summary>
/// 使用弹窗密码框输入自定义密钥并设置与保存
/// </summary>
/// <returns></returns>
Task SetEncryptionKeyAsync();
Task<IApiRsp> BotResumeAsync(string botNames,
CancellationToken cancellationToken = default);
Task<IApiRsp> BotPauseAsync(string botNames,
BotPauseRequest request,
CancellationToken cancellationToken = default);
Task<IApiRsp> BotStopAsync(string botNames,
CancellationToken cancellationToken = default);
Task<IApiRsp> BotStartAsync(string botNames,
CancellationToken cancellationToken = default);
Task<IApiRsp<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>> BotGetUsedAndUnusedKeysAsync(string botNames,
CancellationToken cancellationToken = default);
Task<IApiRsp> BotRedeemKeyAsync(string botNames,
BotGamesToRedeemInBackgroundRequest request,
CancellationToken cancellationToken = default);
Task<IApiRsp> BotResetRedeemedKeysRecordAsync(string botNames,
CancellationToken cancellationToken = default);
Task<IApiRsp> BotDeleteAsync(
string botNames,
CancellationToken cancellationToken = default);
}
#endif
@@ -1,3 +1,5 @@
using ArchiSteamFarm.Steam;
namespace BD.WTTS.Services;
public interface IArchiSteamFarmWebApiService
@@ -9,6 +11,8 @@ public interface IArchiSteamFarmWebApiService
// IApiRsp<T> = GenericResponse<T>
// T maybe null !!!
void SetIPCUrl(string ipcUrl);
IASFClient ASF { get; }
interface IASFClient
@@ -37,7 +41,7 @@ public interface IArchiSteamFarmWebApiService
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IApiRsp> Hash(
Task<IApiRsp<string>> Hash(
ASFHashRequest request,
CancellationToken cancellationToken = default);
@@ -74,7 +78,6 @@ public interface IArchiSteamFarmWebApiService
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IApiRsp<string?>> Update(
UpdateRequest request,
CancellationToken cancellationToken = default);
}
@@ -82,13 +85,121 @@ public interface IArchiSteamFarmWebApiService
interface IBotClient
{
/// <summary>
/// Deletes all files related to given bots.
/// </summary>
Task<IApiRsp> BotDelete(
string botNames,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetches common info related to given bots.
/// </summary>
Task<IApiRsp<IReadOnlyDictionary<string, Bot>>> BotGet(
string botNames,
CancellationToken cancellationToken = default);
/// <summary>
/// Updates bot config of given bot.
/// </summary>
Task<IApiRsp> BotPost(
string botNames,
BotRequest request,
CancellationToken cancellationToken = default);
/// <summary>
/// Removes BGR output files of given bots.
/// </summary>
Task<IApiRsp> GamesToRedeemInBackgroundDelete(
string botNames,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetches BGR output files of given bots.
/// </summary>
Task<IApiRsp<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>> GamesToRedeemInBackgroundGet(
string botNames,
CancellationToken cancellationToken = default);
/// <summary>
/// Adds keys to redeem using BGR to given bot.
/// </summary>
Task<IApiRsp> GamesToRedeemInBackgroundPost(
string botNames,
BotGamesToRedeemInBackgroundRequest request,
CancellationToken cancellationToken = default);
/// <summary>
/// Provides input value to given bot for next usage.
/// </summary>
Task<IApiRsp> InputPost(
string botNames,
BotInputRequest request,
CancellationToken cancellationToken = default);
/// <summary>
/// Pauses given bots.
/// </summary>
Task<IApiRsp> PausePost(
string botNames,
BotPauseRequest request,
CancellationToken cancellationToken = default);
/// <summary>
/// Redeems cd-keys on given bot.
/// </summary>
/// <remarks>
/// Response contains a map that maps each provided cd-key to its redeem result.
/// Redeem result can be a null value, this means that ASF didn't even attempt to send a request (e.g. because of bot not being connected to Steam network).
/// </remarks>
Task<IApiRsp> RedeemPost(
string botNames,
BotRedeemRequest request,
CancellationToken cancellationToken = default);
/// <summary>
/// Renames given bot along with all its related files.
/// </summary>
Task<IApiRsp> RenamePost(
string botNames,
BotRenameRequest request,
CancellationToken cancellationToken = default);
/// <summary>
/// Resumes given bots.
/// </summary>
Task<IApiRsp> ResumePost(
string botNames,
CancellationToken cancellationToken = default);
/// <summary>
/// Starts given bots.
/// </summary>
Task<IApiRsp> StartPost(
string botNames,
CancellationToken cancellationToken = default);
/// <summary>
/// Stops given bots.
/// </summary>
Task<IApiRsp> StopPost(
string botNames,
CancellationToken cancellationToken = default);
}
ICommandClient Command { get; }
interface ICommandClient
{
/// <summary>
/// Executes a command.
/// </summary>
/// <remarks>
/// This API endpoint is supposed to be entirely replaced by ASF actions available under /Api/ASF/{action} and /Api/Bot/{bot}/{action}.
/// You should use "given bot" commands when executing this endpoint, omitting targets of the command will cause the command to be executed on first defined bot
/// </remarks>
Task<IApiRsp<string>> CommandPost(
CommandRequest request,
CancellationToken cancellationToken = default);
}
}
@@ -1,199 +1,253 @@
//#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
//using AppResources = BD.WTTS.Client.Resources.Strings;
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Storage;
using ArchiSteamFarm.Storage;
using Newtonsoft.Json;
using AppResources = BD.WTTS.Client.Resources.Strings;
//// ReSharper disable once CheckNamespace
//namespace BD.WTTS.Services;
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services;
//public sealed class ASFService : ReactiveObject
//{
// static ASFService? mCurrent;
public sealed class ASFService : ReactiveObject
{
static ASFService? mCurrent;
// public static ASFService Current => mCurrent ?? new();
public static ASFService Current => mCurrent ?? new();
// readonly IArchiSteamFarmService archiSteamFarmService = IArchiSteamFarmService.Instance;
readonly IArchiSteamFarmService archiSteamFarmService = IArchiSteamFarmService.Instance;
// string? _IPCUrl;
string? _IPCUrl;
// public string? IPCUrl
// {
// get => _IPCUrl;
// set => this.RaiseAndSetIfChanged(ref _IPCUrl, value);
// }
public string? IPCUrl
{
get => _IPCUrl;
set => this.RaiseAndSetIfChanged(ref _IPCUrl, value);
}
// string? _ConsoleLogText;
string? _ConsoleLogText;
// public string? ConsoleLogText
// {
// get => _ConsoleLogText;
// set => this.RaiseAndSetIfChanged(ref _ConsoleLogText, value);
// }
public string? ConsoleLogText
{
get => _ConsoleLogText;
set => this.RaiseAndSetIfChanged(ref _ConsoleLogText, value);
}
// public IConsoleBuilder ConsoleLogBuilder { get; } = new ConsoleBuilder();
string? _ConsoleLogInput;
// public SourceCache<Bot, string> SteamBotsSourceList;
public string? ConsoleLogInput
{
get => _ConsoleLogInput;
set => this.RaiseAndSetIfChanged(ref _ConsoleLogInput, value);
}
// public bool IsASFRuning => archiSteamFarmService.StartTime != null;
public IConsoleBuilder ConsoleLogBuilder { get; } = new ConsoleBuilder();
// GlobalConfig? _GlobalConfig;
public SourceCache<Bot, string> SteamBotsSourceList;
// public GlobalConfig? GlobalConfig
// {
// get => _GlobalConfig;
// set => this.RaiseAndSetIfChanged(ref _GlobalConfig, value);
// }
public bool IsASFRuning => archiSteamFarmService.StartTime != null;
// private ASFService()
// {
// mCurrent = this;
GlobalConfig? _GlobalConfig;
// SteamBotsSourceList = new SourceCache<Bot, string>(t => t.BotName);
public GlobalConfig? GlobalConfig
{
get => _GlobalConfig;
set => this.RaiseAndSetIfChanged(ref _GlobalConfig, value);
}
// archiSteamFarmService.OnConsoleWirteLine += OnConsoleWirteLine;
private ASFService()
{
mCurrent = this;
// ASFSettings.ConsoleMaxLine.Subscribe(x =>
// {
// var line = x;
// if (x < ASFSettings.MinRangeConsoleMaxLine) line = ASFSettings.MinRangeConsoleMaxLine;
// if (x > ASFSettings.MaxRangeConsoleMaxLine) line = ASFSettings.MaxRangeConsoleMaxLine;
// ConsoleLogBuilder.MaxLine = line;
// });
// }
SteamBotsSourceList = new SourceCache<Bot, string>(t => t.BotName);
// void OnConsoleWirteLine(string message)
// {
// MainThread2.InvokeOnMainThreadAsync(() =>
// {
// ConsoleLogBuilder.AppendLine(message);
// var text = ConsoleLogBuilder.ToString();
// ConsoleLogText = text;
// });
// }
archiSteamFarmService.OnConsoleWirteLine += OnConsoleWirteLine;
// /// <summary>
// /// 是否正在启动或停止中
// /// </summary>
// public bool IsASFRunOrStoping { get; private set; }
ASFSettings.ConsoleMaxLine.Subscribe(x =>
{
var line = x;
if (x < ASFSettings.MinRangeConsoleMaxLine) line = ASFSettings.MinRangeConsoleMaxLine;
if (x > ASFSettings.MaxRangeConsoleMaxLine) line = ASFSettings.MaxRangeConsoleMaxLine;
ConsoleLogBuilder.MaxLine = line;
});
}
// public Task InitASFAsync() => InitASFCoreAsync(true);
void OnConsoleWirteLine(string message)
{
MainThread2.InvokeOnMainThreadAsync(() =>
{
ConsoleLogBuilder.AppendLine(message);
var text = ConsoleLogBuilder.ToString();
ConsoleLogText = text;
});
}
// public async Task InitASFCoreAsync(bool showToast)
// {
// if (IsASFRunOrStoping) return;
public void ShellMessageInput()
{
if (string.IsNullOrEmpty(ConsoleLogInput))
{
return;
}
archiSteamFarmService.ShellMessageInput(ConsoleLogInput);
}
// if (showToast) Toast.Show(AppResources.ASF_Starting, ToastLength.Short);
/// <summary>
/// 是否正在启动或停止中
/// </summary>
public bool IsASFRunOrStoping { get; private set; }
// IsASFRunOrStoping = true;
public Task InitASFAsync() => InitASFCoreAsync(true);
// //if (ASF.GlobalConfig?.SteamOwnerID is null or 0)
// //{
// // Toast.Show(AppResources.ASF_Starting, ToastLength.Long);
// // return;
// //}
public async Task InitASFCoreAsync(bool showToast)
{
if (IsASFRunOrStoping) return;
// var isOk = await archiSteamFarmService.StartAsync();
// if (!isOk)
// {
// if (showToast) Toast.Show(AppResources.ASF_Stoped, ToastLength.Short);
// return;
// }
if (showToast) Toast.Show(ToastIcon.Info, AppResources.ASF_Starting, ToastLength.Short);
// RefreshBots();
IsASFRunOrStoping = true;
// IPCUrl = archiSteamFarmService.GetIPCUrl();
var isOk = await archiSteamFarmService.StartAsync();
if (!isOk)
{
if (showToast) Toast.Show(ToastIcon.Error, AppResources.ASF_Stoped, ToastLength.Short);
IsASFRunOrStoping = false;
return;
}
// MainThread2.BeginInvokeOnMainThread(() =>
// {
// this.RaisePropertyChanged(nameof(IsASFRuning));
// });
RefreshBots();
// IsASFRunOrStoping = false;
IPCUrl = archiSteamFarmService.GetIPCUrl();
// if (showToast) Toast.Show(AppResources.ASF_Started, ToastLength.Short);
// }
MainThread2.BeginInvokeOnMainThread(() =>
{
this.RaisePropertyChanged(nameof(IsASFRuning));
});
// public Task StopASFAsync() => StopASFCoreAsync(true);
IsASFRunOrStoping = false;
// public async Task StopASFCoreAsync(bool showToast)
// {
// if (IsASFRunOrStoping) return;
if (showToast) Toast.Show(ToastIcon.Success, AppResources.ASF_Started, ToastLength.Short);
}
// if (showToast) Toast.Show(AppResources.ASF_Stoping, ToastLength.Short);
public Task StopASFAsync() => StopASFCoreAsync(true);
// IsASFRunOrStoping = true;
public async Task StopASFCoreAsync(bool showToast)
{
if (IsASFRunOrStoping) return;
// await archiSteamFarmService.StopAsync();
if (showToast) Toast.Show(ToastIcon.Info, AppResources.ASF_Stoping, ToastLength.Short);
// MainThread2.BeginInvokeOnMainThread(() =>
// {
// this.RaisePropertyChanged(nameof(IsASFRuning));
// });
IsASFRunOrStoping = true;
// IsASFRunOrStoping = false;
await archiSteamFarmService.StopAsync();
// if (showToast) Toast.Show(AppResources.ASF_Stoped, ToastLength.Short);
// }
MainThread2.BeginInvokeOnMainThread(() =>
{
this.RaisePropertyChanged(nameof(IsASFRuning));
});
// public void RefreshBots()
// {
// var bots = archiSteamFarmService.GetReadOnlyAllBots();
// if (bots.Any_Nullable())
// {
// SteamBotsSourceList.AddOrUpdate(bots!.Values);
// }
// }
IsASFRunOrStoping = false;
// public void RefreshConfig()
// {
// GlobalConfig = archiSteamFarmService.GetGlobalConfig();
// }
if (showToast) Toast.Show(ToastIcon.Info, AppResources.ASF_Stoped, ToastLength.Short);
}
// public async void ImportBotFiles(IEnumerable<string>? files) => await ImportJsonFileAsync(files, allowBot: true, allowGlobal: false);
public async void RefreshBots()
{
var bots = await archiSteamFarmService.GetReadOnlyAllBots();
if (bots.Any_Nullable())
{
SteamBotsSourceList.AddOrUpdate(bots!.Values);
}
}
// public async void ImportGlobalFiles(string? file) => await ImportJsonFileAsync(new[] { file }, allowBot: false, allowGlobal: true);
public async void RefreshConfig()
{
GlobalConfig = await archiSteamFarmService.GetGlobalConfig();
}
// async Task ImportJsonFileAsync(IEnumerable<string?>? files, bool allowBot, bool allowGlobal)
// {
// if (files == null) return;
// var num = 0;
// foreach (var filename in files)
// {
// if (string.IsNullOrWhiteSpace(filename)) continue;
// var file = new FileInfo(filename);
// if (file.Exists && string.Equals(file.Extension, SharedInfo.JsonConfigExtension, StringComparison.OrdinalIgnoreCase))
// {
// object? config;
// if (file.Name != SharedInfo.GlobalConfigFileName)
// {
// if (!allowBot) continue;
// var bot = await BotConfig.Load(file.FullName).ConfigureAwait(false);
// config = bot.BotConfig;
// }
// else
// {
// if (!allowGlobal) continue;
// var g = await GlobalConfig.Load(file.FullName).ConfigureAwait(false);
// config = g.GlobalConfig;
// }
// if (config != null)
// {
// try
// {
// file.CopyTo(Path.Combine(SharedInfo.ConfigDirectory, file.Name), true);
// num++;
// }
// catch (Exception ex)
// {
// Log.Error(nameof(ASFService), ex, nameof(ImportBotFiles));
// continue;
// }
// if (allowGlobal && config is GlobalConfig g)
// {
// GlobalConfig = ASF.GlobalConfig = g;
// }
// }
// }
// }
// Toast.Show(string.Format(AppResources.LocalAuth_ImportSuccessTip_, num));
// }
//}
//#endif
public async void ImportBotFiles(IEnumerable<string>? files) => await ImportJsonFileAsync(files, allowBot: true, allowGlobal: false);
public async void ImportGlobalFiles(string? file) => await ImportJsonFileAsync(new[] { file }, allowBot: false, allowGlobal: true);
async Task ImportJsonFileAsync(IEnumerable<string?>? files, bool allowBot, bool allowGlobal)
{
if (files == null) return;
var num = 0;
foreach (var filename in files)
{
if (string.IsNullOrWhiteSpace(filename)) continue;
var file = new FileInfo(filename);
if (file.Exists && string.Equals(file.Extension, SharedInfo.JsonConfigExtension, StringComparison.OrdinalIgnoreCase))
{
object? config;
string json;
try
{
json = await File.ReadAllTextAsync(file.FullName).ConfigureAwait(false);
if (string.IsNullOrEmpty(json))
{
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
return;
}
if (file.Name != SharedInfo.GlobalConfigFileName)
{
if (!allowBot) continue;
var botConfig = JsonConvert.DeserializeObject<BotConfig>(json);
if (botConfig == null)
{
ASF.ArchiLogger.LogNullError(botConfig);
return;
}
if (await archiSteamFarmService.SaveBot(Path.GetFileNameWithoutExtension(file.FullName), botConfig))
config = botConfig;
else
return;
}
else
{
if (!allowGlobal) continue;
var globalConfig = JsonConvert.DeserializeObject<GlobalConfig>(json);
if (globalConfig == null)
{
ASF.ArchiLogger.LogNullError(globalConfig);
return;
}
if (await archiSteamFarmService.SaveGlobalConfig(globalConfig))
config = globalConfig;
else
return;
}
}
catch (Exception e)
{
ASF.ArchiLogger.LogGenericException(e);
return;
}
if (config != null)
{
try
{
file.CopyTo(Path.Combine(SharedInfo.HomeDirectory, SharedInfo.ConfigDirectory, file.Name), true);
num++;
}
catch (Exception ex)
{
Log.Error(nameof(ASFService), ex, nameof(ImportBotFiles));
continue;
}
if (allowGlobal && config is GlobalConfig g)
{
GlobalConfig = g;
}
}
}
}
Toast.Show(ToastIcon.Success, string.Format(AppResources.LocalAuth_ImportSuccessTip_, num));
}
}
#endif
@@ -1,19 +1,73 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Settings;
public sealed partial class ASFSettings : SettingsHost2<ASFSettings>
[JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyProperties = true)]
[JsonSerializable(typeof(ASFSettings_))]
internal partial class ASFSettingsContext : JsonSerializerContext
{
static ASFSettingsContext? instance;
public static ASFSettingsContext Instance
=> instance ??= new ASFSettingsContext(ISettings.GetDefaultOptions());
}
[MPObj, MP2Obj(SerializeLayout.Explicit)]
public sealed partial class ASFSettings_ : IASFSettings, ISettings, ISettings<ASFSettings_>
{
public const string Name = nameof(ASFSettings);
static string ISettings.Name => Name;
static JsonSerializerContext ISettings.JsonSerializerContext
=> ASFSettingsContext.Instance;
static JsonTypeInfo ISettings.JsonTypeInfo
=> ASFSettingsContext.Instance.ASFSettings_;
static JsonTypeInfo<ASFSettings_> ISettings<ASFSettings_>.JsonTypeInfo
=> ASFSettingsContext.Instance.ASFSettings_;
[MPKey(0), MP2Key(0), JsonPropertyOrder(0)]
public string ArchiSteamFarmExePath { get; set; } = string.Empty;
[MPKey(1), MP2Key(1), JsonPropertyOrder(1)]
public bool AutoRunArchiSteamFarm { get; set; }
[MPKey(2), MP2Key(2), JsonPropertyOrder(2)]
public int ConsoleMaxLine { get; set; } = IASFSettings.DefaultConsoleMaxLine;
[MPKey(3), MP2Key(3), JsonPropertyOrder(3)]
public int ConsoleFontSize { get; set; } = IASFSettings.DefaultConsoleFontSize;
[MPKey(4), MP2Key(4), JsonPropertyOrder(4)]
public int IPCPortId { get; set; } = IASFSettings.DefaultIPCPortIdValue;
[MPKey(5), MP2Key(5), JsonPropertyOrder(5)]
public bool IPCPortOccupiedRandom { get; set; } = true;
[MPKey(6), MP2Key(6), JsonPropertyOrder(6)]
public bool CheckArchiSteamFarmExe { get; set; }
}
public sealed partial class ASFSettings
{
/// <summary>
/// ASF路径
/// </summary>
public static SerializableProperty<string> ArchiSteamFarmExePath { get; }
= GetProperty(defaultValue: string.Empty);
public static SettingsProperty<string, ASFSettings_> ArchiSteamFarmExePath { get; }
= new(string.Empty);
/// <summary>
/// 程序启动时自动运行ASF
/// </summary>
public static SerializableProperty<bool> AutoRunArchiSteamFarm { get; }
= GetProperty(defaultValue: false);
public static SettingsStructProperty<bool, ASFSettings_> AutoRunArchiSteamFarm { get; }
= new(false);
/// <summary>
/// 检查文件安全性
/// </summary>
public static SettingsStructProperty<bool, ASFSettings_> CheckArchiSteamFarmExe { get; }
= new(false);
#region ConsoleMaxLine
@@ -32,8 +86,8 @@ public sealed partial class ASFSettings : SettingsHost2<ASFSettings>
/// </summary>
public const int MaxRangeConsoleMaxLine = 5000;
public static SerializableProperty<int> ConsoleMaxLine { get; }
= GetProperty(defaultValue: DefaultConsoleMaxLine);
public static SettingsStructProperty<int, ASFSettings_> ConsoleMaxLine { get; }
= new(DefaultConsoleMaxLine);
#endregion
@@ -44,20 +98,20 @@ public sealed partial class ASFSettings : SettingsHost2<ASFSettings>
/// <summary>
/// 控制台字体大小,默认值 14,Android 上单位为 sp
/// </summary>
public static SerializableProperty<int> ConsoleFontSize { get; }
= GetProperty(defaultValue: DefaultConsoleFontSize);
public static SettingsStructProperty<int, ASFSettings_> ConsoleFontSize { get; }
= new(DefaultConsoleFontSize);
public const int DefaultIPCPortIdValue = 6242;
/// <summary>
/// IPC 端口号,默认值为 <see cref="DefaultIPCPortIdValue"/>
/// </summary>
public static SerializableProperty<int> IPCPortId { get; }
= GetProperty(defaultValue: DefaultIPCPortIdValue);
public static SettingsStructProperty<int, ASFSettings_> IPCPortId { get; }
= new(DefaultIPCPortIdValue);
/// <summary>
/// IPC 端口号被占用时是否随机一个未使用的端口号,默认值 <see langword="true"/>
/// </summary>
public static SerializableProperty<bool> IPCPortOccupiedRandom { get; }
= GetProperty(defaultValue: true);
public static SettingsStructProperty<bool, ASFSettings_> IPCPortOccupiedRandom { get; }
= new(true);
}
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BD.WTTS.Settings.Abstractions;
public partial interface IASFSettings
{
static IASFSettings? Instance
=> Ioc.Get_Nullable<IOptionsMonitor<IASFSettings>>()?.CurrentValue;
string ArchiSteamFarmExePath { get; set; }
bool AutoRunArchiSteamFarm { get; set; }
bool CheckArchiSteamFarmExe { get; set; }
#region ConsoleMaxLine
/// <summary>
/// 控制台默认最大行数
/// </summary>
const int DefaultConsoleMaxLine = 200;
/// <summary>
/// 控制台默认最大行数范围最小值
/// </summary>
const int MinRangeConsoleMaxLine = DefaultConsoleMaxLine;
/// <summary>
/// 控制台默认最大行数范围最大值
/// </summary>
const int MaxRangeConsoleMaxLine = 5000;
int ConsoleMaxLine { get; set; }
#endregion
const int DefaultConsoleFontSize = 14;
const int MinRangeConsoleFontSize = 8;
const int MaxRangeConsoleFontSize = 24;
int ConsoleFontSize { get; set; }
const int DefaultIPCPortIdValue = 6242;
int IPCPortId { get; set; }
bool IPCPortOccupiedRandom { get; set; }
}
@@ -0,0 +1,293 @@
#if !EXCLUDE_ASF
using ArchiSteamFarm.Steam;
using System.Collections.Specialized;
using Avalonia.Automation;
using BD.SteamClient.Models;
using BD.SteamClient.Models.Idle;
using BD.SteamClient.Services;
using BD.WTTS.UI.Views.Pages;
using System.Linq;
using ArchiSteamFarm.Core;
namespace BD.WTTS.UI.ViewModels;
public sealed partial class ArchiSteamFarmPlusPageViewModel : ViewModelBase
{
private readonly IArchiSteamFarmService asfService = IArchiSteamFarmService.Instance;
public ArchiSteamFarmPlusPageViewModel()
{
SelectBotFiles = ReactiveCommand.CreateFromTask(SelectBotFiles_Click);
SelectGlobalFiles = ReactiveCommand.CreateFromTask(SelectGlobalFiles_Click);
SelectASFExePath = ReactiveCommand.CreateFromTask(SelectASFProgramLocation);
OpenWebUIConsole = ReactiveCommand.Create(() =>
{
OpenBrowser(null);
});
OpenASFBrowser = ReactiveCommand.Create<string>(OpenBrowser);
ShellMessageInput = ReactiveCommand.Create(ShellMessageInput_Click);
RunOrStop = ReactiveCommand.Create(RunOrStopASF);
AddBot = ReactiveCommand.Create(ShowAddBotWindow);
RefreshBots = ReactiveCommand.Create(ASFService.Current.RefreshBots);
OpenASFFolder = ReactiveCommand.Create<string>(OpenFolder);
SetEncryptionKey = ReactiveCommand.Create(SetEncryptionKey_Click);
ASFService.Current.WhenValueChanged(x => x.IsASFRuning)
.Subscribe(x =>
{
RunOrStopText = x ? BDStrings.ASF_Stop : BDStrings.ASF_Start;
});
ASFService.Current.SteamBotsSourceList
.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Sort(SortExpressionComparer<Bot>.Descending(x => x.BotName))
.Bind(out _SteamBots)
.Subscribe();
//if (IApplication.IsDesktopPlatform)
//{
// ConsoleSelectFont = R.Fonts.FirstOrDefault(x => x.Value == ASFSettings.ConsoleFontName.Value);
// this.WhenValueChanged(x => x.ConsoleSelectFont, false)
// .Subscribe(x => ASFSettings.ConsoleFontName.Value = x.Value);
//}
}
public override void Activation()
{
base.Activation();
if (ASFSettings.AutoRunArchiSteamFarm.Value && !string.IsNullOrEmpty(ASFSettings.ArchiSteamFarmExePath.Value))
StartOrStopASF_Click();
}
public async Task SelectBotFiles_Click()
{
FilePickerFileType? fileTypes;
if (IApplication.IsDesktop())
{
fileTypes = new ValueTuple<string, string[]>[]
{
("Json Files", new[] { FileEx.JSON, }),
//("All Files", new[] { "*", }),
};
}
else if (OperatingSystem2.IsAndroid())
{
fileTypes = new[] { MediaTypeNames.JSON };
}
else
{
fileTypes = null;
}
await FilePicker2.PickMultipleAsync(ASFService.Current.ImportBotFiles, fileTypes);
}
public async Task SelectGlobalFiles_Click()
{
FilePickerFileType? fileTypes;
if (IApplication.IsDesktop())
{
fileTypes = new ValueTuple<string, string[]>[]
{
("Json Files", new[] { FileEx.JSON, }),
//("All Files", new[] { "*", }),
};
}
else if (OperatingSystem2.IsAndroid())
{
fileTypes = new[] { MediaTypeNames.JSON };
}
else
{
fileTypes = null;
}
await FilePicker2.PickAsync(ASFService.Current.ImportGlobalFiles, fileTypes);
}
public async Task SelectASFProgramLocation()
{
AvaloniaFilePickerFileTypeFilter fileTypes = new AvaloniaFilePickerFileTypeFilter.Item[] {
new("ArchiSteamFarm") {
Patterns = new[] { "ArchiSteamFarm.exe", },
//MimeTypes =
//AppleUniformTypeIdentifiers =
},
};
await FilePicker2.PickAsync((path) =>
{
if (!string.IsNullOrEmpty(path))
ASFSettings.ArchiSteamFarmExePath.Value = path;
}, fileTypes);
}
public static void StartOrStopASF_Click(bool? startOrStop = null) => Task.Run(async () =>
{
var s = ASFService.Current;
if (!s.IsASFRuning)
{
if (!startOrStop.HasValue || startOrStop.Value)
await s.InitASFAsync();
}
else
{
if (!startOrStop.HasValue || !startOrStop.Value)
await s.StopASFAsync();
}
});
public void RunOrStopASF() => StartOrStopASF_Click();
public async void ShowAddBotWindow()
{
await IWindowManager.Instance.ShowAsync(AppEndPoint.ASF_AddBot, resizeMode: ResizeMode.CanResize);
}
public async void PauseOrResumeBotFarming(Bot bot)
{
(bool success, string message) result;
IApiRsp apiRsp = null;
if (bot.CardsFarmer.Paused)
{
apiRsp = await asfService.BotResumeAsync(bot.BotName);
}
else
{
var request = new BotPauseRequest() { Permanent = true };
apiRsp = await asfService.BotPauseAsync(bot.BotName, request);
}
result = (apiRsp.IsSuccess, apiRsp.Message);
ASFService.Current.SteamBotsSourceList.AddOrUpdate(bot);
if (result.success)
Toast.Show(ToastIcon.Success, result.message);
else
Toast.Show(ToastIcon.Error, string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, result.message));
}
public async void EnableOrDisableBot(Bot bot)
{
(bool success, string message) result;
IApiRsp apiRsp = null;
if (bot.KeepRunning)
apiRsp = await asfService.BotStopAsync(bot.BotName);
else
apiRsp = await asfService.BotStartAsync(bot.BotName);
result = (apiRsp.IsSuccess, apiRsp.Message);
if (!result.success)
Toast.Show(ToastIcon.Error, string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, result.message));
ASFService.Current.SteamBotsSourceList.AddOrUpdate(bot);
}
public async Task<(IReadOnlyDictionary<string, string>? UnusedKeys, IReadOnlyDictionary<string, string>? UsedKeys)> GetUsedAndUnusedKeys(Bot bot)
{
var r = await asfService.BotGetUsedAndUnusedKeysAsync(bot.BotName);
if (r.IsSuccess && r.Content.TryGetValue(bot.BotName, out var response))
return (response.UnusedKeys, response.UsedKeys);
return default;
}
public async void RedeemKeyBot(Bot bot, IOrderedDictionary keys)
{
var request = new BotGamesToRedeemInBackgroundRequest() { GamesToRedeemInBackground = (OrderedDictionary)keys };
await asfService.BotRedeemKeyAsync(bot.BotName, request);
}
public async Task<bool> ResetBotRedeemedKeysRecord(Bot bot)
{
var r = await asfService.BotResetRedeemedKeysRecordAsync(bot.BotName);
return r.IsSuccess;
}
public async void GoToBotSettings(Bot bot)
{
await Browser2.OpenAsync(IPCUrl + "/bot/" + bot.BotName + "/config", BrowserLaunchMode.External);
}
public void EditBotFile(Bot bot)
{
var filePath = Path.Combine(SharedInfo.HomeDirectory, SharedInfo.ConfigDirectory, $"{bot.BotName}{SharedInfo.JsonConfigExtension}");
IPlatformService.Instance.OpenFileByTextReader(filePath);
}
public async void DeleteBot(Bot bot)
{
var s = await MessageBox.ShowAsync(BDStrings.ASF_DeleteBotTip, button: MessageBox.Button.OKCancel);
if (s == MessageBox.Result.OK)
{
var result = await asfService.BotDeleteAsync(bot.BotName);
if (result.IsSuccess)
{
ASFService.Current.SteamBotsSourceList.Remove(bot);
Toast.Show(ToastIcon.Success, BDStrings.ASF_DeleteBotSuccess);
}
}
}
public void OpenFolder(string tag)
{
if (!Enum.TryParse<EPathFolder>(tag, true, out var folderASFPath)) return;
var folderASFPathValue = folderASFPath.GetFolderPath();
IPlatformService.Instance.OpenFolder(folderASFPathValue);
}
async void OpenBrowserCore(ActionItem tag)
{
var url = tag switch
{
ActionItem.Repo => "https://github.com/JustArchiNET/ArchiSteamFarm",
ActionItem.Wiki => "https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Home-zh-CN",
ActionItem.ConfigGenerator => "https://justarchinet.github.io/ASF-WebConfigGenerator",
ActionItem.WebConfig => IPCUrl + "/asf-config",
ActionItem.WebAddBot => IPCUrl + "/bot/new",
_ => IPCUrl,
};
if (url.StartsWith(IPCUrl))
{
string? ipc_error = null;
if (!ASFService.Current.IsASFRuning)
{
ipc_error = BDStrings.ASF_RequirRunASF;
}
else if (!ASF.IsReady)
{
// IPC 未启动前无法获取正确的端口号,会导致拼接的 URL 值不正确
ipc_error = BDStrings.ASF_IPCIsReadyFalse;
}
if (ipc_error != null)
{
Toast.Show(ToastIcon.Error, ipc_error);
return;
}
}
await Browser2.OpenAsync(url, BrowserLaunchMode.External);
}
public void OpenBrowser(string? tag)
{
var tag_ = Enum.TryParse<ActionItem>(tag, out var @enum) ? @enum : default;
OpenBrowserCore(tag_);
}
public async void SetEncryptionKey_Click() => await asfService.SetEncryptionKeyAsync();
public void ShellMessageInput_Click() => ASFService.Current.ShellMessageInput();
public string IPCUrl => asfService.GetIPCUrl();
}
#endif
@@ -0,0 +1,82 @@
using ArchiSteamFarm.Steam;
namespace BD.WTTS.UI.ViewModels;
public sealed partial class ArchiSteamFarmPlusPageViewModel
{
/// <summary>
/// 启动或停止 ASF 文本展示
/// </summary>
[Reactive]
public string? RunOrStopText { get; set; }
/// <summary>
/// ASF bots
/// </summary>
private readonly ReadOnlyObservableCollection<Bot> _SteamBots;
/// <summary>
/// ASF bots
/// </summary>
public ReadOnlyObservableCollection<Bot> SteamBots => _SteamBots;
/// <summary>
/// 通过配置文件导入 ASF Bot
/// </summary>
public ICommand SelectBotFiles { get; }
/// <summary>
/// 通过配置文件导入 ASF 全局配置
/// </summary>
public ICommand SelectGlobalFiles { get; }
/// <summary>
/// 选择 ASF 可执行文件路径
/// </summary>
public ICommand SelectASFExePath { get; }
/// <summary>
/// 启动或停止
/// </summary>
public ICommand RunOrStop { get; }
/// <summary>
/// 添加 ASF Bot
/// </summary>
public ICommand AddBot { get; }
/// <summary>
/// 刷新 ASF Bots
/// </summary>
public ICommand RefreshBots { get; }
/// <summary>
/// 打开 ASF WEBUI 控制台
/// </summary>
public ICommand OpenWebUIConsole { get; }
/// <summary>
/// 打开 ASF 文件夹
/// </summary>
public ICommand OpenASFFolder { get; }
/// <summary>
/// 设置 ASF 加密密钥
/// </summary>
public ICommand SetEncryptionKey { get; }
/// <summary>
/// 打开 ASF 相关网址
/// </summary>
public ICommand OpenASFBrowser { get; }
public ICommand ShellMessageInput { get; }
private bool _IsRedeemKeyDialogOpen;
public bool IsRedeemKeyDialogOpen
{
get => _IsRedeemKeyDialogOpen;
set => this.RaiseAndSetIfChanged(ref _IsRedeemKeyDialogOpen, value);
}
}
@@ -0,0 +1,295 @@
<spp:PageBase
x:Class="BD.WTTS.UI.Views.Pages.ArchiSteamFarmPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ms="https://steampp.net/settings"
xmlns:s="https://steampp.net/services"
xmlns:spp="https://steampp.net/ui"
xmlns:ui="using:FluentAvalonia.UI.Controls"
Title="{Binding Name, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
d:DesignHeight="450"
d:DesignWidth="800"
x:CompileBindings="True"
x:DataType="spp:ArchiSteamFarmPlusPageViewModel"
Description="{Binding Description, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
Subtitle="{Binding Author, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource StringFormatConverter}, ConverterParameter=Plugin_Author}"
mc:Ignorable="d">
<spp:PageBase.PreviewImage>
<ui:ImageIconSource Source="{Binding Icon, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource BitmapAssetValueConverter}}" />
</spp:PageBase.PreviewImage>
<spp:PageBase.ActionContent>
<StackPanel Orientation="Horizontal" Spacing="2">
<Button
Padding="8,4"
Command="{Binding RunOrStop}"
DockPanel.Dock="Right"
Theme="{StaticResource TransparentButton}">
<Panel>
<Viewbox
Width="16"
Height="16"
HorizontalAlignment="Left"
IsVisible="{ReflectionBinding Source={x:Static s:ASFService.Current},
Path=!IsASFRuning}">
<ui:SymbolIcon Symbol="Play" />
</Viewbox>
<Viewbox
Width="16"
Height="16"
HorizontalAlignment="Left"
IsVisible="{ReflectionBinding Source={x:Static s:ASFService.Current},
Path=IsASFRuning}">
<ui:SymbolIcon Symbol="Pause" />
</Viewbox>
<TextBlock
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{ReflectionBinding Source={x:Static s:ASFService.Current},
Path=!IsASFRuning}"
Text="{Binding Path=Res.ASF_Start, Mode=OneWay, Source={x:Static spp:Strings.ASF_Start}}" />
<TextBlock
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{ReflectionBinding Source={x:Static s:ASFService.Current},
Path=IsASFRuning}"
Text="{Binding Path=Res.ASF_Stop, Mode=OneWay, Source={x:Static spp:Strings.ASF_Stop}}" />
</Panel>
</Button>
<Button
Padding="8,4"
Command="{Binding OpenASFBrowser}"
CommandParameter="WebAddBot"
DockPanel.Dock="Right"
Theme="{StaticResource TransparentButton}">
<Panel>
<Viewbox
Width="16"
Height="16"
HorizontalAlignment="Left">
<ui:SymbolIcon Symbol="Add" />
</Viewbox>
<TextBlock
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="新增 Bot" />
</Panel>
</Button>
<Button
Padding="8,4"
Command="{Binding RefreshBots}"
DockPanel.Dock="Right"
Theme="{StaticResource TransparentButton}">
<Panel>
<Viewbox
Width="16"
Height="16"
HorizontalAlignment="Left">
<ui:SymbolIcon Symbol="Refresh" />
</Viewbox>
<TextBlock
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="刷新 Bot" />
</Panel>
</Button>
<ToggleButton
Padding="8,4"
DockPanel.Dock="Right"
IsChecked="{Binding $parent[spp:PageBase].IsPaneOpen, Mode=TwoWay}"
Theme="{StaticResource TransparentToggleButton}">
<Panel>
<Viewbox
Width="16"
Height="16"
HorizontalAlignment="Left">
<ui:SymbolIcon Symbol="Settings" />
</Viewbox>
<TextBlock
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="设置" />
</Panel>
</ToggleButton>
<DropDownButton
Padding="8"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="{StaticResource More}"
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="20"
Theme="{StaticResource TransparentButton}">
<DropDownButton.Flyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<ui:MenuFlyoutItem
Command="{Binding OpenASFBrowser}"
CommandParameter="Repo"
Text="Repo" />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem
Command="{Binding OpenASFBrowser}"
CommandParameter="Wiki"
Text="Wiki" />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem
Command="{Binding OpenASFBrowser}"
CommandParameter="ConfigGenerator"
Text="ConfigGenerator" />
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
</StackPanel>
</spp:PageBase.ActionContent>
<spp:PageBase.PaneContent>
<StackPanel Margin="15,20" Spacing="20">
<spp:OptionsDisplayItem Description="{Binding Source={x:Static ms:ASFSettings.ArchiSteamFarmExePath}, Path=Value, Mode=OneWay, Converter={StaticResource StringOmitConverter}, ConverterParameter=15}" Header="ASF 文件路径">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE756;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<Button Command="{Binding SelectASFExePath}" Content="选择路径" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="自动启动 ASF">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE770;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ASFSettings.AutoRunArchiSteamFarm}, Path=Value, Mode=TwoWay}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="检查文件安全性">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE83D;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ASFSettings.CheckArchiSteamFarmExe}, Path=Value, Mode=TwoWay}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="ASF 监听端口">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE704;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<ui:FAComboBox IsEditable="True" Text="{Binding Source={x:Static ms:ASFSettings.IPCPortId}, Path=Value, Mode=TwoWay}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="端口被占时随机端口号">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE770;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:ASFSettings.IPCPortOccupiedRandom}, Path=Value, Mode=TwoWay}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="控制台最大行数">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xF2B7;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<ui:FAComboBox IsEditable="True" Text="{Binding Source={x:Static ms:ASFSettings.ConsoleMaxLine}, Path=Value, Mode=TwoWay}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="控制台字体大小">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="Segoe Fluent Icons" Glyph="&#xE8E9;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<ui:FAComboBox IsEditable="True" Text="{Binding Source={x:Static ms:ASFSettings.ConsoleFontSize}, Path=Value, Mode=TwoWay}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
</StackPanel>
</spp:PageBase.PaneContent>
<spp:ContentLoader IsLoading="False">
<StackPanel Orientation="Vertical" Spacing="2">
<StackPanel Orientation="Horizontal" Spacing="2">
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding SelectBotFiles}"
Content="选择 Bot 配置文件导入" />
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding SelectGlobalFiles}"
Content="选择全局配置文件导入" />
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding OpenWebUIConsole}"
Content="打开 WebUI 控制台" />
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding SetEncryptionKey}"
Content="SetEncryptionKey" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="2">
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding OpenASFFolder}"
CommandParameter="config"
Content="配置文件文件夹" />
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding OpenASFFolder}"
CommandParameter="plugin"
Content="插件文件夹" />
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding OpenASFFolder}"
CommandParameter="Logs"
Content="日志文件夹" />
<Button
Width="180"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding OpenASFFolder}"
CommandParameter="www"
Content="WEBUI 文件夹" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="2">
<Label
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="控制台输入" />
<TextBox Width="200" Text="{Binding Source={x:Static s:ASFService.Current}, Path=ConsoleLogInput, Mode=TwoWay}" />
<Button
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding ShellMessageInput}"
Content="发送" />
</StackPanel>
<TextBlock Text="{Binding Source={x:Static s:ASFService.Current}, Path=ConsoleLogText, Mode=TwoWay}" />
</StackPanel>
</spp:ContentLoader>
</spp:PageBase>
@@ -0,0 +1,20 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
namespace BD.WTTS.UI.Views.Pages;
public partial class ArchiSteamFarmPage : PageBase<ArchiSteamFarmPlusPageViewModel>
{
public ArchiSteamFarmPage()
{
InitializeComponent();
DataContext ??= new ArchiSteamFarmPlusPageViewModel();
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
}
}
@@ -8,6 +8,8 @@ public static class ASF
[PublicAPI]
public static readonly ArchiLogger ArchiLogger = new(SharedInfo.ASF);
public static bool IsReady { get; set; }
public enum EUserInputType : byte
{
None,
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ArchiSteamFarm.IPC;
public class IPCConfig
{
public IPCConfig(Uri uri) { Kestrel = new(uri); }
public Kestrel Kestrel { get; private set; }
}
public class Kestrel
{
public Kestrel(Uri uri) { Endpoints = new(uri); }
public Endpoints Endpoints { get; private set; }
}
public class Endpoints
{
public Endpoints(Uri uri) { Http = new(uri); }
public Http Http { get; private set; }
}
public class Http
{
public Http(Uri uri) { Url = uri; }
public Uri Url { get; private set; }
}
@@ -0,0 +1,48 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using ArchiSteamFarm.Helpers;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class ASFEncryptRequest
{
/// <summary>
/// Encryption method used for encrypting this string.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public ArchiCryptoHelper.ECryptoMethod CryptoMethod { get; private set; }
/// <summary>
/// String to encrypt with provided <see cref="CryptoMethod" />.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public string StringToEncrypt { get; set; } = "";
[JsonConstructor]
public ASFEncryptRequest() { }
}
@@ -0,0 +1,48 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using ArchiSteamFarm.Helpers;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class ASFHashRequest
{
/// <summary>
/// Hashing method used for hashing this string.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public ArchiCryptoHelper.EHashingMethod HashingMethod { get; private set; }
/// <summary>
/// String to hash with provided <see cref="HashingMethod" />.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public string StringToHash { get; set; } = "";
[JsonConstructor]
public ASFHashRequest() { }
}
@@ -0,0 +1,41 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using ArchiSteamFarm.Storage;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class ASFRequest
{
/// <summary>
/// ASF's global config structure.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public GlobalConfig GlobalConfig { get; set; } = new();
[JsonConstructor]
public ASFRequest() { }
}
@@ -0,0 +1,45 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections.Specialized;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotGamesToRedeemInBackgroundRequest
{
/// <summary>
/// A string-string map that maps cd-key to redeem (key) to its name (value).
/// </summary>
/// <remarks>
/// Key in the map must be a valid and unique Steam cd-key.
/// Value in the map must be a non-null and non-empty name of the key (e.g. game's name, but can be anything).
/// </remarks>
[JsonProperty(Required = Required.Always)]
[Required]
public OrderedDictionary GamesToRedeemInBackground { get; set; } = new();
[JsonConstructor]
public BotGamesToRedeemInBackgroundRequest() { }
}
@@ -0,0 +1,45 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
using ArchiSteamFarm.Core;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotInputRequest
{
/// <summary>
/// Specifies the type of the input.
/// </summary>
[JsonProperty(Required = Required.Always)]
public ASF.EUserInputType Type { get; set; }
/// <summary>
/// Specifies the value for given input type (declared in <see cref="Type" />)
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Value { get; private set; } = "";
[JsonConstructor]
public BotInputRequest() { }
}
@@ -0,0 +1,44 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotPauseRequest
{
/// <summary>
/// Specifies if pause is permanent or temporary (default).
/// </summary>
[JsonProperty(Required = Required.DisallowNull)]
public bool Permanent { get; set; }
/// <summary>
/// Specifies automatic resume action in given seconds. Default value of 0 disables automatic resume.
/// </summary>
[JsonProperty(Required = Required.DisallowNull)]
public ushort ResumeInSeconds { get; private set; } = 0;
[JsonConstructor]
public BotPauseRequest() { }
}
@@ -0,0 +1,41 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotRedeemRequest
{
/// <summary>
/// A collection (set) of keys to redeem.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public ImmutableHashSet<string> KeysToRedeem { get; set; } = ImmutableHashSet<string>.Empty;
[JsonConstructor]
public BotRedeemRequest() { }
}
@@ -0,0 +1,40 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests;
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class BotRenameRequest
{
/// <summary>
/// Specifies the new name for the bot. The new name can't be "ASF", neither the one used by any existing bot.
/// </summary>
[JsonProperty(Required = Required.Always)]
[Required]
public string NewName { get; set; } = "";
[JsonConstructor]
public BotRenameRequest() { }
}

Some files were not shown because too many files have changed in this diff Show More