Skip to content

Instantly share code, notes, and snippets.

@m4dEngi
Last active May 27, 2019 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m4dEngi/97eff07de541ab90f3fe077fee46af73 to your computer and use it in GitHub Desktop.
Save m4dEngi/97eff07de541ab90f3fe077fee46af73 to your computer and use it in GitHub Desktop.
using System;
using ArchiSteamFarm;
using ArchiSteamFarm.Plugins;
using System.Composition;
using SteamKit2;
using System.Collections.Generic;
using HtmlAgilityPack;
using SteamKit2.Internal;
using System.Linq;
using System.Threading.Tasks;
namespace m4dengi.SpringCleanPlugin
{
// this class is used to bypass plugin api limitations
// and expose SteamClient instance associated with bot to plugin
class CallHack : ClientMsgHandler
{
public override void HandleMsg(IPacketMsg packetMsg)
{
//stub
}
public void PlayGames(List<uint> appIDs)
{
var gamesPlayedMsg = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
foreach (uint appID in appIDs)
{
gamesPlayedMsg.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed { game_id = new GameID(appID) });
}
Client.Send(gamesPlayedMsg);
}
public async Task<SteamApps.FreeLicenseCallback> RequestFreeLicense(List<uint> appIDs)
{
SteamApps apps = Client.GetHandler<SteamApps>();
return await apps.RequestFreeLicense(appIDs);
}
}
[Export(typeof(IPlugin))]
public sealed class SpringCleanPlugin : IPlugin, IBot, IBotSteamClient, IBotCommand
{
public string Name => nameof(SpringCleanPlugin);
public Version Version => typeof(SpringCleanPlugin).Assembly.GetName().Version;
private static Dictionary<Bot, CallHack> extHandlers = new Dictionary<Bot, CallHack>();
private async Task<List<uint>> FetchSpringGamesList(Bot bot)
{
List<uint> gamesList = new List<uint>();
HtmlDocument springCleanPage = await bot.ArchiWebHandler.UrlGetToHtmlDocumentWithSession(ArchiWebHandler.SteamStoreURL, "/springcleaning");
HtmlNodeCollection appTaskCollection = springCleanPage.DocumentNode.SelectNodes("//div[contains(@class, 'spring_cleaning_task_ctn')]");
if (appTaskCollection.Count != 0)
{
foreach (HtmlNode node in appTaskCollection)
{
HtmlNode gameAppLinkNode = node.SelectSingleNode(".//div[@data-sg-appid]");
if (gameAppLinkNode != null)
{
string appStoreId = gameAppLinkNode.Attributes["data-sg-appid"].Value;
if (!string.IsNullOrEmpty(appStoreId))
{
uint appIDToAdd = uint.Parse(appStoreId);
gamesList.Add(appIDToAdd);
}
}
}
}
return gamesList;
}
private async Task ApplyGenericTagsToAppID(Bot bot, uint appID, string tagName, uint tagID)
{
bool success = await bot.ArchiWebHandler.UrlPostWithSession(ArchiWebHandler.SteamStoreURL, "/tagdata/tagapp", new Dictionary<string, string>() { { "appid", appID.ToString() }, { "tag", tagName }, { "tagid", tagID.ToString() } });
await Task.Delay(TimeSpan.FromSeconds(2));
}
private async Task<string> SpringClean(Bot bot, int delay = 30)
{
if (!bot.IsConnectedAndLoggedOn)
{
return null;
}
await Task.Delay(TimeSpan.FromSeconds(delay));
bot.ArchiLogger.LogGenericInfo("Applying generic tags to TF2");
await ApplyGenericTagsToAppID(bot, 440, "Multiplayer", 3859);
await ApplyGenericTagsToAppID(bot, 440, "Free+to+Play", 113);
bot.ArchiLogger.LogGenericInfo("Loading spring clean tasks");
List<uint> gamesToPlay = await FetchSpringGamesList(bot);
if (gamesToPlay.Count != 0)
{
bot.ArchiLogger.LogGenericInfo(string.Format("Got {0} games to 'play' : {1}", gamesToPlay.Count, string.Join(',', gamesToPlay)));
if (gamesToPlay.Count != 0 && gamesToPlay.Count < 32)
{
await bot.Actions.Pause(false);
var result = await extHandlers[bot].RequestFreeLicense(gamesToPlay);
bot.ArchiLogger.LogGenericInfo("Got RequestFreeLicense response : " + result.Result);
if (result.Result == EResult.OK)
{
bot.ArchiLogger.LogGenericInfo("Granted appIDs count : " + result.GrantedApps.Count);
}
extHandlers[bot].PlayGames(gamesToPlay);
}
}
else
{
return "Got no games from spring cleaning event page";
}
bot.ArchiLogger.LogGenericInfo("Done.");
return "Ok";
}
public void OnBotSteamCallbacksInit(Bot bot, CallbackManager callbackManager)
{
// stub
}
public IReadOnlyCollection<ClientMsgHandler> OnBotSteamHandlersInit(Bot bot)
{
if (!extHandlers.ContainsKey(bot))
{
extHandlers.Add(bot, new CallHack());
}
return new List<ClientMsgHandler> { extHandlers[bot] };
}
public void OnLoaded()
{
ASF.ArchiLogger.LogGenericInfo("SpringCleanPlugin loaded. Expect weird behaviour and unexpected consequences.");
}
public void OnBotDestroy(Bot bot)
{
extHandlers.Remove(bot);
}
public void OnBotInit(Bot bot)
{
// stub
}
public async Task<string> OnBotCommand(Bot bot, ulong steamID, string message, string[] args)
{
switch (args[0].ToUpperInvariant())
{
case "SPRINGCLEAN" when bot.HasPermission(steamID, BotConfig.EPermission.Operator):
if (args.Length > 1)
{
HashSet<Bot> bots = Bot.GetBots(args[1]);
int delayCtr = bots.Count(b => b.IsConnectedAndLoggedOn);
IList<string> results = await Utilities.InParallel(bots.Where(x => x.IsConnectedAndLoggedOn).Select(b => { --delayCtr; return SpringClean(b, delayCtr * 30); }));
List<string> responses = new List<string>(results.Where(result => !string.IsNullOrEmpty(result)));
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}
else
{
return await SpringClean(bot);
}
default:
return null;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment