Skip to content

Commit

Permalink
Add loot& and transfer& commands (#3287)
Browse files Browse the repository at this point in the history
* Add `loot&` and `transfer&` commands

* Remove trailing comment
  • Loading branch information
dm1tz authored Sep 13, 2024
1 parent 4d0f5a5 commit 8e85b87
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 3 deletions.
14 changes: 13 additions & 1 deletion ArchiSteamFarm/Steam/Data/EAssetRarity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,21 @@

namespace ArchiSteamFarm.Steam.Data;

#pragma warning disable CA1027 // Aliases are intentional, we don't plan to combine fields
public enum EAssetRarity : byte {
Unknown,
Common,
Uncommon,
Rare
Rare,
Mythical,
Epic = Mythical,

Check notice on line 33 in ArchiSteamFarm/Steam/Data/EAssetRarity.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Type member is never used (non-private accessibility)

Enum member 'Epic' is never used
Legendary,
Exotic = Legendary,

Check notice on line 35 in ArchiSteamFarm/Steam/Data/EAssetRarity.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Type member is never used (non-private accessibility)

Enum member 'Exotic' is never used
Ancient,
Extraordinary = Ancient,

Check notice on line 37 in ArchiSteamFarm/Steam/Data/EAssetRarity.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Type member is never used (non-private accessibility)

Enum member 'Extraordinary' is never used
Immortal,
Contraband = Immortal,

Check notice on line 39 in ArchiSteamFarm/Steam/Data/EAssetRarity.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Type member is never used (non-private accessibility)

Enum member 'Contraband' is never used
Arcana,

Check notice on line 40 in ArchiSteamFarm/Steam/Data/EAssetRarity.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Type member is never used (non-private accessibility)

Enum member 'Arcana' is never used
Unusual

Check notice on line 41 in ArchiSteamFarm/Steam/Data/EAssetRarity.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Type member is never used (non-private accessibility)

Enum member 'Unusual' is never used
}
#pragma warning restore CA1027 // Aliases are intentional, we don't plan to combine fields
18 changes: 16 additions & 2 deletions ArchiSteamFarm/Steam/Data/InventoryDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ public EAssetRarity Rarity {
}

foreach (CEconItem_Tag? tag in Body.tags) {
switch (tag.category) {
case "droprate":
switch (tag.category.ToUpperInvariant()) {
case "DROPRATE":
switch (tag.internal_name) {
case "droprate_0":
CachedRarity = EAssetRarity.Common;
Expand All @@ -213,6 +213,20 @@ public EAssetRarity Rarity {

return CachedRarity.Value;
}
case "RARITY":
string internalName = tag.internal_name.Split('_', StringSplitOptions.RemoveEmptyEntries).Skip(1).FirstOrDefault() ?? tag.internal_name;

if (Enum.TryParse<EAssetRarity>(internalName, true, out EAssetRarity assetRarity)) {

Check warning on line 219 in ArchiSteamFarm/Steam/Data/InventoryDescription.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Redundant type arguments of method

Type argument specification is redundant
CachedRarity = assetRarity;

return CachedRarity.Value;
}

ASF.ArchiLogger.LogGenericError(Strings.FormatWarningUnknownValuePleaseReport(nameof(tag.internal_name), tag.internal_name));

CachedRarity = EAssetRarity.Unknown;

return CachedRarity.Value;
}
}

Expand Down
186 changes: 186 additions & 0 deletions ArchiSteamFarm/Steam/Interaction/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ public static EAccess GetProxyAccess(Bot bot, EAccess access, ulong steamID = 0)
return await ResponseAdvancedLoot(access, args[1], args[2], Utilities.GetArgsAsText(message, 3), steamID).ConfigureAwait(false);
case "LOOT^" when args.Length > 2:
return await ResponseAdvancedLoot(access, args[1], args[2]).ConfigureAwait(false);
case "LOOT&" when args.Length > 4:
return await ResponseAdvancedLootByAssetRarity(access, args[1], args[2], args[3], Utilities.GetArgsAsText(args, 4, ",")).ConfigureAwait(false);
case "LOOT&" when args.Length > 3:
return await ResponseAdvancedLootByAssetRarity(access, args[1], args[2], args[3]).ConfigureAwait(false);
case "LOOT@" when args.Length > 2:
return await ResponseLootByRealAppIDs(access, args[1], Utilities.GetArgsAsText(args, 2, ","), false, steamID).ConfigureAwait(false);
case "LOOT@":
Expand Down Expand Up @@ -318,6 +322,10 @@ public static EAccess GetProxyAccess(Bot bot, EAccess access, ulong steamID = 0)
return await ResponseAdvancedTransfer(access, args[1], args[2], args[3], Utilities.GetArgsAsText(message, 4), steamID).ConfigureAwait(false);
case "TRANSFER^" when args.Length > 3:
return await ResponseAdvancedTransfer(access, args[1], args[2], args[3]).ConfigureAwait(false);
case "TRANSFER&" when args.Length > 5:
return await ResponseAdvancedTransferByAssetRarity(access, args[1], args[2], args[3], args[4], Utilities.GetArgsAsText(args, 5, ","), steamID).ConfigureAwait(false);
case "TRANSFER&" when args.Length > 4:
return await ResponseAdvancedTransferByAssetRarity(access, args[1], args[2], args[3], args[4]).ConfigureAwait(false);
case "TRANSFER@" when args.Length > 3:
return await ResponseTransferByRealAppIDs(access, args[1], args[2], Utilities.GetArgsAsText(message, 3), false, steamID).ConfigureAwait(false);
case "TRANSFER@" when args.Length > 2:
Expand Down Expand Up @@ -745,6 +753,65 @@ internal void OnNewLicenseList() {
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}

private async Task<string?> ResponseAdvancedLootByAssetRarity(EAccess access, string targetAppID, string targetContextID, string assetRaritiesText) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}

ArgumentException.ThrowIfNullOrEmpty(targetAppID);
ArgumentException.ThrowIfNullOrEmpty(targetContextID);
ArgumentException.ThrowIfNullOrEmpty(assetRaritiesText);

if (access < EAccess.Master) {
return null;
}

if (!Bot.IsConnectedAndLoggedOn) {
return FormatBotResponse(Strings.BotNotConnected);
}

if (!uint.TryParse(targetAppID, out uint appID) || (appID == 0)) {
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(appID)));
}

if (!ulong.TryParse(targetContextID, out ulong contextID) || (contextID == 0)) {
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(contextID)));
}

HashSet<EAssetRarity>? assetRarities = ParseAssetRarities(assetRaritiesText);

if (assetRarities == null) {
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(assetRarities)));
}

(bool success, string message) = await Bot.Actions.SendInventory(appID, contextID, filterFunction: item => assetRarities.Contains(item.Rarity)).ConfigureAwait(false);

return FormatBotResponse(success ? message : Strings.FormatWarningFailedWithError(message));
}

private static async Task<string?> ResponseAdvancedLootByAssetRarity(EAccess access, string botNames, string appID, string contextID, string assetRaritiesText, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}

ArgumentException.ThrowIfNullOrEmpty(botNames);
ArgumentException.ThrowIfNullOrEmpty(appID);
ArgumentException.ThrowIfNullOrEmpty(contextID);
ArgumentException.ThrowIfNullOrEmpty(assetRaritiesText);

HashSet<Bot>? bots = Bot.GetBots(botNames);

if ((bots == null) || (bots.Count == 0)) {
return access >= EAccess.Owner ? FormatStaticResponse(Strings.FormatBotNotFound(botNames)) : null;
}

IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedLootByAssetRarity(GetProxyAccess(bot, access, steamID), appID, contextID, assetRaritiesText))).ConfigureAwait(false);

List<string> responses = [..results.Where(static result => !string.IsNullOrEmpty(result))!];

return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}

private async Task<string?> ResponseAdvancedRedeem(EAccess access, string options, string keys, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
Expand Down Expand Up @@ -926,6 +993,109 @@ internal void OnNewLicenseList() {
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}

private async Task<string?> ResponseAdvancedTransferByAssetRarity(EAccess access, uint appID, ulong contextID, Bot targetBot, HashSet<EAssetRarity> assetRarities) {

Check notice on line 996 in ArchiSteamFarm/Steam/Interaction/Commands.cs

View workflow job for this annotation

GitHub Actions / Qodana for .NET

Parameter can be declared with the base type

Parameter can be of type 'System.Collections.Generic.IReadOnlySet'
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}

ArgumentOutOfRangeException.ThrowIfZero(appID);
ArgumentOutOfRangeException.ThrowIfZero(contextID);
ArgumentNullException.ThrowIfNull(targetBot);

if (access < EAccess.Master) {
return null;
}

if (!Bot.IsConnectedAndLoggedOn) {
return FormatBotResponse(Strings.BotNotConnected);
}

if (!targetBot.IsConnectedAndLoggedOn) {
return FormatBotResponse(Strings.TargetBotNotConnected);
}

(bool success, string message) = await Bot.Actions.SendInventory(appID, contextID, targetBot.SteamID, filterFunction: item => assetRarities.Contains(item.Rarity)).ConfigureAwait(false);

return FormatBotResponse(success ? message : Strings.FormatWarningFailedWithError(message));
}

private async Task<string?> ResponseAdvancedTransferByAssetRarity(EAccess access, string targetAppID, string targetContextID, string botNameTo, string assetRaritiesText) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}

ArgumentException.ThrowIfNullOrEmpty(targetAppID);
ArgumentException.ThrowIfNullOrEmpty(targetContextID);
ArgumentException.ThrowIfNullOrEmpty(botNameTo);
ArgumentException.ThrowIfNullOrEmpty(assetRaritiesText);

Bot? targetBot = Bot.GetBot(botNameTo);

if (targetBot == null) {
return access >= EAccess.Owner ? FormatBotResponse(Strings.FormatBotNotFound(botNameTo)) : null;
}

if (!uint.TryParse(targetAppID, out uint appID) || (appID == 0)) {
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(appID)));
}

if (!ulong.TryParse(targetContextID, out ulong contextID) || (contextID == 0)) {
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(contextID)));
}

HashSet<EAssetRarity>? assetRarities = ParseAssetRarities(assetRaritiesText);

if (assetRarities == null) {
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(assetRarities)));
}

return await ResponseAdvancedTransferByAssetRarity(access, appID, contextID, targetBot, assetRarities).ConfigureAwait(false);
}

private static async Task<string?> ResponseAdvancedTransferByAssetRarity(EAccess access, string botNames, string targetAppID, string targetContextID, string botNameTo, string assetRaritiesText, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}

ArgumentException.ThrowIfNullOrEmpty(botNames);
ArgumentException.ThrowIfNullOrEmpty(targetAppID);
ArgumentException.ThrowIfNullOrEmpty(targetContextID);
ArgumentException.ThrowIfNullOrEmpty(botNameTo);
ArgumentException.ThrowIfNullOrEmpty(assetRaritiesText);

HashSet<Bot>? bots = Bot.GetBots(botNames);

if ((bots == null) || (bots.Count == 0)) {
return access >= EAccess.Owner ? FormatStaticResponse(Strings.FormatBotNotFound(botNames)) : null;
}

if (!uint.TryParse(targetAppID, out uint appID) || (appID == 0)) {
return FormatStaticResponse(Strings.FormatErrorIsInvalid(nameof(appID)));
}

if (!ulong.TryParse(targetContextID, out ulong contextID) || (contextID == 0)) {
return FormatStaticResponse(Strings.FormatErrorIsInvalid(nameof(contextID)));
}

Bot? targetBot = Bot.GetBot(botNameTo);

if (targetBot == null) {
return access >= EAccess.Owner ? FormatStaticResponse(Strings.FormatBotNotFound(botNameTo)) : null;
}

HashSet<EAssetRarity>? assetRarities = ParseAssetRarities(assetRaritiesText);

if (assetRarities == null) {
return FormatStaticResponse(Strings.FormatErrorIsInvalid(nameof(assetRarities)));
}

IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedTransferByAssetRarity(GetProxyAccess(bot, access, steamID), appID, contextID, targetBot, assetRarities))).ConfigureAwait(false);

List<string> responses = [..results.Where(static result => !string.IsNullOrEmpty(result))!];

return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}

private string? ResponseBackgroundGamesRedeemer(EAccess access) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
Expand Down Expand Up @@ -3257,6 +3427,22 @@ internal void OnNewLicenseList() {
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}

private static HashSet<EAssetRarity>? ParseAssetRarities(string assetRaritiesText) {
string[] assetRaritiesArgs = assetRaritiesText.Split(SharedInfo.ListElementSeparators, StringSplitOptions.RemoveEmptyEntries);

HashSet<EAssetRarity> assetRarities = [];

foreach (string assetRarityArg in assetRaritiesArgs) {
if (!Enum.TryParse(assetRarityArg, true, out EAssetRarity assetRarity) || !Enum.IsDefined(assetRarity)) {
return null;
}

assetRarities.Add(assetRarity);
}

return assetRarities;
}

[Flags]
private enum ERedeemFlags : ushort {
None = 0,
Expand Down

0 comments on commit 8e85b87

Please sign in to comment.