Skip to content

Commit

Permalink
Add loot& and transfer& commands
Browse files Browse the repository at this point in the history
  • Loading branch information
dm1tz committed Sep 9, 2024
1 parent 5f545a9 commit 0341ef8
Show file tree
Hide file tree
Showing 3 changed files with 264 additions and 3 deletions.
8 changes: 7 additions & 1 deletion ArchiSteamFarm/Steam/Data/EAssetRarity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,11 @@ public enum EAssetRarity : byte {
Unknown,
Common,
Uncommon,
Rare
Rare,
Mythical,
Legendary,
Ancient,
Immortal,
Arcana,
Unusual
}
49 changes: 47 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 @@ -211,6 +211,51 @@ public EAssetRarity Rarity {

CachedRarity = EAssetRarity.Unknown;

return CachedRarity.Value;
}
case "RARITY":
switch (tag.internal_name) {
case "common" or "Rarity_common" or "Rarity_Common_Weapon" or "Rarity_Common_Character":
CachedRarity = EAssetRarity.Common;

return CachedRarity.Value;
case "uncommon" or "Rarity_uncommon" or "Rarity_Uncommon_Weapon" or "Rarity_Uncommon_Character":
CachedRarity = EAssetRarity.Uncommon;

return CachedRarity.Value;
case "rare" or "Rarity_rare" or "Rarity_Rare_Weapon" or "Rarity_Rare_Character":
CachedRarity = EAssetRarity.Rare;

return CachedRarity.Value;
case "epic" or "ultra_rare" or "Rarity_mythical" or "Rarity_Mythical_Weapon" or "Rarity_Mythical_Character":
CachedRarity = EAssetRarity.Mythical;

return CachedRarity.Value;
case "exotic" or "legendary" or "Rarity_legendary" or "Rarity_Legendary_Weapon" or "Rarity_Legendary_Character":
CachedRarity = EAssetRarity.Legendary;

return CachedRarity.Value;
case "extraordinary" or "Rarity_ancient" or "Rarity_Ancient_Weapon" or "Rarity_Ancient_Character":
CachedRarity = EAssetRarity.Ancient;

return CachedRarity.Value;
case "Rarity_immortal" or "Rarity_Contraband_Weapon" or "Rarity_Contraband_Character":
CachedRarity = EAssetRarity.Immortal;

return CachedRarity.Value;
case "Rarity_arcana":
CachedRarity = EAssetRarity.Arcana;

return CachedRarity.Value;
case "Rarity_unusual":
CachedRarity = EAssetRarity.Unusual;

return CachedRarity.Value;
default:
ASF.ArchiLogger.LogGenericError(Strings.FormatWarningUnknownValuePleaseReport(nameof(tag.internal_name), tag.internal_name));

CachedRarity = EAssetRarity.Unknown;

return CachedRarity.Value;
}
}
Expand Down
210 changes: 210 additions & 0 deletions ArchiSteamFarm/Steam/Interaction/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ namespace ArchiSteamFarm.Steam.Interaction;

public sealed class Commands {
private const ushort SteamTypingStatusDelay = 10 * 1000; // Steam client broadcasts typing status each 10 seconds
private const byte EAssetRarities = 10; // Currently, we recognize 10 EAssetRarity values

private readonly Bot Bot;
private readonly Dictionary<uint, string> CachedGamesOwned = new();
Expand Down Expand Up @@ -236,6 +237,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 +323,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 +754,78 @@ 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)));
}

string[] assetRaritiesArgs = assetRaritiesText.Split(SharedInfo.ListElementSeparators, EAssetRarities + 1, StringSplitOptions.RemoveEmptyEntries);

switch (assetRaritiesArgs.Length) {
case 0:
return FormatBotResponse(Strings.FormatErrorIsEmpty(nameof(assetRaritiesArgs)));
case > EAssetRarities:
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(assetRaritiesArgs)));
}

HashSet<EAssetRarity> assetRarities = [];

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

assetRarities.Add(assetRarity);
}

(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 +1007,135 @@ 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) {
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)));
}

string[] assetRaritiesArgs = assetRaritiesText.Split(SharedInfo.ListElementSeparators, EAssetRarities + 1, StringSplitOptions.RemoveEmptyEntries);

switch (assetRaritiesArgs.Length) {
case 0:
return FormatBotResponse(Strings.FormatErrorIsEmpty(nameof(assetRaritiesArgs)));
case > EAssetRarities:
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(assetRaritiesArgs)));
}

HashSet<EAssetRarity> assetRarities = [];

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

assetRarities.Add(assetRarity);
}

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;
}

string[] assetRaritiesArgs = assetRaritiesText.Split(SharedInfo.ListElementSeparators, EAssetRarities + 1, StringSplitOptions.RemoveEmptyEntries);

switch (assetRaritiesArgs.Length) {
case 0:
return FormatStaticResponse(Strings.FormatErrorIsEmpty(nameof(assetRaritiesArgs)));
case > EAssetRarities:
return FormatStaticResponse(Strings.FormatErrorIsInvalid(nameof(assetRaritiesArgs)));
}

HashSet<EAssetRarity> assetRarities = [];

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

assetRarities.Add(assetRarity);
}

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

0 comments on commit 0341ef8

Please sign in to comment.