Skip to content

Instantly share code, notes, and snippets.

@pigeonhands
Last active December 14, 2016 05:16
Show Gist options
  • Save pigeonhands/e2b29efe81ec87d1e336 to your computer and use it in GitHub Desktop.
Save pigeonhands/e2b29efe81ec87d1e336 to your computer and use it in GitHub Desktop.
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
namespace SatoshiMines
{
public delegate void OnException(SatoshiMinesBot sender, Exception ex);
public delegate void OnCheckBalance(SatoshiMinesBot sender);
public delegate void OnGameStart(SatoshiMinesBot sender);
public delegate void OnBet(SatoshiMinesBot sender, BetData bet);
public delegate void OnBval(SatoshiMinesBot sender);
public delegate void OnCashout(SatoshiMinesBot sender, CashOutData cashout);
/// <summary>
/// SatoshiMines bot class
/// Made by BahNahNah
/// </summary>
public class SatoshiMinesBot
{
#region " Properties "
public string PlayerHash { get; set; }
public GameData CurrentGame { get; private set; }
public BetData LastBet { get; private set; }
public float LastCheckedBalance { get; private set; }
/// <summary>
/// The ammount to bet in bits (a millionth of a BTC)
/// </summary>
public int BetAmmount { get { return (int)(BetCostBTC * 1000000); } set { BetCostBTC = value / 1000000; } }
public SMines Mines { get; set; }
public string CurrentBDVal { get; private set; }
public int LastSquareBet { get; private set; }
public int NumberOfSquaresBet { get; private set; }
public int[] SequentialBombsOnSquare { get; private set; }
/// <summary>
/// Played squares are 1, and non-played squares are 0
/// </summary>
public int[] CurrentBetGrid { get; private set; }
/// <summary>
/// Used for callabcks. Does not effect bot.
/// </summary>
public bool ShouldRun { get; set; }
#endregion
#region " Fields "
private HttpWebRequest _webRequest = null;
private Regex rgBval = new Regex("var bdval = '(\\d+)'");
private CultureInfo betFormat = new CultureInfo("en-US");
private double BetCostBTC = 0;
private Random Random = new Random();
#endregion
#region " Events "
public event OnCheckBalance OnCheckBalance;
public event OnException OnException;
public event OnGameStart OnGameStart;
public event OnBval OnBVal;
public event OnBet OnBet;
public event OnCashout OnCashout;
#endregion
#region " Constructors "
public SatoshiMinesBot()
{
SequentialBombsOnSquare = new int[25];
CurrentBetGrid = new int[25];
Mines = SMines.Three;
}
public SatoshiMinesBot(string _playerHash) : this()
{
PlayerHash = _playerHash;
}
#endregion
#region " Public Methods "
public bool ValidPlayerHash()
{
return UpdateBalance();
}
public bool UpdateBVal()
{
return TryGetBVal();
}
public bool StartGame()
{
NumberOfSquaresBet = 0;
return AttemptNewGame();
}
/// <summary>
/// Place a bet on a square number (from 0 - 24)
/// </summary>
/// <param name="s">Square to bet on</param>
/// <returns></returns>
public bool Bet(int s)
{
if (s > CurrentBetGrid.Length || s < 0)
return false;
LastSquareBet = s;
CurrentBetGrid[s] = 1;
return AttemptBet(s + 1);
}
public bool BetRandomSquare()
{
if (!SquareAvalible())
return false;
int s = Random.Next(0, 25);
while (CurrentBetGrid[s] != 0)
s = Random.Next(0, 25);
return Bet(s);
}
/// <summary>
/// Returns true if there is a square that has not been bet on
/// </summary>
/// <returns></returns>
public bool SquareAvalible()
{
bool sq = false;
lock (this)
{
foreach (int i in CurrentBetGrid)
{
if (i == 0)
{
sq = true;
break;
}
}
}
return sq;
}
public bool Cashout()
{
return AttemptCashout();
}
public bool UpdateBalance()
{
try
{
if (string.IsNullOrWhiteSpace(PlayerHash))
return false;
string resp = string.Empty;
using (WebClient wc = new WebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
resp = wc.UploadString(new Uri("https://satoshimines.com/action/refresh_balance.php"), "POST", string.Format("secret={0}", PlayerHash));
}
BalanceData b = Deserialize<BalanceData>(resp);
if (b.status == "error")
return false;
LastCheckedBalance = b.balance;
OnCheckBalance?.Invoke(this);
return true;
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
return false;
}
}
#endregion
#region " Private Methods "
private bool AttemptNewGame()
{
try
{
_webRequest = (HttpWebRequest)WebRequest.Create("https://satoshimines.com/action/newgame.php");
UploadPost(NewGameCallback, "player_hash={0}&bet={1}&num_mines={2}", PlayerHash, BetCostBTC.ToString("0.000000", betFormat), (int)Mines);
return true;
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
return false;
}
}
private bool AttemptBet(int square)
{
try
{
_webRequest = (HttpWebRequest)WebRequest.Create("https://satoshimines.com/action/checkboard.php");
UploadPost(PlaceBetCallback, "game_hash={0}&guess={1}&v04=1", CurrentGame.game_hash, square);
return true;
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
return false;
}
}
private bool AttemptCashout()
{
try
{
_webRequest = (HttpWebRequest)WebRequest.Create("https://satoshimines.com/action/cashout.php");
UploadPost(CashoutCallback, "game_hash={0}", CurrentGame.game_hash);
return true;
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
return false;
}
}
private bool TryGetBVal()
{
try
{
using (WebClient bvalRequest = new WebClient())
{
bvalRequest.DownloadStringCompleted += BvalRequest_DownloadStringCompleted;
bvalRequest.DownloadStringTaskAsync(string.Format("https://www.satoshimines.com/play/{0}", PlayerHash));
}
return true;
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
return false;
}
}
private void UploadPost(AsyncCallback after, string postString, params object[] args)
{
byte[] post = Encoding.UTF8.GetBytes(string.Format(postString + "&bd=" + CurrentBDVal, args));
_webRequest.ContentLength = post.Length;
_webRequest.Method = "POST";
_webRequest.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
_webRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36";
_webRequest.BeginGetRequestStream((AR) => OnRequestStream(AR, post, after), null);
}
#endregion
#region " Callbacks "
private void OnRequestStream(IAsyncResult AR, byte[] post, AsyncCallback after)
{
using (Stream s = _webRequest.EndGetRequestStream(AR))
{
s.Write(post, 0, post.Length);
s.Flush();
}
_webRequest.BeginGetResponse(after, null);
}
private void NewGameCallback(IAsyncResult AR)
{
try
{
HttpWebResponse _response = (HttpWebResponse)_webRequest.EndGetResponse(AR);
CurrentGame = Deserialize<GameData>(_response.GetResponseStream());
if (CurrentGame == null)
throw new Exception("Deserialize failed, object is null.");
if (CurrentGame.status != "success")
throw new Exception("Json Error: " + CurrentGame.message);
OnGameStart?.Invoke(this);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
}
private void PlaceBetCallback(IAsyncResult AR)
{
try
{
LastBet = Deserialize<BetData>(_webRequest.EndGetResponse(AR).GetResponseStream());
if (LastBet == null)
throw new Exception("Failed to parse bet responce.");
if (LastBet.status != "success")
throw new Exception("Bet Error: " + LastBet.message);
NumberOfSquaresBet++;
OnBet?.Invoke(this, LastBet);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
}
private void CashoutCallback(IAsyncResult AR)
{
try
{
CashOutData cashout = Deserialize<CashOutData>(_webRequest.EndGetResponse(AR).GetResponseStream());
OnCashout?.Invoke(this, cashout);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
}
private void BvalRequest_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
try
{
string reqVal = e.Result;
Match bdMatch = rgBval.Match(reqVal);
if (!bdMatch.Success)
throw new Exception("Regex failed to match bdvalue.");
CurrentBDVal = bdMatch.Groups[1].Value;
if (string.IsNullOrWhiteSpace(CurrentBDVal))
throw new Exception("Invalid bdvalue.");
OnBVal?.Invoke(this);
}
catch (Exception ex)
{
OnException?.Invoke(this, ex);
}
}
#endregion
#region " Deseralization "
private static T Deserialize<T>(string json)
{
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
return Deserialize<T>(ms);
}
}
private static T Deserialize<T>(Stream ds)
{
var s = new DataContractJsonSerializer(typeof(T));
return (T)s.ReadObject(ds);
}
#endregion
}
#region " Data Structures "
public class BalanceData
{
public string status { get; set; }
public float balance { get; set; }
}
public class BetData
{
public string status { get; set; }
public int guess { get; set; }
public string outcome { get; set; }
public double stake { get; set; }
public double next { get; set; }
public string message { get; set; }
public double change { get; set; }
public string bombs { get; set; }
}
public class CashOutData
{
public string status { get; set; }
public double win { get; set; }
public string mines { get; set; }
public string message { get; set; }
public string random_string { get; set; }
public string game_id { get; set; }
}
public class GameData
{
public string status { get; set; }
public int id { get; set; }
public string game_hash { get; set; }
public string secret { get; set; }
public string bet { get; set; }
public string stake { get; set; }
public string next { get; set; }
public string betNumber { get; set; }
public string gametype { get; set; }
public int num_mines { get; set; }
public string message { get; set; }
}
#endregion
public enum SMines
{
One = 1,
Three = 3,
Five = 5,
TwentyFour = 24
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment