Use C# 10 string interpolation wherever possible

This commit is contained in:
Archi
2021-11-11 01:53:34 +01:00
parent 60376c4d93
commit d1fc7ebb74
20 changed files with 76 additions and 73 deletions

View File

@@ -66,7 +66,7 @@ internal sealed class ExamplePlugin : IASF, IBot, IBotCommand, IBotConnection, I
foreach ((string configProperty, JToken configValue) in additionalConfigProperties) {
// It's a good idea to prefix your custom properties with the name of your plugin, so there will be no possible conflict of ASF or other plugins using the same name, neither now or in the future
switch (configProperty) {
case nameof(ExamplePlugin) + "TestProperty" when configValue.Type == JTokenType.Boolean:
case $"{nameof(ExamplePlugin)}TestProperty" when configValue.Type == JTokenType.Boolean:
bool exampleBooleanValue = configValue.Value<bool>();
ASF.ArchiLogger.LogGenericInfo($"{nameof(ExamplePlugin)}TestProperty boolean property has been found with a value of: {exampleBooleanValue}");

View File

@@ -36,16 +36,16 @@ public sealed class SteamChatMessage {
string prefix = new('x', MaxMessagePrefixBytes);
const string emoji = "😎";
const string message = emoji + emoji + emoji + emoji;
const string message = $"{emoji}{emoji}{emoji}{emoji}";
List<string> output = await GetMessageParts(message, prefix, true).ToListAsync().ConfigureAwait(false);
Assert.AreEqual(4, output.Count);
Assert.AreEqual(prefix + emoji + ContinuationCharacter, output[0]);
Assert.AreEqual(prefix + ContinuationCharacter + emoji + ContinuationCharacter, output[1]);
Assert.AreEqual(prefix + ContinuationCharacter + emoji + ContinuationCharacter, output[2]);
Assert.AreEqual(prefix + ContinuationCharacter + emoji, output[3]);
Assert.AreEqual($"{prefix}{emoji}{ContinuationCharacter}", output[0]);
Assert.AreEqual($"{prefix}{ContinuationCharacter}{emoji}{ContinuationCharacter}", output[1]);
Assert.AreEqual($"{prefix}{ContinuationCharacter}{emoji}{ContinuationCharacter}", output[2]);
Assert.AreEqual($"{prefix}{ContinuationCharacter}{emoji}", output[3]);
}
[TestMethod]
@@ -71,14 +71,14 @@ public sealed class SteamChatMessage {
const string emoji = "😎";
string longSequence = new('a', longLineLength - 1);
string message = longSequence + emoji;
string message = $"{longSequence}{emoji}";
List<string> output = await GetMessageParts(message, isAccountLimited: isAccountLimited).ToListAsync().ConfigureAwait(false);
Assert.AreEqual(2, output.Count);
Assert.AreEqual(longSequence + ContinuationCharacter, output[0]);
Assert.AreEqual(ContinuationCharacter + emoji, output[1]);
Assert.AreEqual($"{longSequence}{ContinuationCharacter}", output[0]);
Assert.AreEqual($"{ContinuationCharacter}{emoji}", output[1]);
}
[TestMethod]
@@ -122,7 +122,7 @@ public sealed class SteamChatMessage {
Assert.AreEqual(2, output.Count);
Assert.AreEqual(longLine + ContinuationCharacter, output[0]);
Assert.AreEqual($"{longLine}{ContinuationCharacter}", output[0]);
Assert.AreEqual($@"{ContinuationCharacter}\[", output[1]);
}
@@ -170,7 +170,7 @@ public sealed class SteamChatMessage {
List<string> output = await GetMessageParts(message, prefix).ToListAsync().ConfigureAwait(false);
Assert.AreEqual(1, output.Count);
Assert.AreEqual(escapedPrefix + message, output.First());
Assert.AreEqual($"{escapedPrefix}{message}", output.First());
}
[DataRow(false)]
@@ -181,16 +181,16 @@ public sealed class SteamChatMessage {
int longLineLength = maxMessageBytes - ReservedContinuationMessageBytes;
string longLine = new('a', longLineLength);
string message = longLine + longLine + longLine + longLine;
string message = $"{longLine}{longLine}{longLine}{longLine}";
List<string> output = await GetMessageParts(message, isAccountLimited: isAccountLimited).ToListAsync().ConfigureAwait(false);
Assert.AreEqual(4, output.Count);
Assert.AreEqual(longLine + ContinuationCharacter, output[0]);
Assert.AreEqual(ContinuationCharacter + longLine + ContinuationCharacter, output[1]);
Assert.AreEqual(ContinuationCharacter + longLine + ContinuationCharacter, output[2]);
Assert.AreEqual(ContinuationCharacter + longLine, output[3]);
Assert.AreEqual($"{longLine}{ContinuationCharacter}", output[0]);
Assert.AreEqual($"{ContinuationCharacter}{longLine}{ContinuationCharacter}", output[1]);
Assert.AreEqual($"{ContinuationCharacter}{longLine}{ContinuationCharacter}", output[2]);
Assert.AreEqual($"{ContinuationCharacter}{longLine}", output[3]);
}
[TestMethod]
@@ -297,15 +297,15 @@ public sealed class SteamChatMessage {
}
string newlinePart = newlinePartBuilder.ToString();
string message = newlinePart + Environment.NewLine + newlinePart + Environment.NewLine + newlinePart + Environment.NewLine + newlinePart;
string message = $"{newlinePart}{Environment.NewLine}{newlinePart}{Environment.NewLine}{newlinePart}{Environment.NewLine}{newlinePart}";
List<string> output = await GetMessageParts(message, isAccountLimited: isAccountLimited).ToListAsync().ConfigureAwait(false);
Assert.AreEqual(4, output.Count);
Assert.AreEqual(newlinePart + ParagraphCharacter, output[0]);
Assert.AreEqual(newlinePart + ParagraphCharacter, output[1]);
Assert.AreEqual(newlinePart + ParagraphCharacter, output[2]);
Assert.AreEqual($"{newlinePart}{ParagraphCharacter}", output[0]);
Assert.AreEqual($"{newlinePart}{ParagraphCharacter}", output[1]);
Assert.AreEqual($"{newlinePart}{ParagraphCharacter}", output[2]);
Assert.AreEqual(newlinePart, output[3]);
}

View File

@@ -395,12 +395,12 @@ public static class ASF {
networkGroupText = $"-{BitConverter.ToString(SHA256.HashData(Encoding.UTF8.GetBytes(GlobalConfig.WebProxyText!))).Replace("-", "", StringComparison.Ordinal)}";
}
ConfirmationsSemaphore ??= await PluginsCore.GetCrossProcessSemaphore(nameof(ConfirmationsSemaphore) + networkGroupText).ConfigureAwait(false);
GiftsSemaphore ??= await PluginsCore.GetCrossProcessSemaphore(nameof(GiftsSemaphore) + networkGroupText).ConfigureAwait(false);
InventorySemaphore ??= await PluginsCore.GetCrossProcessSemaphore(nameof(InventorySemaphore) + networkGroupText).ConfigureAwait(false);
LoginRateLimitingSemaphore ??= await PluginsCore.GetCrossProcessSemaphore(nameof(LoginRateLimitingSemaphore) + networkGroupText).ConfigureAwait(false);
LoginSemaphore ??= await PluginsCore.GetCrossProcessSemaphore(nameof(LoginSemaphore) + networkGroupText).ConfigureAwait(false);
RateLimitingSemaphore ??= await PluginsCore.GetCrossProcessSemaphore(nameof(RateLimitingSemaphore) + networkGroupText).ConfigureAwait(false);
ConfirmationsSemaphore ??= await PluginsCore.GetCrossProcessSemaphore($"{nameof(ConfirmationsSemaphore)}{networkGroupText}").ConfigureAwait(false);
GiftsSemaphore ??= await PluginsCore.GetCrossProcessSemaphore($"{nameof(GiftsSemaphore)}{networkGroupText}").ConfigureAwait(false);
InventorySemaphore ??= await PluginsCore.GetCrossProcessSemaphore($"{nameof(InventorySemaphore)}{networkGroupText}").ConfigureAwait(false);
LoginRateLimitingSemaphore ??= await PluginsCore.GetCrossProcessSemaphore($"{nameof(LoginRateLimitingSemaphore)}{networkGroupText}").ConfigureAwait(false);
LoginSemaphore ??= await PluginsCore.GetCrossProcessSemaphore($"{nameof(LoginSemaphore)}{networkGroupText}").ConfigureAwait(false);
RateLimitingSemaphore ??= await PluginsCore.GetCrossProcessSemaphore($"{nameof(RateLimitingSemaphore)}{networkGroupText}").ConfigureAwait(false);
WebLimitingSemaphores ??= new Dictionary<Uri, (ICrossProcessSemaphore RateLimitingSemaphore, SemaphoreSlim OpenConnectionsSemaphore)>(4) {
{ ArchiWebHandler.SteamCommunityURL, (await PluginsCore.GetCrossProcessSemaphore($"{nameof(ArchiWebHandler)}{networkGroupText}-{nameof(ArchiWebHandler.SteamCommunityURL)}").ConfigureAwait(false), new SemaphoreSlim(WebBrowser.MaxConnections, WebBrowser.MaxConnections)) },

View File

@@ -51,7 +51,7 @@ internal sealed class Statistics : IAsyncDisposable {
private const byte MinHeartBeatTTL = 10; // Minimum amount of minutes we must wait before sending next HeartBeat
private const byte MinItemsCount = 100; // Minimum amount of items to be eligible for public listing
private const byte MinPersonaStateTTL = 8; // Minimum amount of hours we must wait before requesting persona state update
private const string URL = "https://" + SharedInfo.StatisticsServer;
private const string URL = $"https://{SharedInfo.StatisticsServer}";
private static readonly ImmutableHashSet<Asset.EType> AcceptedMatchableTypes = ImmutableHashSet.Create(
Asset.EType.Emoticon,

View File

@@ -30,9 +30,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api;
[ApiController]
[Produces("application/json")]
[Route("Api")]
[SwaggerResponse((int) HttpStatusCode.BadRequest, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse((int) HttpStatusCode.Unauthorized, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + SharedInfo.ProjectURL + "/wiki/IPC#authentication.", typeof(GenericResponse<StatusCodeResponse>))]
[SwaggerResponse((int) HttpStatusCode.Forbidden, "ASF lacks " + nameof(GlobalConfig.IPCPassword) + " and you're not permitted to access the API, or " + nameof(GlobalConfig.IPCPassword) + " is set and you've failed to authenticate too many times (try again in an hour). See " + SharedInfo.ProjectURL + "/wiki/IPC#authentication.", typeof(GenericResponse<StatusCodeResponse>))]
[SwaggerResponse((int) HttpStatusCode.BadRequest, $"The request has failed, check {nameof(GenericResponse.Message)} from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse((int) HttpStatusCode.Unauthorized, $"ASF has {nameof(GlobalConfig.IPCPassword)} set, but you've failed to authenticate. See {SharedInfo.ProjectURL}/wiki/IPC#authentication.", typeof(GenericResponse<StatusCodeResponse>))]
[SwaggerResponse((int) HttpStatusCode.Forbidden, $"ASF lacks {nameof(GlobalConfig.IPCPassword)} and you're not permitted to access the API, or {nameof(GlobalConfig.IPCPassword)} is set and you've failed to authenticate too many times (try again in an hour). See {SharedInfo.ProjectURL}/wiki/IPC#authentication.", typeof(GenericResponse<StatusCodeResponse>))]
[SwaggerResponse((int) HttpStatusCode.InternalServerError, "ASF has encountered an unexpected error while serving the request. The log may include extra info related to this issue.")]
[SwaggerResponse((int) HttpStatusCode.ServiceUnavailable, "ASF has encountered an error while requesting a third-party resource. Try again later.")]
public abstract class ArchiController : ControllerBase { }

View File

@@ -55,7 +55,7 @@ public sealed class StructureController : ArchiController {
try {
obj = Activator.CreateInstance(targetType, true);
} catch (Exception e) {
return BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(targetType)) + Environment.NewLine + e));
return BadRequest(new GenericResponse(false, $"{string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(targetType))}{Environment.NewLine}{e}"));
}
return Ok(new GenericResponse<object>(obj));

View File

@@ -55,7 +55,7 @@ public sealed class TwoFactorAuthenticationConfirmationsRequest {
/// <summary>
/// A helper property which works the same as <see cref="AcceptedCreatorIDs" /> but with values written as strings - for javascript compatibility purposes. Use either this one, or <see cref="AcceptedCreatorIDs" />, not both.
/// </summary>
[JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(AcceptedCreatorIDs), Required = Required.DisallowNull)]
[JsonProperty(PropertyName = $"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(AcceptedCreatorIDs)}", Required = Required.DisallowNull)]
public ImmutableHashSet<string> SAcceptedCreatorIDs {
get => AcceptedCreatorIDs.Select(static creatorID => creatorID.ToString(CultureInfo.InvariantCulture)).ToImmutableHashSet();
set {

View File

@@ -176,7 +176,7 @@ public sealed class ArchiLogger {
}
// Otherwise, we ran into fatal exception before logging module could even get initialized, so activate fallback logging that involves file and console
string message = string.Format(CultureInfo.CurrentCulture, DateTime.Now + " " + Strings.ErrorEarlyFatalExceptionInfo, SharedInfo.Version) + Environment.NewLine;
string message = $"{DateTime.Now} {string.Format(CultureInfo.CurrentCulture, Strings.ErrorEarlyFatalExceptionInfo, SharedInfo.Version)}{Environment.NewLine}";
try {
await File.WriteAllTextAsync(SharedInfo.LogFile, message).ConfigureAwait(false);
@@ -191,7 +191,7 @@ public sealed class ArchiLogger {
}
while (true) {
message = string.Format(CultureInfo.CurrentCulture, Strings.ErrorEarlyFatalExceptionPrint, previousMethodName, exception.Message, exception.StackTrace) + Environment.NewLine;
message = $"{string.Format(CultureInfo.CurrentCulture, Strings.ErrorEarlyFatalExceptionPrint, previousMethodName, exception.Message, exception.StackTrace)}{Environment.NewLine}";
try {
await File.AppendAllTextAsync(SharedInfo.LogFile, message).ConfigureAwait(false);

View File

@@ -44,7 +44,7 @@ internal static class Logging {
internal const string NLogConfigurationFile = "NLog.config";
private const byte ConsoleResponsivenessDelay = 250; // In milliseconds
private const string GeneralLayout = @"${date:format=yyyy-MM-dd HH\:mm\:ss}|${processname}-${processid}|${level:uppercase=true}|" + LayoutMessage;
private const string GeneralLayout = $@"${{date:format=yyyy-MM-dd HH\:mm\:ss}}|${{processname}}-${{processid}}|${{level:uppercase=true}}|{LayoutMessage}";
private const string LayoutMessage = @"${logger}|${message}${onexception:inner= ${exception:format=toString,Data}}";
private static readonly ConcurrentHashSet<LoggingRule> ConsoleLoggingRules = new();

View File

@@ -37,20 +37,20 @@ public static class SharedInfo {
internal const string ArchivalLogsDirectory = "logs";
internal const string ASF = nameof(ASF);
internal const ulong ASFGroupSteamID = 103582791440160998;
internal const string AssemblyDocumentation = AssemblyName + ".xml";
internal const string AssemblyDocumentation = $"{AssemblyName}.xml";
internal const string AssemblyName = nameof(ArchiSteamFarm);
internal const string DatabaseExtension = ".db";
internal const string DebugDirectory = "debug";
internal const string EnvironmentVariableCryptKey = ASF + "_CRYPTKEY";
internal const string EnvironmentVariableNetworkGroup = ASF + "_NETWORK_GROUP";
internal const string EnvironmentVariablePath = ASF + "_PATH";
internal const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases";
internal const string GithubRepo = "JustArchiNET/" + AssemblyName;
internal const string GlobalConfigFileName = ASF + JsonConfigExtension;
internal const string GlobalDatabaseFileName = ASF + DatabaseExtension;
internal const string EnvironmentVariableCryptKey = $"{ASF}_CRYPTKEY";
internal const string EnvironmentVariableNetworkGroup = $"{ASF}_NETWORK_GROUP";
internal const string EnvironmentVariablePath = $"{ASF}_PATH";
internal const string GithubReleaseURL = $"https://api.github.com/repos/{GithubRepo}/releases";
internal const string GithubRepo = $"JustArchiNET/{AssemblyName}";
internal const string GlobalConfigFileName = $"{ASF}{JsonConfigExtension}";
internal const string GlobalDatabaseFileName = $"{ASF}{DatabaseExtension}";
internal const ushort InformationDelay = 10000;
internal const string IPCConfigExtension = ".config";
internal const string IPCConfigFile = nameof(IPC) + IPCConfigExtension;
internal const string IPCConfigFile = $"{nameof(IPC)}{IPCConfigExtension}";
internal const string JsonConfigExtension = ".json";
internal const string KeysExtension = ".keys";
internal const string KeysUnusedExtension = ".unused";
@@ -61,7 +61,7 @@ public static class SharedInfo {
internal const string LolcatCultureName = "qps-Ploc";
internal const string MobileAuthenticatorExtension = ".maFile";
internal const string PluginsDirectory = "plugins";
internal const string ProjectURL = "https://github.com/" + GithubRepo;
internal const string ProjectURL = $"https://github.com/{GithubRepo}";
internal const string SentryHashExtension = ".bin";
internal const ushort ShortInformationDelay = InformationDelay / 2;
internal const string StatisticsServer = "asf.justarchi.net";
@@ -88,7 +88,7 @@ public static class SharedInfo {
}
internal static string ProgramIdentifier => $"{PublicIdentifier} V{Version} ({BuildInfo.Variant}/{ModuleVersion} | {OS.Version})";
internal static string PublicIdentifier => AssemblyName + (BuildInfo.IsCustomBuild ? "-custom" : PluginsCore.HasCustomPluginsLoaded ? "-modded" : "");
internal static string PublicIdentifier => $"{AssemblyName}{(BuildInfo.IsCustomBuild ? "-custom" : PluginsCore.HasCustomPluginsLoaded ? "-modded" : "")}";
internal static Version Version => Assembly.GetExecutingAssembly().GetName().Version ?? throw new InvalidOperationException(nameof(Version));
private static Guid ModuleVersion => Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId;

View File

@@ -180,8 +180,7 @@ public sealed class Bot : IAsyncDisposable {
/// </remarks>
private bool ShouldUseLoginKeys => BotConfig.UseLoginKeys && (!BotConfig.IsSteamPasswordSet || string.IsNullOrEmpty(BotConfig.DecryptedSteamPassword) || !HasMobileAuthenticator);
[JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamID))]
[JsonProperty(PropertyName = $"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(SteamID)}")]
private string SSteamID => SteamID.ToString(CultureInfo.InvariantCulture);
[JsonProperty]
@@ -523,13 +522,13 @@ public sealed class Bot : IAsyncDisposable {
string botPath = Path.Combine(SharedInfo.ConfigDirectory, botName);
return fileType switch {
EFileType.Config => botPath + SharedInfo.JsonConfigExtension,
EFileType.Database => botPath + SharedInfo.DatabaseExtension,
EFileType.KeysToRedeem => botPath + SharedInfo.KeysExtension,
EFileType.KeysToRedeemUnused => botPath + SharedInfo.KeysExtension + SharedInfo.KeysUnusedExtension,
EFileType.KeysToRedeemUsed => botPath + SharedInfo.KeysExtension + SharedInfo.KeysUsedExtension,
EFileType.MobileAuthenticator => botPath + SharedInfo.MobileAuthenticatorExtension,
EFileType.SentryFile => botPath + SharedInfo.SentryHashExtension,
EFileType.Config => $"{botPath}{SharedInfo.JsonConfigExtension}",
EFileType.Database => $"{botPath}{SharedInfo.DatabaseExtension}",
EFileType.KeysToRedeem => $"{botPath}{SharedInfo.KeysExtension}",
EFileType.KeysToRedeemUnused => $"{botPath}{SharedInfo.KeysExtension}{SharedInfo.KeysUnusedExtension}",
EFileType.KeysToRedeemUsed => $"{botPath}{SharedInfo.KeysExtension}{SharedInfo.KeysUsedExtension}",
EFileType.MobileAuthenticator => $"{botPath}{SharedInfo.MobileAuthenticatorExtension}",
EFileType.SentryFile => $"{botPath}{SharedInfo.SentryHashExtension}",
_ => throw new ArgumentOutOfRangeException(nameof(fileType))
};
}
@@ -2741,7 +2740,7 @@ public sealed class Bot : IAsyncDisposable {
AccountFlags = callback.AccountFlags;
SteamID = callback.ClientSteamID ?? throw new InvalidOperationException(nameof(callback.ClientSteamID));
ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotLoggedOn, SteamID + (!string.IsNullOrEmpty(callback.VanityURL) ? $"/{callback.VanityURL}" : "")));
ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotLoggedOn, $"{SteamID}{(!string.IsNullOrEmpty(callback.VanityURL) ? $"/{callback.VanityURL}" : "")}"));
// Old status for these doesn't matter, we'll update them if needed
InvalidPasswordFailures = TwoFactorCodeFailures = 0;
@@ -2765,7 +2764,7 @@ public sealed class Bot : IAsyncDisposable {
// Handle steamID-based maFile
if (!HasMobileAuthenticator) {
string maFilePath = Path.Combine(SharedInfo.ConfigDirectory, SteamID + SharedInfo.MobileAuthenticatorExtension);
string maFilePath = Path.Combine(SharedInfo.ConfigDirectory, $"{SteamID}{SharedInfo.MobileAuthenticatorExtension}");
if (File.Exists(maFilePath)) {
await ImportAuthenticatorFromFile(maFilePath).ConfigureAwait(false);
@@ -3244,7 +3243,7 @@ public sealed class Bot : IAsyncDisposable {
name = string.Join(", ", result.Items.Values);
}
string logEntry = $"{name}{DefaultBackgroundKeysRedeemerSeparator}[{result.PurchaseResultDetail}]{(result.Items?.Count > 0 ? DefaultBackgroundKeysRedeemerSeparator + string.Join(", ", result.Items) : "")}{DefaultBackgroundKeysRedeemerSeparator}{key}";
string logEntry = $"{name}{DefaultBackgroundKeysRedeemerSeparator}[{result.PurchaseResultDetail}]{(result.Items?.Count > 0 ? $"{DefaultBackgroundKeysRedeemerSeparator}{string.Join(", ", result.Items)}" : "")}{DefaultBackgroundKeysRedeemerSeparator}{key}";
string filePath = GetFilePath(redeemed ? EFileType.KeysToRedeemUsed : EFileType.KeysToRedeemUnused);
@@ -3255,7 +3254,7 @@ public sealed class Bot : IAsyncDisposable {
}
try {
await File.AppendAllTextAsync(filePath, logEntry + Environment.NewLine).ConfigureAwait(false);
await File.AppendAllTextAsync(filePath, $"{logEntry}{Environment.NewLine}").ConfigureAwait(false);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.Content, logEntry));

View File

@@ -2226,7 +2226,7 @@ public sealed class Commands {
ownedGamesStats[gameID] = ownedGameStats;
}
IEnumerable<string> extraResponses = ownedGamesStats.Select(kv => FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnsOverviewPerGame, kv.Value.Count, validResults.Count, kv.Key + (!string.IsNullOrEmpty(kv.Value.GameName) ? $" | {kv.Value.GameName}" : ""))));
IEnumerable<string> extraResponses = ownedGamesStats.Select(kv => FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnsOverviewPerGame, kv.Value.Count, validResults.Count, $"{kv.Key}{(!string.IsNullOrEmpty(kv.Value.GameName) ? $" | {kv.Value.GameName}" : "")}")));
return string.Join(Environment.NewLine, validResults.Select(static result => result.Response).Concat(extraResponses));
}
@@ -2374,7 +2374,11 @@ public sealed class Commands {
foreach (string game in games) {
if (!uint.TryParse(game, out uint gameID) || (gameID == 0)) {
gameName.Append((gameName.Length > 0 ? " " : "") + game);
if (gameName.Length > 0) {
gameName.Append(' ');
}
gameName.Append(game);
continue;
}
@@ -3408,7 +3412,7 @@ public sealed class Commands {
(bool success, string? message, Version? version) = await Actions.Update().ConfigureAwait(false);
return FormatStaticResponse((success ? Strings.Success : Strings.WarningFailed) + (!string.IsNullOrEmpty(message) ? $" {message}" : version != null ? $" {version}" : ""));
return FormatStaticResponse($"{(success ? Strings.Success : Strings.WarningFailed)}{(!string.IsNullOrEmpty(message) ? $" {message}" : version != null ? $" {version}" : "")}");
}
private string? ResponseVersion(ulong steamID) {

View File

@@ -314,7 +314,7 @@ public sealed class BotConfig {
private string? BackingSteamParentalCode = DefaultSteamParentalCode;
private string? BackingSteamPassword = DefaultSteamPassword;
[JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamMasterClanID), Required = Required.DisallowNull)]
[JsonProperty(PropertyName = $"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(SteamMasterClanID)}", Required = Required.DisallowNull)]
private string SSteamMasterClanID {
get => SteamMasterClanID.ToString(CultureInfo.InvariantCulture);

View File

@@ -88,10 +88,10 @@ internal sealed class BotDatabase : SerializableFile {
}
}
[JsonProperty(PropertyName = "_" + nameof(LoginKey))]
[JsonProperty(PropertyName = $"_{nameof(LoginKey)}")]
private string? BackingLoginKey;
[JsonProperty(PropertyName = "_" + nameof(MobileAuthenticator))]
[JsonProperty(PropertyName = $"_{nameof(MobileAuthenticator)}")]
private MobileAuthenticator? BackingMobileAuthenticator;
private BotDatabase(string filePath) {

View File

@@ -310,7 +310,7 @@ public sealed class GlobalConfig {
private WebProxy? BackingWebProxy;
private string? BackingWebProxyPassword = DefaultWebProxyPassword;
[JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamOwnerID), Required = Required.DisallowNull)]
[JsonProperty(PropertyName = $"{SharedInfo.UlongCompatibilityStringPrefix}{nameof(SteamOwnerID)}", Required = Required.DisallowNull)]
private string SSteamOwnerID {
get => SteamOwnerID.ToString(CultureInfo.InvariantCulture);

View File

@@ -92,10 +92,10 @@ public sealed class GlobalDatabase : SerializableFile {
}
}
[JsonProperty(PropertyName = "_" + nameof(CellID), Required = Required.DisallowNull)]
[JsonProperty(PropertyName = $"_{nameof(CellID)}", Required = Required.DisallowNull)]
private uint BackingCellID;
[JsonProperty(PropertyName = "_" + nameof(LastChangeNumber), Required = Required.DisallowNull)]
[JsonProperty(PropertyName = $"_{nameof(LastChangeNumber)}", Required = Required.DisallowNull)]
private uint BackingLastChangeNumber;
private GlobalDatabase(string filePath) : this() {

View File

@@ -41,7 +41,7 @@ namespace ArchiSteamFarm.Web;
internal static class GitHub {
internal static async Task<ReleaseResponse?> GetLatestRelease(bool stable = true) {
Uri request = new(SharedInfo.GithubReleaseURL + (stable ? "/latest" : "?per_page=1"));
Uri request = new($"{SharedInfo.GithubReleaseURL}{(stable ? "/latest" : "?per_page=1")}");
if (stable) {
return await GetReleaseFromURL(request).ConfigureAwait(false);

View File

@@ -25,7 +25,7 @@ using JetBrains.Annotations;
namespace ArchiSteamFarm.Web.Responses;
[Obsolete("ASF no longer uses this class, re-implement it yourself using " + nameof(BasicResponse) + " if needed.")]
[Obsolete($"ASF no longer uses this class, re-implement it yourself using {nameof(BasicResponse)} if needed.")]
public sealed class StringResponse : BasicResponse {
[PublicAPI]
public string Content { get; }

View File

@@ -25,7 +25,7 @@ using JetBrains.Annotations;
namespace ArchiSteamFarm.Web.Responses;
[Obsolete("ASF no longer uses any XML-related functions, re-implement it yourself using " + nameof(BasicResponse) + " if needed.")]
[Obsolete($"ASF no longer uses any XML-related functions, re-implement it yourself using {nameof(BasicResponse)} if needed.")]
public sealed class XmlDocumentResponse : BasicResponse {
[PublicAPI]
public XmlDocument Content { get; }

View File

@@ -362,7 +362,7 @@ public sealed class WebBrowser : IDisposable {
return null;
}
[Obsolete("ASF no longer uses this function, re-implement it yourself using " + nameof(UrlGetToStream) + " if needed.")]
[Obsolete($"ASF no longer uses this function, re-implement it yourself using {nameof(UrlGetToStream)} if needed.")]
[PublicAPI]
public async Task<StringResponse?> UrlGetToString(Uri request, IReadOnlyCollection<KeyValuePair<string, string>>? headers = null, Uri? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) {
if (request == null) {
@@ -404,7 +404,7 @@ public sealed class WebBrowser : IDisposable {
return null;
}
[Obsolete("ASF no longer uses any XML-related functions, re-implement it yourself using " + nameof(UrlGetToStream) + " if needed.")]
[Obsolete($"ASF no longer uses any XML-related functions, re-implement it yourself using {nameof(UrlGetToStream)} if needed.")]
[PublicAPI]
public async Task<XmlDocumentResponse?> UrlGetToXmlDocument(Uri request, IReadOnlyCollection<KeyValuePair<string, string>>? headers = null, Uri? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) {
if (request == null) {